315 lines
13 KiB
C#
315 lines
13 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
|
|
namespace AudioBotProject
|
|
{
|
|
public class AppConfig
|
|
{
|
|
public string BotToken { get; set; } = "";
|
|
public string[] SelectedApps { get; set; } = Array.Empty<string>();
|
|
public ulong LastServerId { get; set; } = 0; // 글자 대신 고유 ID로 저장!
|
|
public ulong LastChannelId { get; set; } = 0;
|
|
}
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
// 💡 API 통신 매니저 생성
|
|
private DiscordBotManager _botManager = new DiscordBotManager();
|
|
|
|
private bool _isBotRunning = false;
|
|
private bool _isInChannel = false;
|
|
private bool _isRoutingStarted = false;
|
|
|
|
private ObservableCollection<string> _selectedApps = new ObservableCollection<string>();
|
|
private readonly string _configFilePath = "config.json";
|
|
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
SelectedAppsListBox.ItemsSource = _selectedApps;
|
|
|
|
// 💡 디스코드 매니저에서 오는 신호(로그, 준비완료)를 화면(UI)에 연결
|
|
// (주의: 디스코드 신호는 백그라운드 스레드에서 오므로 Dispatcher.Invoke를 써야 화면 수정이 가능합니다.)
|
|
_botManager.OnLog += msg => Dispatcher.Invoke(() => LogToMain(msg));
|
|
_botManager.OnReady += () => Dispatcher.Invoke(BotReadyHandler);
|
|
|
|
LogToMain("프로그램이 시작되었습니다.");
|
|
LoadConfig();
|
|
}
|
|
|
|
// 봇이 완전히 로그인되었을 때 실행될 UI 업데이트
|
|
private void BotReadyHandler()
|
|
{
|
|
BotToggleBtn.Content = "봇 중지";
|
|
BotToggleBtn.IsEnabled = true; // 로그인 중에는 버튼 잠갔다가 완료되면 풀기
|
|
BotResultText.Text = "✅ 봇이 정상적으로 실행되었습니다.";
|
|
CurrentBotNameText.Text = $"현재 실행중인 봇: {_botManager.GetBotName()}";
|
|
NavChannelSettingsBtn.IsEnabled = true;
|
|
}
|
|
|
|
#region [ 설정 저장 & 불러오기 로직 ]
|
|
private void SaveConfig()
|
|
{
|
|
var config = new AppConfig
|
|
{
|
|
BotToken = TokenTextBox.Text,
|
|
SelectedApps = _selectedApps.ToArray(),
|
|
// 콤보박스에서 선택된 서버/채널의 고유 ID 값을 저장합니다.
|
|
LastServerId = ServerComboBox.SelectedValue is ulong sId ? sId : 0,
|
|
LastChannelId = ChannelComboBox.SelectedValue is ulong cId ? cId : 0
|
|
};
|
|
|
|
var options = new JsonSerializerOptions { WriteIndented = true };
|
|
string jsonString = JsonSerializer.Serialize(config, options);
|
|
File.WriteAllText(_configFilePath, jsonString);
|
|
}
|
|
|
|
private void LoadConfig()
|
|
{
|
|
if (File.Exists(_configFilePath))
|
|
{
|
|
try
|
|
{
|
|
string jsonString = File.ReadAllText(_configFilePath);
|
|
var config = JsonSerializer.Deserialize<AppConfig>(jsonString);
|
|
|
|
if (config != null)
|
|
{
|
|
TokenTextBox.Text = config.BotToken;
|
|
_selectedApps.Clear();
|
|
foreach (var app in config.SelectedApps) _selectedApps.Add(app);
|
|
LogToMain("✅ 이전 설정을 성공적으로 불러왔습니다.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogToMain($"❌ 설정 불러오기 실패: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region [ 화면 전환 로직 (Navigation) ]
|
|
private void HideAllViews()
|
|
{
|
|
MainView.Visibility = Visibility.Collapsed;
|
|
BotSettingsView.Visibility = Visibility.Collapsed;
|
|
ChannelSettingsView.Visibility = Visibility.Collapsed;
|
|
OutputSettingsView.Visibility = Visibility.Collapsed;
|
|
}
|
|
private void Nav_BackToMain_Click(object sender, RoutedEventArgs e) { HideAllViews(); MainView.Visibility = Visibility.Visible; }
|
|
private void Nav_BotSettings_Click(object sender, RoutedEventArgs e) { HideAllViews(); BotSettingsView.Visibility = Visibility.Visible; }
|
|
private void Nav_ChannelSettings_Click(object sender, RoutedEventArgs e) { HideAllViews(); ChannelSettingsView.Visibility = Visibility.Visible; }
|
|
private void Nav_OutputSettings_Click(object sender, RoutedEventArgs e) { HideAllViews(); OutputSettingsView.Visibility = Visibility.Visible; }
|
|
#endregion
|
|
|
|
#region [ 봇 설정 기능 (비동기 연동) ]
|
|
private async void BotToggleBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (!_isBotRunning)
|
|
{
|
|
string token = TokenTextBox.Text;
|
|
if (string.IsNullOrWhiteSpace(token))
|
|
{
|
|
BotResultText.Text = "❌ 토큰을 입력해주세요.";
|
|
return;
|
|
}
|
|
|
|
SaveConfig();
|
|
_isBotRunning = true;
|
|
BotToggleBtn.IsEnabled = false; // 로그인 완료 전까지 연타 방지
|
|
BotResultText.Text = "⏳ 디스코드 서버에 연결 중...";
|
|
|
|
// 💡 API 연결 시작!
|
|
await _botManager.StartBotAsync(token);
|
|
}
|
|
else
|
|
{
|
|
if (_isRoutingStarted)
|
|
{
|
|
_isRoutingStarted = false;
|
|
MainStartRoutingBtn.Content = "🚀 시작";
|
|
MainStartRoutingBtn.Background = (System.Windows.Media.Brush)new System.Windows.Media.BrushConverter().ConvertFrom("#FF5865F2")!;
|
|
}
|
|
|
|
if (_isInChannel)
|
|
{
|
|
_isInChannel = false;
|
|
ChannelToggleBtn.Content = "채널 접속";
|
|
ChannelResultText.Text = "봇이 중지되어 채널에서 나왔습니다.";
|
|
}
|
|
|
|
// 💡 API 연결 종료!
|
|
await _botManager.StopBotAsync();
|
|
|
|
_isBotRunning = false;
|
|
BotToggleBtn.Content = "봇 실행";
|
|
BotResultText.Text = "봇이 안전하게 중지되었습니다.";
|
|
CurrentBotNameText.Text = "현재 실행중인 봇: 없음";
|
|
NavChannelSettingsBtn.IsEnabled = false;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region [ 채널 설정 기능 (실제 서버 데이터 바인딩) ]
|
|
private void ServerComboBox_DropDownOpened(object sender, EventArgs e)
|
|
{
|
|
// 💡 봇 매니저에게 실제 서버 목록을 달라고 요청합니다.
|
|
var servers = _botManager.GetServers()
|
|
.Select(g => new { Id = g.Id, Name = g.Name })
|
|
.ToList();
|
|
|
|
ServerComboBox.ItemsSource = servers;
|
|
ServerComboBox.DisplayMemberPath = "Name"; // 화면에 보여줄 글자
|
|
ServerComboBox.SelectedValuePath = "Id"; // 뒤에 숨겨둘 고유 ID
|
|
}
|
|
|
|
private void ServerComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
ChannelComboBox.IsEnabled = ServerComboBox.SelectedItem != null;
|
|
ChannelComboBox.ItemsSource = null; // 서버가 바뀌면 채널 목록 초기화
|
|
}
|
|
|
|
private void ChannelComboBox_DropDownOpened(object sender, EventArgs e)
|
|
{
|
|
if (ServerComboBox.SelectedValue is ulong guildId)
|
|
{
|
|
// 💡 선택한 서버의 고유 ID를 넘겨주고, 실제 음성 채널 목록을 받아옵니다.
|
|
var channels = _botManager.GetVoiceChannels(guildId)
|
|
.Select(c => new { Id = c.Id, Name = c.Name })
|
|
.ToList();
|
|
|
|
ChannelComboBox.ItemsSource = channels;
|
|
ChannelComboBox.DisplayMemberPath = "Name";
|
|
ChannelComboBox.SelectedValuePath = "Id";
|
|
}
|
|
}
|
|
|
|
private async void ChannelToggleBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (ServerComboBox.SelectedValue == null || ChannelComboBox.SelectedValue == null)
|
|
{
|
|
ChannelResultText.Text = "❌ 서버와 채널을 모두 선택해주세요.";
|
|
return;
|
|
}
|
|
|
|
// 숨겨둔 고유 ID 추출
|
|
ulong channelId = (ulong)ChannelComboBox.SelectedValue;
|
|
|
|
if (!_isInChannel)
|
|
{
|
|
ChannelToggleBtn.IsEnabled = false;
|
|
ChannelResultText.Text = "⏳ 접속 중...";
|
|
|
|
// 💡 음성 채널 접속 API 호출
|
|
await _botManager.JoinChannelAsync(channelId);
|
|
|
|
_isInChannel = true;
|
|
ChannelToggleBtn.IsEnabled = true;
|
|
ChannelToggleBtn.Content = "채널 나가기";
|
|
ChannelResultText.Text = "✅ 음성 채널에 정상적으로 접속했습니다.";
|
|
|
|
SaveConfig(); // 접속 성공 시 저장
|
|
}
|
|
else
|
|
{
|
|
ChannelToggleBtn.IsEnabled = false;
|
|
|
|
// 💡 음성 채널 퇴장 API 호출
|
|
await _botManager.LeaveChannelAsync();
|
|
|
|
_isInChannel = false;
|
|
ChannelToggleBtn.IsEnabled = true;
|
|
ChannelToggleBtn.Content = "채널 접속";
|
|
ChannelResultText.Text = "채널에서 나왔습니다.";
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region [ 출력 설정 기능 ]
|
|
private void ProcessComboBox_DropDownOpened(object sender, EventArgs e)
|
|
{
|
|
ProcessComboBox.Items.Clear();
|
|
var processes = Process.GetProcesses()
|
|
.Where(p => p.MainWindowHandle != IntPtr.Zero && !string.IsNullOrEmpty(p.MainWindowTitle))
|
|
.Select(p => p.ProcessName)
|
|
.Distinct()
|
|
.OrderBy(n => n);
|
|
foreach (var pName in processes) ProcessComboBox.Items.Add(pName);
|
|
}
|
|
|
|
private void AddOutputAppBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (ProcessComboBox.SelectedItem != null)
|
|
{
|
|
string? appName = ProcessComboBox.SelectedItem.ToString();
|
|
if (!string.IsNullOrEmpty(appName) && !_selectedApps.Contains(appName))
|
|
_selectedApps.Add(appName);
|
|
}
|
|
}
|
|
|
|
private void RemoveAppBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
Button? btn = sender as Button;
|
|
if (btn != null && btn.Tag != null)
|
|
{
|
|
string? appName = btn.Tag.ToString();
|
|
if (!string.IsNullOrEmpty(appName)) _selectedApps.Remove(appName);
|
|
}
|
|
}
|
|
|
|
private void SaveOutputSettingsBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
SaveConfig();
|
|
LogToMain($"출력 설정이 저장되었습니다. (대상 앱: {_selectedApps.Count}개)");
|
|
MessageBox.Show("설정이 성공적으로 저장되었습니다.", "알림", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
}
|
|
#endregion
|
|
|
|
#region [ 메인 화면 라우팅 시작/종료 ]
|
|
private void MainStartRoutingBtn_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
if (!_isBotRunning) { MessageBox.Show("먼저 봇을 실행해주세요!", "경고", MessageBoxButton.OK, MessageBoxImage.Warning); return; }
|
|
if (!_isInChannel) { MessageBox.Show("먼저 채널에 접속해주세요!", "경고", MessageBoxButton.OK, MessageBoxImage.Warning); return; }
|
|
|
|
if (!_isRoutingStarted)
|
|
{
|
|
_isRoutingStarted = true;
|
|
MainStartRoutingBtn.Content = "⏹️ 종료";
|
|
MainStartRoutingBtn.Background = System.Windows.Media.Brushes.DarkRed;
|
|
LogToMain("오디오 라우팅을 시작했습니다!");
|
|
}
|
|
else
|
|
{
|
|
_isRoutingStarted = false;
|
|
MainStartRoutingBtn.Content = "🚀 시작";
|
|
MainStartRoutingBtn.Background = (System.Windows.Media.Brush)new System.Windows.Media.BrushConverter().ConvertFrom("#FF5865F2")!;
|
|
LogToMain("오디오 라우팅을 종료했습니다.");
|
|
}
|
|
}
|
|
|
|
private void LogToMain(string message)
|
|
{
|
|
MainLogBox.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\n");
|
|
MainLogBox.ScrollToEnd();
|
|
}
|
|
#endregion
|
|
|
|
#region [ 프로그램 종료 시 완벽한 정리 ]
|
|
private async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
|
{
|
|
// 프로그램 종료 시 봇도 확실하게 로그아웃 시키기
|
|
if (_isBotRunning)
|
|
{
|
|
await _botManager.StopBotAsync();
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
} |