forked from nint8835/iTunesRichPresence
/
DiscordBridge.cs
160 lines (136 loc) · 6.24 KB
/
DiscordBridge.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Threading;
using iTunesLib;
using iTunesRichPresence_Rewrite.Properties;
using iTunesRichPresence_Rewrite.Tokens;
using SharpRaven.Data;
namespace iTunesRichPresence_Rewrite {
/// <summary>
/// Simplifies interactions between iTunes and Discord
/// </summary>
internal class DiscordBridge {
public readonly List<IToken> Tokens;
private string _currentArtist;
private string _currentTitle;
private ITPlayerState _currentState;
private int _currentPosition;
private readonly DispatcherTimer _timer;
private IiTunes _iTunes;
/// <summary>
/// Initializes the bridge and connects it to DiscordRPC
/// </summary>
/// <param name="applicationId">The Discord application ID for rich presence</param>
public DiscordBridge(string applicationId) {
var handlers = new DiscordRpc.EventHandlers();
DiscordRpc.Initialize(applicationId, ref handlers, true, null);
Tokens = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(p => typeof(IToken).IsAssignableFrom(p) && p.IsClass).Select(Activator.CreateInstance).Select(i => (IToken) i).ToList();
_iTunes = new iTunesApp();
_timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(15)};
_timer.Tick += Timer_OnTick;
_timer.Start();
_currentArtist = "";
_currentTitle = "";
_currentState = ITPlayerState.ITPlayerStateStopped;
_currentPosition = 0;
}
/// <summary>
/// Renders a string template using information about the currently playing song
/// </summary>
/// <param name="template">The template to render</param>
/// <returns>The rendered string</returns>
private string RenderString(string template) {
foreach (var token in Tokens) {
try {
template = template.Replace(token.Token, token.GetText(_iTunes));
}
catch (COMException) {
template = template.Replace(token.Token, "ERROR");
}
}
return template;
}
/// <summary>
/// Truncates a string to fit into Discord's 127 byte size limit
/// </summary>
/// <param name="s">String to truncate</param>
/// <returns>Truncated string</returns>
private static string TruncateString(string s) {
var n = Encoding.Unicode.GetByteCount(s);
if (n <= 127) return s;
s = s.Substring(0, 64);
while (Encoding.Unicode.GetByteCount(s + "...") > 127)
s = s.Substring(0, s.Length - 1);
return s + "...";
}
/// <summary>
/// Handles checking for playing status changes and pushing out presence updates
/// </summary>
/// <param name="sender">Sender of this event</param>
/// <param name="e">Args of this event</param>
private void Timer_OnTick(object sender, EventArgs e) {
try {
if (_iTunes == null) {
_iTunes = new iTunesApp();
}
if (_iTunes.CurrentTrack == null) {
DiscordRpc.ClearPresence();
return;
}
}
catch (COMException) {
_iTunes = null;
var newPresence = new DiscordRpc.RichPresence {
largeImageKey = "itunes_logo_big",
details = "Error connecting to iTunes",
state = "Playback information unavailable"
};
DiscordRpc.UpdatePresence(newPresence);
return;
}
if (_currentArtist == _iTunes.CurrentTrack.Artist && _currentTitle == _iTunes.CurrentTrack.Name &&
_currentState == _iTunes.PlayerState && _currentPosition == _iTunes.PlayerPosition) return;
_currentArtist = _iTunes.CurrentTrack.Artist;
_currentTitle = _iTunes.CurrentTrack.Name;
_currentState = _iTunes.PlayerState;
_currentPosition = _iTunes.PlayerPosition;
var presence = new DiscordRpc.RichPresence{largeImageKey = "itunes_logo_big"};
if (_currentState != ITPlayerState.ITPlayerStatePlaying) {
presence.details = TruncateString(RenderString(Settings.Default.PausedTopLine));
presence.state = TruncateString(RenderString(Settings.Default.PausedBottomLine));
}
else {
presence.details = TruncateString(RenderString(Settings.Default.PlayingTopLine));
presence.state = TruncateString(RenderString(Settings.Default.PlayingBottomLine));
if (Settings.Default.DisplayPlaybackDuration) {
presence.startTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds() - _currentPosition;
presence.endTimestamp = DateTimeOffset.Now.ToUnixTimeSeconds() +
(_iTunes.CurrentTrack.Duration - _currentPosition);
}
}
try {
DiscordRpc.UpdatePresence(presence);
}
catch (Exception exception) {
exception.Data.Add("CurrentArtist", _currentArtist);
exception.Data.Add("CurrentTitle", _currentTitle);
exception.Data.Add("CurrentState", _currentState.ToString());
exception.Data.Add("CurrentPosition", _currentPosition.ToString());
exception.Data.Add("Details", presence.details);
exception.Data.Add("State", presence.state);
Globals.RavenClient.Capture(new SentryEvent(exception));
Globals.Log($"An unhandled exception has occurred and been reported to Sentry: {exception.Message}");
}
}
/// <summary>
/// Disconnects from DiscordRPC and shuts down the bridge
/// </summary>
public void Shutdown() {
_timer.Stop();
DiscordRpc.Shutdown();
}
}
}