mirror of
https://github.com/Myxelium/Lunaris2.0.git
synced 2026-08-22 13:51:22 +00:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3a00f4ed | ||
|
|
0673653491 | ||
|
|
a650fd431e | ||
|
|
f6a763589e | ||
|
|
b7198bd01e | ||
|
|
f0678a4244 | ||
|
|
984d6b0af8 | ||
|
|
175f1f0d01 | ||
|
|
c8a1936f64 | ||
|
|
3f091ed7cc | ||
|
|
49872791b3 | ||
|
|
5a7b326dc2 | ||
|
|
0bc2a9be56 | ||
|
|
2d7ec0829e | ||
|
|
9b48536cd7 | ||
|
|
31c39e8893 | ||
|
|
521f808209 | ||
|
|
97667df53b | ||
|
|
95ab1281a4 | ||
|
|
f7b54ca80c | ||
|
|
3699a13cd9 | ||
|
|
13efaa7552 | ||
|
|
169b71c724 | ||
|
|
5b996ecd1d |
@@ -0,0 +1,60 @@
|
|||||||
|
name: .NET Core CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v1
|
||||||
|
with:
|
||||||
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore ./Bot/Lunaris2.csproj
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build ./Bot/Lunaris2.csproj --no-restore -c Release -o ./out
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: dotnet publish ./Bot/Lunaris2.csproj --configuration Release --output ./out
|
||||||
|
|
||||||
|
- name: Zip the build
|
||||||
|
run: 7z a -tzip ./out/Bot.zip ./out/*
|
||||||
|
|
||||||
|
- name: Get the tag name
|
||||||
|
id: get_tag
|
||||||
|
run: echo "::set-output name=tag::${GITHUB_REF#refs/tags/}"
|
||||||
|
|
||||||
|
- name: Get the version
|
||||||
|
id: get_version
|
||||||
|
run: echo "::set-output name=version::$(date +%s).${{ github.run_id }}"
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
|
||||||
|
with:
|
||||||
|
tag_name: ${{ steps.get_version.outputs.version }}
|
||||||
|
release_name: Release v${{ steps.get_version.outputs.version }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release Asset
|
||||||
|
id: upload-release-asset
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./out/Bot.zip
|
||||||
|
asset_name: Bot.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
obj
|
||||||
|
.idea
|
||||||
|
.vs
|
||||||
|
bin
|
||||||
|
appsettings.json
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.GoodByeCommand;
|
||||||
|
|
||||||
|
public record GoodbyeCommand(SocketSlashCommand Message) : IRequest;
|
||||||
|
|
||||||
|
public class GoodbyeHandler : IRequestHandler<GoodbyeCommand>
|
||||||
|
{
|
||||||
|
public async Task Handle(GoodbyeCommand message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await message.Message.RespondAsync($"Goodbye, {message.Message.User.Username}! :c");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using Lunaris2.SlashCommand;
|
||||||
|
using MediatR;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.HelloCommand;
|
||||||
|
|
||||||
|
public record HelloCommand(SocketSlashCommand Message) : IRequest;
|
||||||
|
|
||||||
|
public class HelloHandler : IRequestHandler<HelloCommand>
|
||||||
|
{
|
||||||
|
public async Task Handle(HelloCommand message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Console.WriteLine(JsonConvert.SerializeObject(Command.GetAllCommands()));
|
||||||
|
|
||||||
|
await message.Message.RespondAsync($"Hello, {message.Message.User.Username}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Lunaris2.Handler.GoodByeCommand;
|
||||||
|
using Lunaris2.Handler.MusicPlayer.JoinCommand;
|
||||||
|
using Lunaris2.Handler.MusicPlayer.PlayCommand;
|
||||||
|
using Lunaris2.Handler.MusicPlayer.SkipCommand;
|
||||||
|
using Lunaris2.Notification;
|
||||||
|
using Lunaris2.SlashCommand;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler;
|
||||||
|
|
||||||
|
public class MessageReceivedHandler(ISender mediator) : INotificationHandler<MessageReceivedNotification>
|
||||||
|
{
|
||||||
|
public async Task Handle(MessageReceivedNotification notification, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
switch (notification.Message.CommandName)
|
||||||
|
{
|
||||||
|
case Command.Hello.Name:
|
||||||
|
await mediator.Send(new HelloCommand.HelloCommand(notification.Message), cancellationToken);
|
||||||
|
break;
|
||||||
|
case Command.Goodbye.Name:
|
||||||
|
await mediator.Send(new GoodbyeCommand(notification.Message), cancellationToken);
|
||||||
|
break;
|
||||||
|
case Command.Join.Name:
|
||||||
|
await mediator.Send(new JoinCommand(notification.Message), cancellationToken);
|
||||||
|
break;
|
||||||
|
case Command.Play.Name:
|
||||||
|
await mediator.Send(new PlayCommand(notification.Message), cancellationToken);
|
||||||
|
break;
|
||||||
|
case Command.Skip.Name:
|
||||||
|
await mediator.Send(new SkipCommand(notification.Message), cancellationToken);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Victoria.Node;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.MusicPlayer;
|
||||||
|
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static SocketGuild GetGuild(this SocketSlashCommand message, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
if (message.GuildId == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Guild ID is null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.GetGuild(message.GuildId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IVoiceState GetVoiceState(this SocketSlashCommand message)
|
||||||
|
{
|
||||||
|
var voiceState = message.User as IVoiceState;
|
||||||
|
|
||||||
|
if (voiceState?.VoiceChannel == null)
|
||||||
|
{
|
||||||
|
throw new Exception("You must be connected to a voice channel!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return voiceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RespondAsync(this SocketSlashCommand message, string content)
|
||||||
|
{
|
||||||
|
await message.RespondAsync(content, ephemeral: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task EnsureConnected(this LavaNode lavaNode)
|
||||||
|
{
|
||||||
|
if(!lavaNode.IsConnected)
|
||||||
|
await lavaNode.ConnectAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task JoinVoiceChannel(this SocketSlashCommand context, LavaNode lavaNode)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var textChannel = context.Channel as ITextChannel;
|
||||||
|
await lavaNode.JoinAsync(context.GetVoiceState().VoiceChannel, textChannel);
|
||||||
|
await context.RespondAsync($"Joined {context.GetVoiceState().VoiceChannel.Name}!");
|
||||||
|
}
|
||||||
|
catch (Exception exception) {
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetOptionValueByName(this SocketSlashCommand command, string optionName)
|
||||||
|
{
|
||||||
|
return command.Data.Options.FirstOrDefault(option => option.Name == optionName)?.Value.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MediatR;
|
||||||
|
using Victoria.Node;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.MusicPlayer.JoinCommand;
|
||||||
|
|
||||||
|
public record JoinCommand(SocketSlashCommand Message) : IRequest;
|
||||||
|
|
||||||
|
public class JoinHandler : IRequestHandler<JoinCommand>
|
||||||
|
{
|
||||||
|
private readonly LavaNode _lavaNode;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public JoinHandler(LavaNode lavaNode, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
_lavaNode = lavaNode;
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(JoinCommand command, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var context = command.Message;
|
||||||
|
|
||||||
|
await _lavaNode.EnsureConnected();
|
||||||
|
|
||||||
|
if (_lavaNode.HasPlayer(context.GetGuild(_client))) {
|
||||||
|
await context.RespondAsync("I'm already connected to a voice channel!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.JoinVoiceChannel(_lavaNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Lunaris2.Handler.MusicPlayer;
|
||||||
|
|
||||||
|
public static class MessageModule
|
||||||
|
{
|
||||||
|
private static Dictionary<ulong, List<ulong>> guildMessageIds = new Dictionary<ulong, List<ulong>>();
|
||||||
|
|
||||||
|
public static async Task SendMessageAsync(this SocketSlashCommand context, string message, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
var guildId = await StoreForRemoval(context, client);
|
||||||
|
|
||||||
|
await context.RespondAsync(message);
|
||||||
|
var sentMessage = await context.GetOriginalResponseAsync();
|
||||||
|
|
||||||
|
guildMessageIds[guildId].Add(sentMessage.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task SendMessageAsync(this SocketSlashCommand context, Embed message, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
var guildId = await StoreForRemoval(context, client);
|
||||||
|
|
||||||
|
await context.RespondAsync(embed: message);
|
||||||
|
|
||||||
|
var sentMessage = await context.GetOriginalResponseAsync();
|
||||||
|
|
||||||
|
guildMessageIds[guildId].Add(sentMessage.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ulong> StoreForRemoval(SocketSlashCommand context, DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
var guildId = context.GetGuild(client).Id;
|
||||||
|
|
||||||
|
if (guildMessageIds.ContainsKey(guildId))
|
||||||
|
{
|
||||||
|
foreach (var messageId in guildMessageIds[guildId])
|
||||||
|
{
|
||||||
|
var messageToDelete = await context.Channel.GetMessageAsync(messageId);
|
||||||
|
if (messageToDelete != null)
|
||||||
|
await messageToDelete.DeleteAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
guildMessageIds[guildId].Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
guildMessageIds.Add(guildId, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return guildId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Victoria;
|
||||||
|
using Victoria.Player;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.MusicPlayer;
|
||||||
|
|
||||||
|
public class MusicEmbed
|
||||||
|
{
|
||||||
|
private Embed SendMusicEmbed(
|
||||||
|
string imageUrl,
|
||||||
|
string title,
|
||||||
|
string length,
|
||||||
|
string artist,
|
||||||
|
string queuedBy,
|
||||||
|
string? nextInQueue)
|
||||||
|
{
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithAuthor("Lunaris", "https://media.tenor.com/GqAwMt01UXgAAAAi/cd.gif")
|
||||||
|
.WithTitle(title)
|
||||||
|
.WithDescription($"Length: {length}\nArtist: {artist}\nQueued by: {queuedBy}\nNext in queue: {nextInQueue}")
|
||||||
|
.WithColor(Color.Magenta)
|
||||||
|
.WithThumbnailUrl(imageUrl)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task NowPlayingEmbed(
|
||||||
|
LavaPlayer<LavaTrack> player,
|
||||||
|
SocketSlashCommand context,
|
||||||
|
DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
var artwork = await player.Track.FetchArtworkAsync();
|
||||||
|
var getNextTrack = player.Vueue.Count > 1 ? player.Vueue.ToArray()[1].Title : "No songs in queue.";
|
||||||
|
var embed = SendMusicEmbed(
|
||||||
|
artwork,
|
||||||
|
player.Track.Title,
|
||||||
|
player.Track.Duration.ToString(),
|
||||||
|
player.Track.Author,
|
||||||
|
context.User.Username,
|
||||||
|
getNextTrack);
|
||||||
|
|
||||||
|
await context.SendMessageAsync(embed, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Lunaris2.SlashCommand;
|
||||||
|
using MediatR;
|
||||||
|
using Victoria.Node;
|
||||||
|
using Victoria.Node.EventArgs;
|
||||||
|
using Victoria.Player;
|
||||||
|
using Victoria.Responses.Search;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.MusicPlayer.PlayCommand;
|
||||||
|
|
||||||
|
public record PlayCommand(SocketSlashCommand Message) : IRequest;
|
||||||
|
|
||||||
|
public class PlayHandler : IRequestHandler<PlayCommand>
|
||||||
|
{
|
||||||
|
private readonly MusicEmbed _musicEmbed;
|
||||||
|
private readonly LavaNode _lavaNode;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private SocketSlashCommand _context;
|
||||||
|
|
||||||
|
public PlayHandler(
|
||||||
|
LavaNode lavaNode,
|
||||||
|
DiscordSocketClient client,
|
||||||
|
MusicEmbed musicEmbed)
|
||||||
|
{
|
||||||
|
_lavaNode = lavaNode;
|
||||||
|
_client = client;
|
||||||
|
_musicEmbed = musicEmbed;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(RunMode = RunMode.Async)]
|
||||||
|
public async Task Handle(PlayCommand command, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_context = command.Message;
|
||||||
|
|
||||||
|
await _lavaNode.EnsureConnected();
|
||||||
|
|
||||||
|
var songName = _context.GetOptionValueByName(Option.Input);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(songName)) {
|
||||||
|
await _context.RespondAsync("Please provide search terms.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = await GetPlayer();
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var searchResponse = await _lavaNode.SearchAsync(
|
||||||
|
Uri.IsWellFormedUriString(songName, UriKind.Absolute)
|
||||||
|
? SearchType.Direct
|
||||||
|
: SearchType.YouTube, songName);
|
||||||
|
|
||||||
|
if (!await SearchResponse(searchResponse, player, songName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await PlayTrack(player);
|
||||||
|
|
||||||
|
await _musicEmbed.NowPlayingEmbed(player, _context, _client);
|
||||||
|
|
||||||
|
_lavaNode.OnTrackEnd += OnTrackEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnTrackEnd(TrackEndEventArg<LavaPlayer<LavaTrack>, LavaTrack> arg)
|
||||||
|
{
|
||||||
|
var player = arg.Player;
|
||||||
|
if (!player.Vueue.TryDequeue(out var nextTrack))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await player.PlayAsync(nextTrack);
|
||||||
|
|
||||||
|
await _musicEmbed.NowPlayingEmbed(player, _context, _client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task PlayTrack(LavaPlayer<LavaTrack> player)
|
||||||
|
{
|
||||||
|
if (player.PlayerState is PlayerState.Playing or PlayerState.Paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Vueue.TryDequeue(out var lavaTrack);
|
||||||
|
await player.PlayAsync(lavaTrack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<LavaPlayer<LavaTrack>?> GetPlayer()
|
||||||
|
{
|
||||||
|
var voiceState = _context.User as IVoiceState;
|
||||||
|
|
||||||
|
if (voiceState?.VoiceChannel != null)
|
||||||
|
return await _lavaNode.JoinAsync(voiceState.VoiceChannel, _context.Channel as ITextChannel);
|
||||||
|
|
||||||
|
await _context.RespondAsync("You must be connected to a voice channel!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SearchResponse(
|
||||||
|
SearchResponse searchResponse, LavaPlayer<LavaTrack> player,
|
||||||
|
string songName)
|
||||||
|
{
|
||||||
|
if (searchResponse.Status is SearchStatus.LoadFailed or SearchStatus.NoMatches) {
|
||||||
|
await _context.RespondAsync($"I wasn't able to find anything for `{songName}`.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(searchResponse.Playlist.Name)) {
|
||||||
|
player.Vueue.Enqueue(searchResponse.Tracks);
|
||||||
|
|
||||||
|
await _context.RespondAsync($"Enqueued {searchResponse.Tracks.Count} songs.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var track = searchResponse.Tracks.FirstOrDefault()!;
|
||||||
|
player.Vueue.Enqueue(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MediatR;
|
||||||
|
using Victoria.Node;
|
||||||
|
using Victoria.Player;
|
||||||
|
|
||||||
|
namespace Lunaris2.Handler.MusicPlayer.SkipCommand;
|
||||||
|
|
||||||
|
public record SkipCommand(SocketSlashCommand Message) : IRequest;
|
||||||
|
|
||||||
|
public class SkipHandler : IRequestHandler<SkipCommand>
|
||||||
|
{
|
||||||
|
private readonly LavaNode _lavaNode;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly MusicEmbed _musicEmbed;
|
||||||
|
|
||||||
|
public SkipHandler(LavaNode lavaNode, DiscordSocketClient client, MusicEmbed musicEmbed)
|
||||||
|
{
|
||||||
|
_lavaNode = lavaNode;
|
||||||
|
_client = client;
|
||||||
|
_musicEmbed = musicEmbed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(SkipCommand message, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var context = message.Message;
|
||||||
|
|
||||||
|
await _lavaNode.EnsureConnected();
|
||||||
|
|
||||||
|
if (!_lavaNode.TryGetPlayer(context.GetGuild(_client), out var player)) {
|
||||||
|
await context.RespondAsync("I'm not connected to a voice channel.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.PlayerState != PlayerState.Playing) {
|
||||||
|
await context.RespondAsync("Woaaah there, I can't skip when nothing is playing.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await player.SkipAsync();
|
||||||
|
await _musicEmbed.NowPlayingEmbed(player, context, _client);
|
||||||
|
}
|
||||||
|
catch (Exception exception) {
|
||||||
|
await context.RespondAsync("There is not more tracks to skip.");
|
||||||
|
Console.WriteLine(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Lunaris2.Helper;
|
||||||
|
|
||||||
|
public static class Async
|
||||||
|
{
|
||||||
|
public static void Run(Func<Task> task)
|
||||||
|
{
|
||||||
|
_ = Task.Run(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UserSecretsId>ec2f340f-a44c-4869-ab79-a12ba9459d80</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Discord.Net" Version="3.13.1" />
|
||||||
|
<PackageReference Include="Discord.Net.Commands" Version="3.13.1" />
|
||||||
|
<PackageReference Include="Discord.Net.Core" Version="3.13.1" />
|
||||||
|
<PackageReference Include="Discord.Net.Interactions" Version="3.13.1" />
|
||||||
|
<PackageReference Include="Discord.Net.Rest" Version="3.13.1" />
|
||||||
|
<PackageReference Include="MediatR" Version="12.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Victoria" Version="6.0.23.324" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MediatR;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Lunaris2.Notification;
|
||||||
|
|
||||||
|
public class DiscordEventListener(DiscordSocketClient client, IServiceScopeFactory serviceScope)
|
||||||
|
{
|
||||||
|
private readonly CancellationToken _cancellationToken = new CancellationTokenSource().Token;
|
||||||
|
|
||||||
|
private IMediator Mediator
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var scope = serviceScope.CreateScope();
|
||||||
|
return scope.ServiceProvider.GetRequiredService<IMediator>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync()
|
||||||
|
{
|
||||||
|
client.SlashCommandExecuted += OnMessageReceivedAsync;
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnMessageReceivedAsync(SocketSlashCommand arg)
|
||||||
|
{
|
||||||
|
await Mediator.Publish(new MessageReceivedNotification(arg), _cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Discord.WebSocket;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace Lunaris2.Notification;
|
||||||
|
|
||||||
|
public class MessageReceivedNotification(SocketSlashCommand message) : INotification
|
||||||
|
{
|
||||||
|
public SocketSlashCommand Message { get; } = message ?? throw new ArgumentNullException(nameof(message));
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.Interactions;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Lunaris2.Handler.MusicPlayer;
|
||||||
|
using Lunaris2.Notification;
|
||||||
|
using Lunaris2.SlashCommand;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Victoria;
|
||||||
|
using Victoria.Node;
|
||||||
|
using RunMode = Discord.Commands.RunMode;
|
||||||
|
|
||||||
|
namespace Lunaris2;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
private static LavaNode? _lavaNode;
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureServices((_, services) =>
|
||||||
|
{
|
||||||
|
var config = new DiscordSocketConfig
|
||||||
|
{
|
||||||
|
GatewayIntents = GatewayIntents.All
|
||||||
|
};
|
||||||
|
|
||||||
|
var commandServiceConfig = new CommandServiceConfig{ DefaultRunMode = RunMode.Async };
|
||||||
|
|
||||||
|
var client = new DiscordSocketClient(config);
|
||||||
|
var commands = new CommandService(commandServiceConfig);
|
||||||
|
var configuration = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(AppContext.BaseDirectory)
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
services.AddSingleton(client)
|
||||||
|
.AddSingleton(commands)
|
||||||
|
.AddMediatR(configuration => configuration.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()))
|
||||||
|
.AddSingleton<DiscordEventListener>()
|
||||||
|
.AddSingleton(service => new InteractionService(service.GetRequiredService<DiscordSocketClient>()))
|
||||||
|
.AddLavaNode(nodeConfiguration =>
|
||||||
|
{
|
||||||
|
nodeConfiguration.SelfDeaf = false;
|
||||||
|
nodeConfiguration.Hostname = configuration["LavaLinkHostname"];
|
||||||
|
nodeConfiguration.Port = Convert.ToUInt16(configuration["LavaLinkPort"]);
|
||||||
|
nodeConfiguration.Authorization = configuration["LavaLinkPassword"];
|
||||||
|
})
|
||||||
|
.AddSingleton<LavaNode>()
|
||||||
|
.AddSingleton<MusicEmbed>();
|
||||||
|
|
||||||
|
client.Ready += () => Client_Ready(client);
|
||||||
|
client.Log += Log;
|
||||||
|
|
||||||
|
client
|
||||||
|
.LoginAsync(TokenType.Bot, configuration["Token"])
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
|
||||||
|
client
|
||||||
|
.StartAsync()
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
|
||||||
|
_lavaNode = services.BuildServiceProvider().GetRequiredService<LavaNode>();
|
||||||
|
|
||||||
|
var listener = services
|
||||||
|
.BuildServiceProvider()
|
||||||
|
.GetRequiredService<DiscordEventListener>();
|
||||||
|
|
||||||
|
listener
|
||||||
|
.StartAsync()
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
});
|
||||||
|
|
||||||
|
private static async Task Client_Ready(DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
await _lavaNode.ConnectAsync();
|
||||||
|
client.RegisterCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task Log(LogMessage arg)
|
||||||
|
{
|
||||||
|
Console.WriteLine(arg);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
## How commands from Discord gets executed
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Program[Program] -->|Register| EventListener
|
||||||
|
EventListener[DiscordEventListener] --> A
|
||||||
|
|
||||||
|
A[MessageReceivedHandler] -->|Message| C{Send to correct command by
|
||||||
|
looking at commandName}
|
||||||
|
|
||||||
|
C -->|JoinCommand| D[JoinHandler]
|
||||||
|
C -->|PlayCommand| E[PlayHandler]
|
||||||
|
C -->|HelloCommand| F[HelloHandler]
|
||||||
|
C -->|GoodbyeCommand| G[GoodbyeHandler]
|
||||||
|
```
|
||||||
|
|
||||||
|
Program registers an event listener ```DiscordEventListener``` which publish a message :
|
||||||
|
|
||||||
|
```c#
|
||||||
|
await Mediator.Publish(new MessageReceivedNotification(arg), _cancellationToken);
|
||||||
|
```
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using Discord;
|
||||||
|
|
||||||
|
namespace Lunaris2.SlashCommand;
|
||||||
|
|
||||||
|
public static class Option
|
||||||
|
{
|
||||||
|
public const string Input = "input";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Command
|
||||||
|
{
|
||||||
|
public static class Hello
|
||||||
|
{
|
||||||
|
public const string Name = "hello";
|
||||||
|
public const string Description = "Say hello to the bot!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Goodbye
|
||||||
|
{
|
||||||
|
public const string Name = "goodbye";
|
||||||
|
public const string Description = "Say goodbye to the bot!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Join
|
||||||
|
{
|
||||||
|
public const string Name = "join";
|
||||||
|
public const string Description = "Join the voice channel!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Skip
|
||||||
|
{
|
||||||
|
public const string Name = "skip";
|
||||||
|
public const string Description = "Skip the current song!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Stop
|
||||||
|
{
|
||||||
|
public const string Name = "stop";
|
||||||
|
public const string Description = "Stop the music!";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Play
|
||||||
|
{
|
||||||
|
public const string Name = "play";
|
||||||
|
public const string Description = "Play a song!";
|
||||||
|
|
||||||
|
public static readonly List<SlashCommandOptionBuilder>? Options = new()
|
||||||
|
{
|
||||||
|
new SlashCommandOptionBuilder
|
||||||
|
{
|
||||||
|
Name = "input",
|
||||||
|
Description = "The song you want to play",
|
||||||
|
Type = ApplicationCommandOptionType.String,
|
||||||
|
IsRequired = true
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] GetAllCommands()
|
||||||
|
{
|
||||||
|
return typeof(Command)
|
||||||
|
.GetNestedTypes()
|
||||||
|
.Select(command => command.GetField("Name")?.GetValue(null)?.ToString())
|
||||||
|
.ToArray()!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Net;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Lunaris2.SlashCommand;
|
||||||
|
|
||||||
|
public class SlashCommandBuilder(
|
||||||
|
string commandName,
|
||||||
|
string commandDescription,
|
||||||
|
List<SlashCommandOptionBuilder>? commandOptions = null)
|
||||||
|
{
|
||||||
|
private string CommandName { get; set; } = commandName;
|
||||||
|
private string CommandDescription { get; set; } = commandDescription;
|
||||||
|
private List<SlashCommandOptionBuilder>? CommandOptions { get; set; } = commandOptions;
|
||||||
|
|
||||||
|
public async Task CreateSlashCommand(DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
var registeredCommands = await client.GetGlobalApplicationCommandsAsync();
|
||||||
|
await RemoveUnusedCommands(Command.GetAllCommands(), registeredCommands);
|
||||||
|
|
||||||
|
if (await CommandExists(registeredCommands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var globalCommand = new Discord.SlashCommandBuilder();
|
||||||
|
globalCommand.WithName(CommandName);
|
||||||
|
globalCommand.WithDescription(CommandDescription);
|
||||||
|
|
||||||
|
CommandOptions?.ForEach(option => globalCommand.AddOption(option));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.CreateGlobalApplicationCommandAsync(globalCommand.Build());
|
||||||
|
Console.WriteLine($"Command {CommandName} has been registered.");
|
||||||
|
}
|
||||||
|
catch(HttpException exception)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
|
||||||
|
Console.WriteLine(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task RemoveUnusedCommands(string[] commands, IEnumerable<SocketApplicationCommand> registeredCommands)
|
||||||
|
{
|
||||||
|
// Remove commands from Discord(registeredCommands) that are not in the list of commands
|
||||||
|
foreach(var command in registeredCommands)
|
||||||
|
{
|
||||||
|
if (commands.Contains(command.Name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
await command.DeleteAsync();
|
||||||
|
Console.WriteLine($"Command {command.Name} has been removed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<bool> CommandExists(IEnumerable<SocketApplicationCommand> registeredCommands)
|
||||||
|
{
|
||||||
|
if (!registeredCommands.Any(command => command.Name == CommandName && command.Description == CommandDescription))
|
||||||
|
return Task.FromResult(false);
|
||||||
|
|
||||||
|
Console.WriteLine($"Command {CommandName} already exists.");
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
namespace Lunaris2.SlashCommand;
|
||||||
|
|
||||||
|
public static class SlashCommandRegistration
|
||||||
|
{
|
||||||
|
public static void RegisterCommands(this DiscordSocketClient client)
|
||||||
|
{
|
||||||
|
RegisterCommand(client, Command.Hello.Name, Command.Hello.Description);
|
||||||
|
RegisterCommand(client, Command.Goodbye.Name, Command.Goodbye.Description);
|
||||||
|
RegisterCommand(client, Command.Join.Name, Command.Join.Description);
|
||||||
|
RegisterCommand(client, Command.Skip.Name, Command.Skip.Description);
|
||||||
|
RegisterCommand(client, Command.Play.Name, Command.Play.Description, Command.Play.Options);
|
||||||
|
RegisterCommand(client, Command.Stop.Name, Command.Stop.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RegisterCommand(
|
||||||
|
DiscordSocketClient client,
|
||||||
|
string commandName,
|
||||||
|
string commandDescription,
|
||||||
|
List<SlashCommandOptionBuilder>? commandOptions = null)
|
||||||
|
{
|
||||||
|
var command = new SlashCommandBuilder(commandName, commandDescription, commandOptions);
|
||||||
|
_ = command.CreateSlashCommand(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Token": "discordToken",
|
||||||
|
"LavaLinkPassword": "youshallnotpass",
|
||||||
|
"LavaLinkHostname": "127.0.0.1",
|
||||||
|
"LavaLinkPort": 2333
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.9.34701.34
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lunaris2", "Bot/Lunaris2.csproj", "{3A16FD4C-EE50-4B05-9A75-0DDCD93AA615}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{3A16FD4C-EE50-4B05-9A75-0DDCD93AA615}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3A16FD4C-EE50-4B05-9A75-0DDCD93AA615}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3A16FD4C-EE50-4B05-9A75-0DDCD93AA615}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3A16FD4C-EE50-4B05-9A75-0DDCD93AA615}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {4E738D71-9DB0-456E-9BF2-0CE3FED1DE44}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -1,2 +1,31 @@
|
|||||||
# Lunaris2.0
|
# Lunaris2 - Discord Music Bot
|
||||||
WIP
|
|
||||||
|
Lunaris2 is a Discord bot designed to play music in your server's voice channels. It's built using C# and the Discord.Net library, and it uses the LavaLink music client for audio streaming.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Play music from YouTube directly in your Discord server.
|
||||||
|
- Skip tracks, pause, and resume playback.
|
||||||
|
- Queue system to line up your favorite tracks.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Clone the repo.
|
||||||
|
2. Extract.
|
||||||
|
3. If there isn't already a appsettings.json file in there, create one.
|
||||||
|
4. Set the discord bot token. How the file should look (without token): [appsettings.json](https://github.com/Myxelium/Lunaris2.0/blob/master/Bot/appsettings.json)]
|
||||||
|
5. Make sure you got docker installed. And run the file ``start-services.sh``, make sure you got git-bash installed.
|
||||||
|
6. Now you can start the project and run the application.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- `/play <song>`: Plays the specified song in the voice channel you're currently in.
|
||||||
|
- `/skip`: Skips the currently playing song.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](https://choosealicense.com/licenses/mit/)
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
server: # REST and WS server
|
||||||
|
port: 2333
|
||||||
|
address: 0.0.0.0
|
||||||
|
plugins:
|
||||||
|
# name: # Name of the plugin
|
||||||
|
# some_key: some_value # Some key-value pair for the plugin
|
||||||
|
# another_key: another_value
|
||||||
|
lavalink:
|
||||||
|
plugins:
|
||||||
|
# - dependency: "group:artifact:version"
|
||||||
|
# repository: "repository"
|
||||||
|
server:
|
||||||
|
password: "youshallnotpass"
|
||||||
|
sources:
|
||||||
|
youtube: true
|
||||||
|
bandcamp: true
|
||||||
|
soundcloud: true
|
||||||
|
twitch: true
|
||||||
|
vimeo: true
|
||||||
|
http: true
|
||||||
|
local: false
|
||||||
|
filters: # All filters are enabled by default
|
||||||
|
volume: true
|
||||||
|
equalizer: true
|
||||||
|
karaoke: true
|
||||||
|
timescale: true
|
||||||
|
tremolo: true
|
||||||
|
vibrato: true
|
||||||
|
distortion: true
|
||||||
|
rotation: true
|
||||||
|
channelMix: true
|
||||||
|
lowPass: true
|
||||||
|
bufferDurationMs: 400 # The duration of the NAS buffer. Higher values fare better against longer GC pauses. Duration <= 0 to disable JDA-NAS. Minimum of 40ms, lower values may introduce pauses.
|
||||||
|
frameBufferDurationMs: 5000 # How many milliseconds of audio to keep buffered
|
||||||
|
opusEncodingQuality: 10 # Opus encoder quality. Valid values range from 0 to 10, where 10 is best quality but is the most expensive on the CPU.
|
||||||
|
resamplingQuality: LOW # Quality of resampling operations. Valid values are LOW, MEDIUM and HIGH, where HIGH uses the most CPU.
|
||||||
|
trackStuckThresholdMs: 10000 # The threshold for how long a track can be stuck. A track is stuck if does not return any audio data.
|
||||||
|
useSeekGhosting: true # Seek ghosting is the effect where whilst a seek is in progress, the audio buffer is read from until empty, or until seek is ready.
|
||||||
|
youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
|
||||||
|
playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
|
||||||
|
youtubeSearchEnabled: true
|
||||||
|
soundcloudSearchEnabled: true
|
||||||
|
gc-warnings: true
|
||||||
|
#ratelimit:
|
||||||
|
#ipBlocks: ["1.0.0.0/8", "..."] # list of ip blocks
|
||||||
|
#excludedIps: ["...", "..."] # ips which should be explicit excluded from usage by lavalink
|
||||||
|
#strategy: "RotateOnBan" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
||||||
|
#searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
|
||||||
|
#retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
|
||||||
|
#youtubeConfig: # Required for avoiding all age restrictions by YouTube, some restricted videos still can be played without.
|
||||||
|
#email: "" # Email of Google account
|
||||||
|
#password: "" # Password of Google account
|
||||||
|
#httpConfig: # Useful for blocking bad-actors from ip-grabbing your music node and attacking it, this way only the http proxy will be attacked
|
||||||
|
#proxyHost: "localhost" # Hostname of the proxy, (ip or domain)
|
||||||
|
#proxyPort: 3128 # Proxy port, 3128 is the default for squidProxy
|
||||||
|
#proxyUser: "" # Optional user for basic authentication fields, leave blank if you don't use basic auth
|
||||||
|
#proxyPassword: "" # Password for basic authentication
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
prometheus:
|
||||||
|
enabled: false
|
||||||
|
endpoint: /metrics
|
||||||
|
|
||||||
|
sentry:
|
||||||
|
dsn: ""
|
||||||
|
environment: ""
|
||||||
|
# tags:
|
||||||
|
# some_key: some_value
|
||||||
|
# another_key: another_value
|
||||||
|
|
||||||
|
logging:
|
||||||
|
file:
|
||||||
|
path: ./logs/
|
||||||
|
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
|
lavalink: INFO
|
||||||
|
|
||||||
|
request:
|
||||||
|
enabled: true
|
||||||
|
includeClientInfo: true
|
||||||
|
includeHeaders: false
|
||||||
|
includeQueryString: true
|
||||||
|
includePayload: true
|
||||||
|
maxPayloadLength: 10000
|
||||||
|
|
||||||
|
|
||||||
|
logback:
|
||||||
|
rollingpolicy:
|
||||||
|
max-file-size: 1GB
|
||||||
|
max-history: 30
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
services:
|
||||||
|
lavalink:
|
||||||
|
# pin the image version to Lavalink v4
|
||||||
|
image: ghcr.io/lavalink-devs/lavalink:3.7.11
|
||||||
|
container_name: lavalink
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# set Java options here
|
||||||
|
- _JAVA_OPTIONS=-Xmx6G
|
||||||
|
# set lavalink server port
|
||||||
|
- SERVER_PORT=2333
|
||||||
|
# set password for lavalink
|
||||||
|
- LAVALINK_SERVER_PASSWORD=youshallnotpass
|
||||||
|
volumes:
|
||||||
|
# mount application.yml from the same directory or use environment variables
|
||||||
|
- ./application.yml:/opt/Lavalink/application.yml
|
||||||
|
# persist plugins between restarts, make sure to set the correct permissions (user: 322, group: 322)
|
||||||
|
- ./plugins/:/opt/Lavalink/plugins/
|
||||||
|
networks:
|
||||||
|
- lavalink
|
||||||
|
expose:
|
||||||
|
# lavalink exposes port 2333 to connect to for other containers (this is for documentation purposes only)
|
||||||
|
- 2333
|
||||||
|
ports:
|
||||||
|
# you only need this if you want to make your lavalink accessible from outside of containers
|
||||||
|
- "2333:2333"
|
||||||
|
networks:
|
||||||
|
# create a lavalink network you can add other containers to, to give them access to Lavalink
|
||||||
|
lavalink:
|
||||||
|
name: lavalink
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
docker compose up -d
|
||||||
Reference in New Issue
Block a user