diff --git a/DiscordRPC/RichPresence.cs b/DiscordRPC/RichPresence.cs index bef38bd1..311467ca 100644 --- a/DiscordRPC/RichPresence.cs +++ b/DiscordRPC/RichPresence.cs @@ -30,6 +30,26 @@ public string State /// Inernal inner state string protected internal string _state; + + /// + /// URL that is linked to when clicking on the details text in the activity card. + /// Max 256 characters + /// + [JsonProperty("state_url", NullValueHandling = NullValueHandling.Ignore)] + public string StateUrl + { + get { return _stateUrl; } + set + { + if (!ValidateString(value, out _stateUrl, false, 256)) + throw new StringOutOfRangeException(256); + + if (!ValidateUrl(_stateUrl)) + throw new ArgumentException("Url must be a valid URI"); + } + } + /// Inernal inner state URL string + protected internal string _stateUrl; /// /// What the user is currently doing. For example, "Competitive - Total Mayhem". @@ -47,6 +67,26 @@ public string Details } /// Inernal inner detail string protected internal string _details; + + /// + /// URL that is linked to when clicking on the details text in the activity card. + /// Max 256 characters + /// + [JsonProperty("details_url", NullValueHandling = NullValueHandling.Ignore)] + public string DetailsUrl + { + get { return _detailsUrl; } + set + { + if (!ValidateString(value, out _detailsUrl, false, 256)) + throw new StringOutOfRangeException(256); + + if (!ValidateUrl(_detailsUrl)) + throw new ArgumentException("Url must be a valid URI"); + } + } + /// Inernal inner detail URL string + protected internal string _detailsUrl; /// /// The time elapsed / remaining time data. @@ -163,6 +203,23 @@ internal static bool ValidateString(string str, out string result, bool useBytes result = s.GetNullOrString(); return true; } + + /// + /// Validates URLs. + /// + /// The URL to check + /// True if the URL is valid + internal static bool ValidateUrl(string url) + { + if (string.IsNullOrEmpty(url)) + return true; + + //Check if the URL is valid + if (!Uri.TryCreate(url, UriKind.Absolute, out _)) + return false; + + return true; + } /// /// Operator that converts a presence into a boolean for null checks. @@ -184,7 +241,11 @@ internal virtual bool Matches(RichPresence other) if (other == null) return false; - if (State != other.State || Details != other.Details || Type != other.Type) + if (State != other.State || + StateUrl != other.StateUrl || + Details != other.Details || + DetailsUrl != other.DetailsUrl || + Type != other.Type) return false; //Checks if the timestamps are different @@ -235,8 +296,10 @@ internal virtual bool Matches(RichPresence other) if (other.Assets == null || other.Assets.LargeImageKey != Assets.LargeImageKey || other.Assets.LargeImageText != Assets.LargeImageText || + other.Assets.LargeImageUrl != Assets.LargeImageUrl || other.Assets.SmallImageKey != Assets.SmallImageKey || - other.Assets.SmallImageText != Assets.SmallImageText) + other.Assets.SmallImageText != Assets.SmallImageText || + other.Assets.SmallImageUrl != Assets.SmallImageUrl) return false; } else if (other.Assets != null) @@ -256,7 +319,9 @@ public RichPresence ToRichPresence() { var presence = new RichPresence(); presence.State = State; + presence.StateUrl = StateUrl; presence.Details = Details; + presence.DetailsUrl = DetailsUrl; presence.Type = Type; presence.StatusDisplay = StatusDisplay; @@ -269,9 +334,11 @@ public RichPresence ToRichPresence() { SmallImageKey = Assets.SmallImageKey, SmallImageText = Assets.SmallImageText, + SmallImageUrl = Assets.SmallImageUrl, LargeImageKey = Assets.LargeImageKey, - LargeImageText = Assets.LargeImageText + LargeImageText = Assets.LargeImageText, + LargeImageUrl = Assets.LargeImageUrl }; } @@ -453,7 +520,25 @@ public string LargeImageText } } private string _largeimagetext; - + + /// + /// URL that is linked to when clicking on the large image in the activity card. + /// Max 256 characters. + /// + [JsonProperty("large_url", NullValueHandling = NullValueHandling.Ignore)] + public string LargeImageUrl + { + get { return _largeimageurl; } + set + { + if (!BaseRichPresence.ValidateString(value, out _largeimageurl, false, 256)) + throw new StringOutOfRangeException(256); + + if (!BaseRichPresence.ValidateUrl(_largeimageurl)) + throw new ArgumentException("Url must be a valid URI"); + } + } + private string _largeimageurl; /// /// Name of the uploaded image for the small profile artwork. @@ -503,6 +588,25 @@ public string SmallImageText } } private string _smallimagetext; + + /// + /// URL that is linked to when clicking on the small image in the activity card. + /// Max 256 characters. + /// + [JsonProperty("small_url", NullValueHandling = NullValueHandling.Ignore)] + public string SmallImageUrl + { + get { return _smallimageurl; } + set + { + if (!BaseRichPresence.ValidateString(value, out _smallimageurl, false, 256)) + throw new StringOutOfRangeException(256); + + if (!BaseRichPresence.ValidateUrl(_smallimageurl)) + throw new ArgumentException("Url must be a valid URI"); + } + } + private string _smallimageurl; /// /// The ID of the large image. This is only set after Update Presence and will automatically become null when is changed. @@ -526,7 +630,9 @@ internal void Merge(Assets other) { //Copy over the names _smallimagetext = other._smallimagetext; + _smallimageurl = other._smallimageurl; _largeimagetext = other._largeimagetext; + _largeimageurl = other._largeimageurl; //Convert large ID ulong largeID; @@ -796,7 +902,7 @@ public string Url if (!BaseRichPresence.ValidateString(value, out _url, false, 512)) throw new StringOutOfRangeException(512); - if (!Uri.TryCreate(_url, UriKind.Absolute, out var uriResult)) // || !(uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)) + if (!BaseRichPresence.ValidateUrl(_url)) throw new ArgumentException("Url must be a valid URI"); } } @@ -880,6 +986,17 @@ public RichPresence WithState(string state) State = state; return this; } + + /// + /// Sets the state URL of the Rich Presence. See also . + /// + /// State URL when clicking on the state text. + /// The modified Rich Presence. + public RichPresence WithStateUrl(string stateUrl) + { + StateUrl = stateUrl; + return this; + } /// /// Sets the details of the Rich Presence. See also . @@ -891,6 +1008,17 @@ public RichPresence WithDetails(string details) Details = details; return this; } + + /// + /// Sets the details URL of the Rich Presence. See also . + /// + /// Details URL when clicking on the details text. + /// The modified Rich Presence. + public RichPresence WithDetailsUrl(string detailsUrl) + { + DetailsUrl = detailsUrl; + return this; + } /// /// Sets the type of the Rich Presence. See also . @@ -970,7 +1098,9 @@ public RichPresence Clone() return new RichPresence { State = this._state != null ? _state.Clone() as string : null, + StateUrl = this._stateUrl != null ? _stateUrl.Clone() as string : null, Details = this._details != null ? _details.Clone() as string : null, + DetailsUrl = this._detailsUrl != null ? _detailsUrl.Clone() as string : null, Type = this.Type, StatusDisplay = this.StatusDisplay, @@ -992,8 +1122,10 @@ public RichPresence Clone() { LargeImageKey = this.Assets.LargeImageKey != null ? this.Assets.LargeImageKey.Clone() as string : null, LargeImageText = this.Assets.LargeImageText != null ? this.Assets.LargeImageText.Clone() as string : null, + LargeImageUrl = this.Assets.LargeImageUrl != null ? this.Assets.LargeImageUrl.Clone() as string : null, SmallImageKey = this.Assets.SmallImageKey != null ? this.Assets.SmallImageKey.Clone() as string : null, - SmallImageText = this.Assets.SmallImageText != null ? this.Assets.SmallImageText.Clone() as string : null + SmallImageText = this.Assets.SmallImageText != null ? this.Assets.SmallImageText.Clone() as string : null, + SmallImageUrl = this.Assets.SmallImageUrl != null ? this.Assets.SmallImageUrl.Clone() as string : null, }, Party = !HasParty() ? null : new Party @@ -1014,7 +1146,9 @@ public RichPresence Clone() internal RichPresence Merge(BaseRichPresence presence) { this._state = presence.State; + this._stateUrl = presence.StateUrl; this._details = presence.Details; + this._detailsUrl = presence.DetailsUrl; this.Type = presence.Type; this.StatusDisplay = presence.StatusDisplay; this.Party = presence.Party;