Skip to content

Commit

Permalink
Added AFK detection and debug logging functionality
Browse files Browse the repository at this point in the history
 This commit introduces AFK detection and debug logging features. A new method `WriteDebug` was added in `Logging.cs` for logging debug messages. `AfkModule.cs` now includes `AfkModuleSettings` and `AfkModule` classes for managing AFK detection settings and status. Unused namespace `System.Diagnostics` was removed from `WhisperModule.cs`. `MainWindow.xaml` and `MainWindow.xaml.cs` were updated to bind to the `IsAfk` property and handle `AfkDetected` event respectively. Lastly, `ViewModel.cs` now has a new property `AfkModule` for accessing the `AfkModule` instance.
  • Loading branch information
BoiHanny committed Apr 14, 2024
1 parent 35dd4e0 commit caab36d
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 2 deletions.
9 changes: 9 additions & 0 deletions vrcosc-magicchatbox/Classes/DataAndSecurity/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,14 @@ public static void WriteInfo(string info, bool MSGBox = false, bool autoclose =
if(exitapp)
System.Environment.Exit(10);
}

public static void WriteDebug(string debug, bool MSGBox = false, bool autoclose = false, bool exitapp = false)
{
LogController.Debug(debug);
if(MSGBox)
ShowMSGBox(msgboxtext: debug, autoClose: autoclose);
if(exitapp)
ShowMSGBox(msgboxtext: "debug did throw application exit", autoClose: autoclose);
}
}
}
179 changes: 179 additions & 0 deletions vrcosc-magicchatbox/Classes/Modules/AfkModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using vrcosc_magicchatbox.Classes.DataAndSecurity;
using Newtonsoft.Json;

namespace vrcosc_magicchatbox.Classes.Modules
{
public partial class AfkModuleSettings : ObservableObject
{
private const string SettingsFileName = "AfkModuleSettings.json";

[ObservableProperty]
private int afkTimeout = 5;

[ObservableProperty]
private bool enableAfkDetection = true;

[ObservableProperty]
private bool detectKeyboardActivity = false;

[ObservableProperty]
private bool detectMouseActivity = true;

[ObservableProperty]
private string targetApplication = "VRChat.exe";

[ObservableProperty]
private bool overrideAfk = false;


public static AfkModuleSettings LoadSettings()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vrcosc-MagicChatbox", SettingsFileName);

if (File.Exists(path))
{
var settingsJson = File.ReadAllText(path);

if (string.IsNullOrWhiteSpace(settingsJson) || settingsJson.All(c => c == '\0'))
{
Logging.WriteInfo("The settings JSON file is empty or corrupted.");
return new AfkModuleSettings();
}

try
{
var settings = JsonConvert.DeserializeObject<AfkModuleSettings>(settingsJson);

if (settings != null)
{
return settings;
}
else
{
Logging.WriteInfo("Failed to deserialize the settings JSON.");
return new AfkModuleSettings();
}
}
catch (JsonException ex)
{
Logging.WriteInfo($"Error parsing settings JSON: {ex.Message}");
return new AfkModuleSettings();
}
}
else
{
Logging.WriteInfo("Settings file does not exist, returning new settings instance.");
return new AfkModuleSettings();
}
}

public void SaveSettings()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vrcosc-MagicChatbox", SettingsFileName);
Directory.CreateDirectory(Path.GetDirectoryName(path)); // Ensure directory exists
var settingsJson = JsonConvert.SerializeObject(this, Formatting.Indented);
File.WriteAllText(path, settingsJson);
}

}

public partial class AfkModule : ObservableObject
{
private DispatcherTimer afkTimer;

public event EventHandler<EventArgs> AfkDetected;

[ObservableProperty]
private AfkModuleSettings settings;

[ObservableProperty]
private bool isAfk;

public AfkModule()
{
Settings = AfkModuleSettings.LoadSettings();
InitializeAfkDetection();
}

private void InitializeAfkDetection()
{
if (Settings.EnableAfkDetection)
{
afkTimer = new DispatcherTimer();
afkTimer.Interval = TimeSpan.FromSeconds(2);
afkTimer.Tick += AfkTimer_Tick;
afkTimer.Start();
}
}

private void AfkTimer_Tick(object sender, EventArgs e)
{
if (Settings.EnableAfkDetection)
{
uint idleTime = GetIdleTime();
if (idleTime >= Settings.AfkTimeout)
{
if (!Settings.OverrideAfk)
{
IsAfk = true;
OnAfkDetected();
}
}
else
{
IsAfk = false;
}
}
}

private uint GetIdleTime()
{
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
GetLastInputInfo(ref lastInputInfo);

return ((uint)Environment.TickCount - lastInputInfo.dwTime) / 1000;
}

private bool IsTargetApplicationFocused()
{
var foregroundWindow = GetForegroundWindow();
if (foregroundWindow != IntPtr.Zero)
{
uint processId;
GetWindowThreadProcessId(foregroundWindow, out processId);
var process = Process.GetProcessById((int)processId);
return process.ProcessName.Equals(settings.TargetApplication, StringComparison.OrdinalIgnoreCase);
}
return false;
}

private void OnAfkDetected()
{
AfkDetected?.Invoke(this, EventArgs.Empty);
}

[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

[StructLayout(LayoutKind.Sequential)]
private struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}
}
1 change: 0 additions & 1 deletion vrcosc-magicchatbox/Classes/Modules/WhisperModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using vrcosc_magicchatbox.ViewModels;
using Newtonsoft.Json;
using System.Diagnostics;
using vrcosc_magicchatbox.Classes.DataAndSecurity;

namespace vrcosc_magicchatbox.Classes.Modules
Expand Down
2 changes: 1 addition & 1 deletion vrcosc-magicchatbox/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5940,7 +5940,7 @@
FontFamily="Albert Sans Thin"
FontSize="12"
Foreground="#FFB3B0B9"
Text="Sent to VRChat" />
Text="{Binding AfkModule.IsAfk,UpdateSourceTrigger=PropertyChanged}"/>
<CheckBox
Name="MasterSwitch"
Width="62"
Expand Down
8 changes: 8 additions & 0 deletions vrcosc-magicchatbox/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ public MainWindow()
ViewModel.Instance.WhisperModule = new WhisperModule();
ViewModel.Instance.WhisperModule.TranscriptionReceived += WhisperModule_TranscriptionReceived;

ViewModel.Instance.AfkModule = new AfkModule();
ViewModel.Instance.AfkModule.AfkDetected += AfkModule_AfkDetected;

}

private void AfkModule_AfkDetected(object? sender, EventArgs e)
{
//throw new NotImplementedException();
}

private void WhisperModule_TranscriptionReceived(string newTranscription)
Expand Down
11 changes: 11 additions & 0 deletions vrcosc-magicchatbox/ViewModels/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ public PulsoidModule HeartRateConnector
}
}

private AfkModule _AfkModule;
public AfkModule AfkModule
{
get { return _AfkModule; }
set
{
_AfkModule = value;
NotifyPropertyChanged(nameof(AfkModule));
}
}


private IntelliChatModule _IntelliChatModule;
public IntelliChatModule IntelliChatModule
Expand Down

0 comments on commit caab36d

Please sign in to comment.