first commit
This commit is contained in:
commit
b6abc9f526
25
.dockerignore
Normal file
25
.dockerignore
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.idea
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
130
.idea/.idea.DiscordAudioBot/.idea/workspace.xml
generated
Normal file
130
.idea/.idea.DiscordAudioBot/.idea/workspace.xml
generated
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AutoGeneratedRunConfigurationManager">
|
||||||
|
<projectFile>DiscordAudioBot/DiscordAudioBot.csproj</projectFile>
|
||||||
|
</component>
|
||||||
|
<component name="AutoImportSettings">
|
||||||
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
|
</component>
|
||||||
|
<component name="ChangeListManager">
|
||||||
|
<list default="true" id="f4ab407f-7a01-4e71-a0f0-7d63ad2fa6c3" name="変更" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/.dockerignore" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/.idea/.idea.DiscordAudioBot/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary.sln" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary/Config/Configuration.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary/Dockerfile" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary/Elementary.csproj" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary/Program.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/Elementary/appsettings.json" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||||
|
</list>
|
||||||
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
|
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||||
|
</component>
|
||||||
|
<component name="FileTemplateManagerImpl">
|
||||||
|
<option name="RECENT_TEMPLATES">
|
||||||
|
<list>
|
||||||
|
<option value="JSON File" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="Git.Settings">
|
||||||
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
|
</component>
|
||||||
|
<component name="HighlightingSettingsPerFile">
|
||||||
|
<setting file="file://$PROJECT_DIR$/Elementary/Config/Configuration.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/Elementary/Program.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
</component>
|
||||||
|
<component name="MarkdownSettingsMigration">
|
||||||
|
<option name="stateVersion" value="1" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectId" id="2Sdv0B6dZS0v9XvLddiZrHzEXEw" />
|
||||||
|
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||||
|
<component name="ProjectViewState">
|
||||||
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
|
<option name="showLibraryContents" value="true" />
|
||||||
|
</component>
|
||||||
|
<component name="PropertiesComponent"><![CDATA[{
|
||||||
|
"keyToString": {
|
||||||
|
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||||
|
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||||
|
"WebServerToolWindowFactoryState": "false",
|
||||||
|
"git-widget-placeholder": "master",
|
||||||
|
"node.js.detected.package.eslint": "true",
|
||||||
|
"node.js.detected.package.tslint": "true",
|
||||||
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
|
"node.js.selected.package.tslint": "(autodetect)",
|
||||||
|
"settings.editor.selected.configurable": "preferences.lookFeel",
|
||||||
|
"vue.rearranger.settings.migration": "true"
|
||||||
|
},
|
||||||
|
"keyToStringList": {
|
||||||
|
"rider.external.source.directories": [
|
||||||
|
"C:\\Users\\user\\AppData\\Roaming\\JetBrains\\Rider2023.1\\resharper-host\\DecompilerCache",
|
||||||
|
"C:\\Users\\user\\AppData\\Roaming\\JetBrains\\Rider2023.1\\resharper-host\\SourcesCache",
|
||||||
|
"C:\\Users\\user\\AppData\\Local\\Symbols\\src"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}]]></component>
|
||||||
|
<component name="RunManager" selected=".NET プロジェクト.DiscordAudioBot">
|
||||||
|
<configuration name="DiscordAudioBot" type="DotNetProject" factoryName=".NET Project">
|
||||||
|
<option name="EXE_PATH" value="" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="1" />
|
||||||
|
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||||
|
<option name="USE_MONO" value="0" />
|
||||||
|
<option name="RUNTIME_ARGUMENTS" value="" />
|
||||||
|
<option name="PROJECT_PATH" value="$PROJECT_DIR$/Elementary/Elementary.csproj" />
|
||||||
|
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||||
|
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||||
|
<option name="PROJECT_TFM" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="Build" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
<configuration name="DiscordAudioBot/Dockerfile" type="docker-deploy" factoryName="dockerfile">
|
||||||
|
<deployment type="dockerfile">
|
||||||
|
<settings>
|
||||||
|
<option name="imageTag" value="discordaudiobot" />
|
||||||
|
<option name="containerName" value="discordaudiobot" />
|
||||||
|
<option name="contextFolderPath" value="G:\git\DiscordAudioBot" />
|
||||||
|
<option name="sourceFilePath" value="DiscordAudioBot/Dockerfile" />
|
||||||
|
</settings>
|
||||||
|
</deployment>
|
||||||
|
<EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="true" isSslEnabled="true" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
<configuration default="true" type="docker-deploy" factoryName="dockerfile" temporary="true">
|
||||||
|
<deployment type="dockerfile">
|
||||||
|
<settings />
|
||||||
|
</deployment>
|
||||||
|
<EXTENSION ID="com.jetbrains.rider.docker.debug" isFastModeEnabled="true" isSslEnabled="false" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
|
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="アプリケーションレベル" UseSingleDictionary="true" transferred="true" />
|
||||||
|
<component name="TaskManager">
|
||||||
|
<task active="true" id="Default" summary="デフォルトタスク">
|
||||||
|
<changelist id="f4ab407f-7a01-4e71-a0f0-7d63ad2fa6c3" name="変更" comment="" />
|
||||||
|
<created>1689489047458</created>
|
||||||
|
<option name="number" value="Default" />
|
||||||
|
<option name="presentableId" value="Default" />
|
||||||
|
<updated>1689489047458</updated>
|
||||||
|
<workItem from="1689489050574" duration="4153000" />
|
||||||
|
<workItem from="1689494655108" duration="2977000" />
|
||||||
|
<workItem from="1689502313585" duration="1275000" />
|
||||||
|
</task>
|
||||||
|
<servers />
|
||||||
|
</component>
|
||||||
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
|
<option name="version" value="3" />
|
||||||
|
</component>
|
||||||
|
<component name="VcsManagerConfiguration">
|
||||||
|
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
31
Elementary.sln
Normal file
31
Elementary.sln
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.6.33815.320
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elementary", "Elementary\Elementary.csproj", "{E853B381-09A7-46CF-88CC-62C44B5ACA89}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{E853B381-09A7-46CF-88CC-62C44B5ACA89}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {C3A79BA4-F57B-4B08-A752-906D0AF3800F}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
36
Elementary/Audio/AudioConverter.cs
Normal file
36
Elementary/Audio/AudioConverter.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using NAudio;
|
||||||
|
using NAudio.Wave;
|
||||||
|
// using ManagedBass;
|
||||||
|
// using ManagedBass.Mix;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class AudioConverter
|
||||||
|
{
|
||||||
|
public readonly WaveFormat WaveFormat = new(48000, 16, 2);
|
||||||
|
|
||||||
|
public WaveFormatConversionStream CreateStreamFromFilePath(string path, float volume = 1.0f)
|
||||||
|
=> new(WaveFormat, new VolumeAdjustedWaveStream(new MediaFoundationReader(path), volume));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return PCM stream from any codec of audio stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public WaveFormatConversionStream CreateStreamFromStream(Stream stream)
|
||||||
|
{
|
||||||
|
return new(WaveFormat, new StreamMediaFoundationReader(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return PCM stream from any codec of audio stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="volume"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public WaveFormatConversionStream CreateStreamFromStream(Stream stream, float volume = 1.0f)
|
||||||
|
{
|
||||||
|
return new(WaveFormat, new VolumeAdjustedWaveStream(new StreamMediaFoundationReader(stream), volume));
|
||||||
|
}
|
||||||
|
}
|
93
Elementary/Audio/AudioManager.cs
Normal file
93
Elementary/Audio/AudioManager.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Audio;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using ManagedBass;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NAudio.Wave;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class AudioManager
|
||||||
|
{
|
||||||
|
private DiscordSocketClient _client;
|
||||||
|
private IServiceProvider _services;
|
||||||
|
private IAudioClient _audioClient;
|
||||||
|
private AudioOutStream _audioStream;
|
||||||
|
|
||||||
|
private SozaiAPI _sozaiAPI;
|
||||||
|
private VoicevoxAPI _voicevoxAPI;
|
||||||
|
|
||||||
|
private AudioConverter _audioConverter;
|
||||||
|
|
||||||
|
private PlaybackQueue _playbackQueue;
|
||||||
|
// private AudioMixer _audioMixer;
|
||||||
|
|
||||||
|
public bool isConnected;
|
||||||
|
|
||||||
|
public AudioManager(IServiceProvider services, DiscordSocketClient client, SozaiAPI sozaiApi,
|
||||||
|
VoicevoxAPI voicevoxApi)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
_client = client;
|
||||||
|
_sozaiAPI = sozaiApi;
|
||||||
|
_voicevoxAPI = voicevoxApi;
|
||||||
|
_audioConverter = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task JoinChannel(IVoiceChannel channel)
|
||||||
|
{
|
||||||
|
_audioClient = await channel.ConnectAsync(true);
|
||||||
|
|
||||||
|
_audioStream = _audioClient.CreatePCMStream(AudioApplication.Music, 128 * 1024);
|
||||||
|
|
||||||
|
_playbackQueue = _services.GetRequiredService<PlaybackQueue>();
|
||||||
|
|
||||||
|
// _audioMixer = new AudioMixer(_audioStream);
|
||||||
|
|
||||||
|
isConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LeaveChannel()
|
||||||
|
{
|
||||||
|
await _audioClient.StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PlayAudio(string path)
|
||||||
|
{
|
||||||
|
await using var wave = _audioConverter.CreateStreamFromFilePath(path, 0.15f);
|
||||||
|
|
||||||
|
// _audioMixer.AddStream(wave);
|
||||||
|
await wave.CopyToAsync(_audioStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PlayText(string text)
|
||||||
|
{
|
||||||
|
if (text.Contains("```")) text = "コードブロック";
|
||||||
|
if (text.StartsWith("!") || text.StartsWith(".")) return;
|
||||||
|
text = Regex.Replace(text, @"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "URL");
|
||||||
|
|
||||||
|
float volume = 0.12f;
|
||||||
|
|
||||||
|
Stream? stream = await _sozaiAPI.GetAudioStream(text);
|
||||||
|
if (stream == null)
|
||||||
|
{
|
||||||
|
stream = await _voicevoxAPI.Speak(text);
|
||||||
|
volume = 0.8f;
|
||||||
|
if (stream == null) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var wave = _audioConverter.CreateStreamFromStream(stream, volume);
|
||||||
|
// _audioMixer.AddStream(wave);
|
||||||
|
await wave.CopyToAsync(_audioStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAudio()
|
||||||
|
{
|
||||||
|
// _audioMixer.Stop();
|
||||||
|
_playbackQueue.Flush();
|
||||||
|
await _audioStream.FlushAsync();
|
||||||
|
}
|
||||||
|
}
|
80
Elementary/Audio/AudioMixer.cs
Normal file
80
Elementary/Audio/AudioMixer.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using ManagedBass;
|
||||||
|
using ManagedBass.Mix;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class AudioMixer
|
||||||
|
{
|
||||||
|
private readonly int _mixerStream;
|
||||||
|
private MemoryStream?[] _inputStreams;
|
||||||
|
private Stream _outStream;
|
||||||
|
private object _lock = new();
|
||||||
|
|
||||||
|
public AudioMixer(Stream outStream)
|
||||||
|
{
|
||||||
|
_outStream = outStream;
|
||||||
|
Bass.Init(-1, 44100, DeviceInitFlags.NoSpeakerAssignment, IntPtr.Zero);
|
||||||
|
_mixerStream = BassMix.CreateMixerStream(44100, 2, BassFlags.Float | BassFlags.MixerNonStop);
|
||||||
|
_inputStreams = new MemoryStream[10];
|
||||||
|
|
||||||
|
Bass.ChannelPlay(_mixerStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a stream to the mixer.
|
||||||
|
/// wait for the stream to finish playing
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="volume"></param>
|
||||||
|
public void AddStream(Stream stream, float volume = 1.0f)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Adding stream to mixer");
|
||||||
|
byte[] pcmBytes;
|
||||||
|
using (MemoryStream ms = new())
|
||||||
|
{
|
||||||
|
stream.CopyTo(ms);
|
||||||
|
pcmBytes = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < _inputStreams.Length; i++)
|
||||||
|
{
|
||||||
|
if (_inputStreams[i] == null)
|
||||||
|
{
|
||||||
|
_inputStreams[i] = new MemoryStream(pcmBytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel = Bass.CreateStream(pcmBytes, 0, pcmBytes.Length, BassFlags.Float);
|
||||||
|
Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, volume);
|
||||||
|
BassMix.MixerAddChannel(_mixerStream, channel, BassFlags.Default);
|
||||||
|
|
||||||
|
int length = Bass.ChannelGetData(_mixerStream, new byte[4096], 4096);
|
||||||
|
byte[] buffer = new byte[length];
|
||||||
|
length = Bass.ChannelGetData(_mixerStream, buffer, length);
|
||||||
|
_outStream.Write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
Bass.ChannelStop(_mixerStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
foreach (var stream in _inputStreams)
|
||||||
|
{
|
||||||
|
stream?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_outStream.Flush();
|
||||||
|
Bass.StreamFree(_mixerStream);
|
||||||
|
Bass.Free();
|
||||||
|
}
|
||||||
|
}
|
10
Elementary/Audio/IAudioConverter.cs
Normal file
10
Elementary/Audio/IAudioConverter.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public interface IAudioConverter
|
||||||
|
{
|
||||||
|
public Process CreateStreamFromFilePath(string path);
|
||||||
|
|
||||||
|
public Process CreateStreamFromStream(Stream stream);
|
||||||
|
}
|
25
Elementary/Audio/MessageHandler.cs
Normal file
25
Elementary/Audio/MessageHandler.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Discord.Audio;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class MessageHandler
|
||||||
|
{
|
||||||
|
// private IAudioClient _audioClient;
|
||||||
|
private PlaybackQueue _playbackQueue;
|
||||||
|
|
||||||
|
public MessageHandler(PlaybackQueue playbackQueue)
|
||||||
|
{
|
||||||
|
_playbackQueue = playbackQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task HandleMessage(SocketMessage message)
|
||||||
|
{
|
||||||
|
await _playbackQueue.Enqueue(new PlaybackJob()
|
||||||
|
{
|
||||||
|
Type = JobType.Text,
|
||||||
|
Text = message.Content
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
94
Elementary/Audio/PlaybackQueue.cs
Normal file
94
Elementary/Audio/PlaybackQueue.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Discord.Audio;
|
||||||
|
using NAudio.Wave;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public enum JobType
|
||||||
|
{
|
||||||
|
Audio,
|
||||||
|
Text,
|
||||||
|
// Sozai
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlaybackJob
|
||||||
|
{
|
||||||
|
public JobType Type;
|
||||||
|
public string Text;
|
||||||
|
public float Volume = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlaybackQueue
|
||||||
|
{
|
||||||
|
// any type of job ConcurrentQueue
|
||||||
|
|
||||||
|
private ConcurrentQueue<PlaybackJob> _queue;
|
||||||
|
|
||||||
|
private AudioManager _audioManager;
|
||||||
|
|
||||||
|
private object _lock;
|
||||||
|
|
||||||
|
private bool _isPlaying;
|
||||||
|
|
||||||
|
public PlaybackQueue(AudioManager audioManager)
|
||||||
|
{
|
||||||
|
_queue = new();
|
||||||
|
_isPlaying = false;
|
||||||
|
_lock = new();
|
||||||
|
_audioManager = audioManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueue audio stream and play it
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="job"></param>
|
||||||
|
public async Task Enqueue(PlaybackJob job)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(job);
|
||||||
|
Console.WriteLine("Enqueued");
|
||||||
|
if (!_isPlaying)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!_isPlaying)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Start Playing due to empty queue");
|
||||||
|
PlayNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
_queue.Clear();
|
||||||
|
Console.WriteLine("Queue Flushed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PlayNext()
|
||||||
|
{
|
||||||
|
if (_queue.TryDequeue(out var currentStream))
|
||||||
|
{
|
||||||
|
Console.WriteLine("Start Playing");
|
||||||
|
|
||||||
|
_isPlaying = true;
|
||||||
|
await (currentStream.Type switch
|
||||||
|
{
|
||||||
|
JobType.Audio => _audioManager.PlayAudio(currentStream.Text),
|
||||||
|
JobType.Text => _audioManager.PlayText(currentStream.Text),
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
});
|
||||||
|
Console.WriteLine("Finished Playing");
|
||||||
|
await Task.Delay(200);
|
||||||
|
_isPlaying = false;
|
||||||
|
PlayNext();
|
||||||
|
}
|
||||||
|
else if (_queue.IsEmpty)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Queue is empty");
|
||||||
|
_isPlaying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
Elementary/Audio/SozaiAPI.cs
Normal file
52
Elementary/Audio/SozaiAPI.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class SozaiAPI
|
||||||
|
{
|
||||||
|
private Uri AssetsInfoUrl = new("https://synchthia-sounds.storage.googleapis.com/index.json");
|
||||||
|
private Asset[] Assets;
|
||||||
|
|
||||||
|
private class Asset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SHA1 hash of the file
|
||||||
|
/// </summary>
|
||||||
|
public string hash { get; set; }
|
||||||
|
|
||||||
|
public string id { get; set; }
|
||||||
|
public string[] names { get; set; }
|
||||||
|
public string[] namespaces { get; set; }
|
||||||
|
public string path { get; set; }
|
||||||
|
public string url { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient _client;
|
||||||
|
|
||||||
|
public SozaiAPI()
|
||||||
|
{
|
||||||
|
_client = new HttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Setup()
|
||||||
|
{
|
||||||
|
var response = await _client.GetAsync(AssetsInfoUrl);
|
||||||
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
|
Assets = await JsonSerializer.DeserializeAsync<Asset[]>(stream);
|
||||||
|
|
||||||
|
Console.WriteLine($"Loaded {Assets.Length} assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Stream?> GetAudioStream(string name)
|
||||||
|
{
|
||||||
|
var asset = Assets.FirstOrDefault(asset => asset.names.Contains(name));
|
||||||
|
if (asset == null) return null;
|
||||||
|
|
||||||
|
Console.WriteLine($"Requested {asset.names[0]}");
|
||||||
|
|
||||||
|
var response = await _client.GetAsync(asset.url);
|
||||||
|
Console.WriteLine($"Got response {response.StatusCode}");
|
||||||
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
58
Elementary/Audio/VoicevoxAPI.cs
Normal file
58
Elementary/Audio/VoicevoxAPI.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class VoicevoxAPI
|
||||||
|
{
|
||||||
|
private UriBuilder _APIRootUrl;
|
||||||
|
private HttpClient _client;
|
||||||
|
|
||||||
|
public async Task Setup(string url)
|
||||||
|
{
|
||||||
|
Uri _url = new(url);
|
||||||
|
|
||||||
|
_APIRootUrl = new UriBuilder($"{_url.Scheme}://{_url.Host}:{_url.Port}");
|
||||||
|
_client = new HttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a stream of the 24000Hz wave audio generated from the text
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
/// <param name="speaker"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<Stream?> Speak(string text, string speaker = "47")
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Requested TTS {text}");
|
||||||
|
|
||||||
|
var query = await GetAudioQuery(text, speaker);
|
||||||
|
if (query == null) return null;
|
||||||
|
var stream = await GetAudioStream(query, speaker);
|
||||||
|
if (stream == null) return null;
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<string?> GetAudioQuery(string text, string speaker = "1")
|
||||||
|
{
|
||||||
|
UriBuilder builder = new(_APIRootUrl.Uri);
|
||||||
|
builder.Path = "/audio_query";
|
||||||
|
builder.Query = $"text={text}&speaker={speaker}";
|
||||||
|
|
||||||
|
var response = await _client.PostAsync(builder.Uri, new StringContent(string.Empty));
|
||||||
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
|
return await new StreamReader(stream).ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task<Stream?> GetAudioStream(string query, string speaker = "1")
|
||||||
|
{
|
||||||
|
UriBuilder builder = new(_APIRootUrl.Uri);
|
||||||
|
builder.Path = "/synthesis";
|
||||||
|
builder.Query = $"speaker={speaker}";
|
||||||
|
|
||||||
|
var response =
|
||||||
|
await _client.PostAsync(builder.Uri, new StringContent(query, Encoding.UTF8, "application/json"));
|
||||||
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
52
Elementary/Audio/VolumeAdjustedWaveStream.cs
Normal file
52
Elementary/Audio/VolumeAdjustedWaveStream.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using NAudio.Wave;
|
||||||
|
|
||||||
|
namespace Elementary.Audio;
|
||||||
|
|
||||||
|
public class VolumeAdjustedWaveStream : WaveStream
|
||||||
|
{
|
||||||
|
private readonly WaveStream _waveStream;
|
||||||
|
private readonly float _volume;
|
||||||
|
|
||||||
|
public VolumeAdjustedWaveStream(WaveStream waveStream, float volume)
|
||||||
|
{
|
||||||
|
_waveStream = waveStream;
|
||||||
|
_volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override WaveFormat WaveFormat => _waveStream.WaveFormat;
|
||||||
|
|
||||||
|
public override long Length => _waveStream.Length;
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _waveStream.Position;
|
||||||
|
set => _waveStream.Position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var read = _waveStream.Read(buffer, offset, count);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (var i = 0; i < read; i += 2)
|
||||||
|
{
|
||||||
|
var sample = (short) ((buffer[offset + i + 1] << 8) | buffer[offset + i]);
|
||||||
|
sample = (short) (sample * _volume);
|
||||||
|
buffer[offset + i] = (byte) (sample & 0xFF);
|
||||||
|
buffer[offset + i + 1] = (byte) (sample >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
// _waveStream.Dispose();
|
||||||
|
// base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
59
Elementary/Commands/Handler.cs
Normal file
59
Elementary/Commands/Handler.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Discord.Audio;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Elementary.Audio;
|
||||||
|
|
||||||
|
namespace Elementary.Commands;
|
||||||
|
|
||||||
|
public class Handler
|
||||||
|
{
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly CommandService _commands;
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
private readonly AudioManager _audioManager;
|
||||||
|
private readonly MessageHandler _messageHandler;
|
||||||
|
|
||||||
|
// Retrieve client and CommandService instance via ctor
|
||||||
|
public Handler(DiscordSocketClient client, CommandService commands, IServiceProvider services,
|
||||||
|
AudioManager audioManager, MessageHandler messageHandler)
|
||||||
|
{
|
||||||
|
_commands = commands;
|
||||||
|
_client = client;
|
||||||
|
_services = services;
|
||||||
|
_audioManager = audioManager;
|
||||||
|
_messageHandler = messageHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Setup()
|
||||||
|
{
|
||||||
|
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
|
||||||
|
|
||||||
|
_client.MessageReceived += HandleMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleMessage(SocketMessage messageParam)
|
||||||
|
{
|
||||||
|
var message = messageParam as SocketUserMessage;
|
||||||
|
if (message == null || message.Author.IsBot) return;
|
||||||
|
|
||||||
|
var argPos = 0;
|
||||||
|
|
||||||
|
if (message.HasCharPrefix('!', ref argPos) ||
|
||||||
|
message.HasMentionPrefix(_client.CurrentUser, ref argPos))
|
||||||
|
{
|
||||||
|
SocketCommandContext context = new(_client, message);
|
||||||
|
|
||||||
|
await _commands.ExecuteAsync(
|
||||||
|
context,
|
||||||
|
argPos,
|
||||||
|
_services);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_audioManager.isConnected)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Handling message");
|
||||||
|
await _messageHandler.HandleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
Elementary/Commands/MessageCommands.cs
Normal file
80
Elementary/Commands/MessageCommands.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Elementary.Audio;
|
||||||
|
|
||||||
|
namespace Elementary.Commands;
|
||||||
|
|
||||||
|
public class MessageCommands : ModuleBase<SocketCommandContext>
|
||||||
|
{
|
||||||
|
private AudioManager _audioManager;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly CommandService _commandService;
|
||||||
|
|
||||||
|
public MessageCommands(AudioManager audioManager, CommandService commandService, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_audioManager = audioManager;
|
||||||
|
_commandService = commandService;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("help")]
|
||||||
|
[Summary("Displays a list of commands.")]
|
||||||
|
public async Task HelpAsync()
|
||||||
|
{
|
||||||
|
EmbedBuilder embedBuilder = new()
|
||||||
|
{
|
||||||
|
Title = "Elementary Commands",
|
||||||
|
Description = "A list of commands for Elementary.",
|
||||||
|
Color = Color.Blue
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var module in _commandService.Modules)
|
||||||
|
{
|
||||||
|
string description = null;
|
||||||
|
foreach (var command in module.Commands)
|
||||||
|
{
|
||||||
|
var result = await command.CheckPreconditionsAsync(Context, _serviceProvider);
|
||||||
|
if (result.IsSuccess) description += $"!{command.Name} - {command.Summary}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(description)) embedBuilder.AddField(module.Name, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
await ReplyAsync(embed: embedBuilder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("say")]
|
||||||
|
[Summary("Echoes a message.")]
|
||||||
|
public Task SayAsync([Remainder] [Summary("The text to echo")] string echo)
|
||||||
|
{
|
||||||
|
return ReplyAsync(echo);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("join", RunMode = RunMode.Async)]
|
||||||
|
[Summary("Joins a voice channel.")]
|
||||||
|
public async Task JoinAsync(IVoiceChannel channel = null)
|
||||||
|
{
|
||||||
|
await _audioManager.JoinChannel(channel ?? (Context.User as IGuildUser)?.VoiceChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("leave", RunMode = RunMode.Async)]
|
||||||
|
[Summary("Leaves a voice channel.")]
|
||||||
|
public async Task LeaveAsync()
|
||||||
|
{
|
||||||
|
await _audioManager.LeaveChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("play", RunMode = RunMode.Async)]
|
||||||
|
[Summary("Plays audio from a file.")]
|
||||||
|
public async Task PlayAsync([Remainder] [Summary("The path to the file")] string path)
|
||||||
|
{
|
||||||
|
await _audioManager.PlayAudio(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("stop", RunMode = RunMode.Async)]
|
||||||
|
[Summary("Stops playing audio.")]
|
||||||
|
public async Task StopAsync()
|
||||||
|
{
|
||||||
|
await _audioManager.StopAudio();
|
||||||
|
}
|
||||||
|
}
|
31
Elementary/Config/Configuration.cs
Normal file
31
Elementary/Config/Configuration.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Configuration.Json;
|
||||||
|
|
||||||
|
namespace Elementary.Config;
|
||||||
|
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public DiscordSettings DiscordSettings { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DiscordSettings
|
||||||
|
{
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Configuration
|
||||||
|
{
|
||||||
|
public AppSettings AppSettings { get; set; }
|
||||||
|
|
||||||
|
public static Configuration LoadFromJson()
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json", false, true);
|
||||||
|
var configuration = builder.Build();
|
||||||
|
|
||||||
|
var config = new Configuration();
|
||||||
|
configuration.Bind(config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
18
Elementary/Dockerfile
Normal file
18
Elementary/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/runtime:7.0 AS base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["DiscordAudioBot/DiscordAudioBot.csproj", "DiscordAudioBot/"]
|
||||||
|
RUN dotnet restore "DiscordAudioBot/DiscordAudioBot.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/DiscordAudioBot"
|
||||||
|
RUN dotnet build "DiscordAudioBot.csproj" -c Release -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
RUN dotnet publish "DiscordAudioBot.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "DiscordAudioBot.dll"]
|
52
Elementary/Elementary.csproj
Normal file
52
Elementary/Elementary.csproj
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<Configurations>Debug;Release</Configurations>
|
||||||
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\.dockerignore">
|
||||||
|
<Link>.dockerignore</Link>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||||
|
<PackageReference Include="Discord.Net" Version="3.11.0" />
|
||||||
|
<PackageReference Include="Discord.Net.Core" Version="3.11.0" />
|
||||||
|
<PackageReference Include="ManagedBass" Version="3.1.1" />
|
||||||
|
<PackageReference Include="ManagedBass.Mix" Version="3.1.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||||
|
<PackageReference Include="NAudio" Version="2.1.0" />
|
||||||
|
<PackageReference Include="System.Speech" Version="7.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="libsodium.dll">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="opus.dll">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="bass.dll">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="bassmix.dll">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
76
Elementary/Program.cs
Normal file
76
Elementary/Program.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Audio;
|
||||||
|
using Discord.Commands;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using Elementary.Audio;
|
||||||
|
using Elementary.Commands;
|
||||||
|
using ManagedBass;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NAudio.Wave;
|
||||||
|
using Configuration = Elementary.Config.Configuration;
|
||||||
|
|
||||||
|
namespace Elementary;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
private DiscordSocketClient _client;
|
||||||
|
private IServiceProvider _services;
|
||||||
|
private CommandService _commands;
|
||||||
|
private Handler _handler;
|
||||||
|
private SozaiAPI _sozaiAPI;
|
||||||
|
private VoicevoxAPI _voicevoxAPI;
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
new Program().Setup().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Setup()
|
||||||
|
{
|
||||||
|
var configuration = Configuration.LoadFromJson();
|
||||||
|
_client = new DiscordSocketClient(new DiscordSocketConfig
|
||||||
|
{
|
||||||
|
GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
|
||||||
|
});
|
||||||
|
_commands = new CommandService();
|
||||||
|
_services = new ServiceCollection()
|
||||||
|
.AddSingleton(_client)
|
||||||
|
.AddSingleton(_commands)
|
||||||
|
.AddSingleton<Handler>()
|
||||||
|
.AddSingleton<AudioManager>()
|
||||||
|
.AddSingleton<VoicevoxAPI>()
|
||||||
|
.AddSingleton<SozaiAPI>()
|
||||||
|
.AddSingleton<PlaybackQueue>()
|
||||||
|
.AddTransient<AudioConverter>()
|
||||||
|
.AddSingleton<MessageHandler>()
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
_sozaiAPI = _services.GetRequiredService<SozaiAPI>();
|
||||||
|
_voicevoxAPI = _services.GetRequiredService<VoicevoxAPI>();
|
||||||
|
|
||||||
|
_handler = new Handler(_client, _commands, _services, _services.GetRequiredService<AudioManager>(),
|
||||||
|
_services.GetRequiredService<MessageHandler>());
|
||||||
|
|
||||||
|
_client.Log += Log;
|
||||||
|
_commands.Log += Log;
|
||||||
|
|
||||||
|
// Bass.Init(-1, 44100, DeviceInitFlags.NoSpeakerAssignment, IntPtr.Zero);
|
||||||
|
|
||||||
|
await _handler.Setup();
|
||||||
|
await _sozaiAPI.Setup();
|
||||||
|
await _voicevoxAPI.Setup("http://localhost:50021");
|
||||||
|
await _client.LoginAsync(TokenType.Bot, configuration.AppSettings.DiscordSettings.Token);
|
||||||
|
await _client.StartAsync();
|
||||||
|
|
||||||
|
|
||||||
|
await Task.Delay(-1); //keep the program running
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task Log(LogMessage msg)
|
||||||
|
{
|
||||||
|
Console.WriteLine(msg.ToString());
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
14
Elementary/appsettings.json
Normal file
14
Elementary/appsettings.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AppSettings": {
|
||||||
|
"DiscordSettings": {
|
||||||
|
"Token": "MTEzMDAzMjkxMTQzMzg1OTE5Mw.G8yVqt.HrC65f0t4dvZQIZ7iER4CV70pLvhAl8PIyknSs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Elementary/bass.dll
Normal file
BIN
Elementary/bass.dll
Normal file
Binary file not shown.
BIN
Elementary/bassmix.dll
Normal file
BIN
Elementary/bassmix.dll
Normal file
Binary file not shown.
BIN
Elementary/libsodium.dll
Normal file
BIN
Elementary/libsodium.dll
Normal file
Binary file not shown.
BIN
Elementary/opus.dll
Normal file
BIN
Elementary/opus.dll
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user