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(); 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 _selectedApps = new ObservableCollection(); 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(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 } }