mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
243 lines
7.4 KiB
C#
243 lines
7.4 KiB
C#
using System;
|
|
using DiscordRPC;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using NLog;
|
|
|
|
namespace VRCX
|
|
{
|
|
public class Discord
|
|
{
|
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
|
public static readonly Discord Instance;
|
|
private readonly ReaderWriterLockSlim _lock;
|
|
private readonly RichPresence _presence;
|
|
private DiscordRpcClient _client;
|
|
private readonly Timer _timer;
|
|
private bool _active;
|
|
private string _discordAppId;
|
|
private const string VrcxUrl = "https://vrcx.app";
|
|
|
|
static Discord()
|
|
{
|
|
Instance = new Discord();
|
|
}
|
|
|
|
public Discord()
|
|
{
|
|
_lock = new ReaderWriterLockSlim();
|
|
_presence = new RichPresence();
|
|
_timer = new Timer(TimerCallback, null, -1, -1);
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
_timer.Change(0, 3000);
|
|
}
|
|
|
|
public void Exit()
|
|
{
|
|
lock (this)
|
|
{
|
|
_timer.Change(-1, -1);
|
|
_client?.Dispose();
|
|
}
|
|
}
|
|
|
|
private void TimerCallback(object state)
|
|
{
|
|
lock (this)
|
|
{
|
|
try
|
|
{
|
|
Update();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Error updating Discord Rich Presence {Error}", ex.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_client == null && _active)
|
|
{
|
|
_client = new DiscordRpcClient(_discordAppId);
|
|
_client.OnReady += (sender, e) =>
|
|
{
|
|
_logger.Info("Discord Rich Presence connected: {User}", e.User.DisplayName);
|
|
};
|
|
_client.OnError += (sender, e) =>
|
|
{
|
|
_logger.Error("Discord Rich Presence error: {Error}", e.Message);
|
|
};
|
|
_client.OnConnectionFailed += (sender, e) =>
|
|
{
|
|
_logger.Error("Discord Rich Presence connection failed: {Error}", e.Type);
|
|
};
|
|
_client.OnConnectionEstablished += (sender, e) =>
|
|
{
|
|
_logger.Info("Discord Rich Presence connection established");
|
|
};
|
|
if (!_client.Initialize())
|
|
{
|
|
_client.Dispose();
|
|
_client = null;
|
|
}
|
|
}
|
|
|
|
if (_client != null && !_active)
|
|
{
|
|
_client.Dispose();
|
|
_client = null;
|
|
}
|
|
|
|
if (_client != null && !_lock.IsWriteLockHeld)
|
|
{
|
|
_lock.EnterReadLock();
|
|
try
|
|
{
|
|
_client.SetPresence(_presence);
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitReadLock();
|
|
}
|
|
_client.Invoke();
|
|
}
|
|
}
|
|
|
|
public bool SetActive(bool active)
|
|
{
|
|
_active = active;
|
|
return _active;
|
|
}
|
|
|
|
// https://stackoverflow.com/questions/1225052/best-way-to-shorten-utf8-string-based-on-byte-length
|
|
private static string LimitByteLength(string str, int maxBytesLength)
|
|
{
|
|
if (str == null)
|
|
return string.Empty;
|
|
var bytesArr = Encoding.UTF8.GetBytes(str);
|
|
var bytesToRemove = 0;
|
|
var lastIndexInString = str.Length - 1;
|
|
while (bytesArr.Length - bytesToRemove > maxBytesLength)
|
|
{
|
|
bytesToRemove += Encoding.UTF8.GetByteCount(new char[] { str[lastIndexInString] });
|
|
--lastIndexInString;
|
|
}
|
|
return Encoding.UTF8.GetString(bytesArr, 0, bytesArr.Length - bytesToRemove);
|
|
}
|
|
|
|
public void SetAssets(
|
|
string details,
|
|
string state,
|
|
string detailsUrl,
|
|
|
|
string largeKey,
|
|
string largeText,
|
|
|
|
string smallKey,
|
|
string smallText,
|
|
|
|
double startUnixMilliseconds,
|
|
double endUnixMilliseconds,
|
|
|
|
string partyId,
|
|
int partySize,
|
|
int partyMax,
|
|
string buttonText,
|
|
string buttonUrl,
|
|
string appId,
|
|
int activityType,
|
|
int statusDisplayType)
|
|
{
|
|
_lock.EnterWriteLock();
|
|
try
|
|
{
|
|
if (string.IsNullOrEmpty(largeKey) &&
|
|
string.IsNullOrEmpty(smallKey))
|
|
{
|
|
_presence.Assets = null;
|
|
_presence.Party = null;
|
|
_presence.Timestamps = null;
|
|
_lock.ExitWriteLock();
|
|
return;
|
|
}
|
|
|
|
_presence.Details = LimitByteLength(details, 127);
|
|
_presence.DetailsUrl = !string.IsNullOrEmpty(detailsUrl) ? detailsUrl : null;
|
|
// _presence.StateUrl
|
|
_presence.State = LimitByteLength(state, 127);
|
|
_presence.Assets ??= new Assets();
|
|
|
|
_presence.Assets.LargeImageKey = largeKey;
|
|
_presence.Assets.LargeImageText = largeText;
|
|
_presence.Assets.LargeImageUrl = VrcxUrl;
|
|
|
|
_presence.Assets.SmallImageKey = smallKey;
|
|
_presence.Assets.SmallImageText = smallText;
|
|
// m_Presence.Assets.SmallImageUrl
|
|
|
|
if (startUnixMilliseconds == 0)
|
|
{
|
|
_presence.Timestamps = null;
|
|
}
|
|
else
|
|
{
|
|
_presence.Timestamps ??= new Timestamps();
|
|
_presence.Timestamps.StartUnixMilliseconds = (ulong)startUnixMilliseconds;
|
|
if (endUnixMilliseconds == 0)
|
|
_presence.Timestamps.End = null;
|
|
else
|
|
_presence.Timestamps.EndUnixMilliseconds = (ulong)endUnixMilliseconds;
|
|
}
|
|
|
|
if (partyMax == 0)
|
|
{
|
|
_presence.Party = null;
|
|
}
|
|
else
|
|
{
|
|
_presence.Party ??= new Party();
|
|
_presence.Party.ID = partyId;
|
|
_presence.Party.Size = partySize;
|
|
_presence.Party.Max = partyMax;
|
|
}
|
|
|
|
_presence.Type = (ActivityType)activityType;
|
|
_presence.StatusDisplay = (StatusDisplayType)statusDisplayType;
|
|
|
|
Button[] buttons = [];
|
|
if (!string.IsNullOrEmpty(buttonUrl))
|
|
{
|
|
buttons =
|
|
[
|
|
new Button { Label = buttonText, Url = buttonUrl }
|
|
];
|
|
}
|
|
_presence.Buttons = buttons;
|
|
|
|
if (_discordAppId != appId)
|
|
{
|
|
_discordAppId = appId;
|
|
if (_client != null)
|
|
{
|
|
_client.Dispose();
|
|
_client = null;
|
|
}
|
|
Update();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex, "Error setting Discord Rich Presence assets: {Error}", ex.Message);
|
|
}
|
|
finally
|
|
{
|
|
_lock.ExitWriteLock();
|
|
}
|
|
}
|
|
}
|
|
} |