@@ -1,5 +1,4 @@ | |||
using Discord.Commands.Permissions; | |||
using System; | |||
using System; | |||
namespace Discord.Commands | |||
{ | |||
@@ -40,7 +39,7 @@ namespace Discord.Commands | |||
protected void SetValue<T>(ref T storage, T value) | |||
{ | |||
if (_isLocked) | |||
throw new InvalidOperationException("Unable to modify a discord client's configuration after it has been created."); | |||
throw new InvalidOperationException("Unable to modify a service's configuration after it has been created."); | |||
storage = value; | |||
} | |||
} | |||
@@ -18,7 +18,5 @@ | |||
"frameworks": { | |||
"net45": { }, | |||
"dotnet5.4": { } | |||
}, | |||
"configurations": { | |||
} | |||
} |
@@ -22,6 +22,7 @@ | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
<LangVersion>6</LangVersion> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<DebugType>pdbonly</DebugType> | |||
@@ -32,6 +33,7 @@ | |||
<WarningLevel>4</WarningLevel> | |||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | |||
<LangVersion>6</LangVersion> | |||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="System" /> | |||
@@ -35,7 +35,6 @@ namespace Discord.Modules | |||
public event EventHandler<UserEventArgs> UserPresenceUpdated; | |||
public event EventHandler<UserEventArgs> UserVoiceStateUpdated; | |||
public event EventHandler<UserChannelEventArgs> UserIsTypingUpdated; | |||
public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated; | |||
public event EventHandler<MessageEventArgs> MessageReceived; | |||
public event EventHandler<MessageEventArgs> MessageSent; | |||
@@ -77,9 +76,7 @@ namespace Discord.Modules | |||
if (_allowAll || _useServerWhitelist) //Server-only events | |||
{ | |||
client.ChannelCreated += (s, e) => { if (ChannelCreated != null && HasServer(e.Server)) ChannelCreated(s, e); }; | |||
client.UserVoiceStateUpdated += (s, e) => { if (UserVoiceStateUpdated != null && HasServer(e.Server)) UserVoiceStateUpdated(s, e); }; | |||
client.UserIsSpeakingUpdated += (s, e) => { if (UserIsSpeakingUpdated != null && HasServer(e.Server)) UserIsSpeakingUpdated(s, e); }; | |||
} | |||
client.ChannelDestroyed += (s, e) => { if (ChannelDestroyed != null && HasChannel(e.Channel)) ChannelDestroyed(s, e); }; | |||
@@ -103,7 +100,7 @@ namespace Discord.Modules | |||
client.UserJoined += (s, e) => { if (UserJoined != null && HasIndirectServer(e.Server)) UserJoined(s, e); }; | |||
client.UserLeft += (s, e) => { if (UserLeft != null && HasIndirectServer(e.Server)) UserLeft(s, e); }; | |||
client.UserUpdated += (s, e) => { if (UserUpdated != null && HasIndirectServer(e.Server)) UserUpdated(s, e); }; | |||
client.UserIsTypingUpdated += (s, e) => { if (UserIsSpeakingUpdated != null && HasChannel(e.Channel)) UserIsTypingUpdated(s, e); }; | |||
client.UserIsTypingUpdated += (s, e) => { if (UserIsTypingUpdated != null && HasChannel(e.Channel)) UserIsTypingUpdated(s, e); }; | |||
//TODO: We aren't getting events from UserPresence if AllowPrivate is enabled, but the server we know that user through isn't on the whitelist | |||
client.UserPresenceUpdated += (s, e) => { if (UserPresenceUpdated != null && HasIndirectServer(e.Server)) UserPresenceUpdated(s, e); }; | |||
client.UserBanned += (s, e) => { if (UserBanned != null && HasIndirectServer(e.Server)) UserBanned(s, e); }; | |||
@@ -19,7 +19,5 @@ | |||
"frameworks": { | |||
"net45": { }, | |||
"dotnet5.4": { } | |||
}, | |||
"configurations": { | |||
} | |||
} |
@@ -134,27 +134,6 @@ | |||
<Compile Include="..\Discord.Net\API\WebSockets.cs"> | |||
<Link>API\WebSockets.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\IDiscordVoiceBuffer.cs"> | |||
<Link>Audio\IDiscordVoiceBuffer.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\IDiscordVoiceClient.cs"> | |||
<Link>Audio\IDiscordVoiceClient.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\Opus.cs"> | |||
<Link>Audio\Opus.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\OpusDecoder.cs"> | |||
<Link>Audio\OpusDecoder.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\OpusEncoder.cs"> | |||
<Link>Audio\OpusEncoder.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\Sodium.cs"> | |||
<Link>Audio\Sodium.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Audio\VoiceBuffer.cs"> | |||
<Link>Audio\VoiceBuffer.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordAPIClient.cs"> | |||
<Link>DiscordAPIClient.cs</Link> | |||
</Compile> | |||
@@ -185,9 +164,6 @@ | |||
<Compile Include="..\Discord.Net\DiscordClient.Users.cs"> | |||
<Link>DiscordClient.Users.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordClient.Voice.cs"> | |||
<Link>DiscordClient.Voice.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordClientConfig.cs"> | |||
<Link>DiscordClientConfig.cs</Link> | |||
</Compile> | |||
@@ -197,9 +173,6 @@ | |||
<Compile Include="..\Discord.Net\DiscordWSClient.Events.cs"> | |||
<Link>DiscordWSClient.Events.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordWSClient.Voice.cs"> | |||
<Link>DiscordWSClient.Voice.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\DiscordWSClientConfig.cs"> | |||
<Link>DiscordWSClientConfig.cs</Link> | |||
</Compile> | |||
@@ -290,12 +263,6 @@ | |||
<Compile Include="..\Discord.Net\Net\WebSockets\IWebSocketEngine.cs"> | |||
<Link>Net\WebSockets\IWebSocketEngine.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Net\WebSockets\VoiceWebSocket.cs"> | |||
<Link>Net\WebSockets\VoiceWebSocket.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Net\WebSockets\VoiceWebSocket.Events.cs"> | |||
<Link>Net\WebSockets\VoiceWebSocket.Events.cs</Link> | |||
</Compile> | |||
<Compile Include="..\Discord.Net\Net\WebSockets\WebSocket.cs"> | |||
<Link>Net\WebSockets\WebSocket.cs</Link> | |||
</Compile> | |||
@@ -1,18 +0,0 @@ | |||
using System.Threading.Tasks; | |||
namespace Discord.Audio | |||
{ | |||
public interface IDiscordVoiceClient | |||
{ | |||
long? ChannelId { get; } | |||
long? ServerId { get; } | |||
IDiscordVoiceBuffer OutputBuffer { get; } | |||
Task JoinChannel(long channelId); | |||
void SendVoicePCM(byte[] data, int count); | |||
void ClearVoicePCM(); | |||
Task WaitVoice(); | |||
} | |||
} |
@@ -105,12 +105,6 @@ namespace Discord | |||
if (UserIsTypingUpdated != null) | |||
RaiseEvent(nameof(UserIsTypingUpdated), () => UserIsTypingUpdated(this, new UserChannelEventArgs(user, channel))); | |||
} | |||
public event EventHandler<UserIsSpeakingEventArgs> UserIsSpeakingUpdated; | |||
private void RaiseUserIsSpeaking(User user, Channel channel, bool isSpeaking) | |||
{ | |||
if (UserIsSpeakingUpdated != null) | |||
RaiseEvent(nameof(UserIsSpeakingUpdated), () => UserIsSpeakingUpdated(this, new UserIsSpeakingEventArgs(user, channel, isSpeaking))); | |||
} | |||
public event EventHandler ProfileUpdated; | |||
private void RaiseProfileUpdated() | |||
{ | |||
@@ -278,7 +272,7 @@ namespace Discord | |||
{ | |||
if (server == null) throw new ArgumentNullException(nameof(server)); | |||
_dataSocket.SendGetUsers(server.Id); | |||
_dataSocket.SendRequestUsers(server.Id); | |||
} | |||
public async Task EditProfile(string currentPassword = "", | |||
@@ -19,10 +19,8 @@ namespace Discord | |||
private readonly Random _rand; | |||
private readonly JsonSerializer _messageImporter; | |||
private readonly ConcurrentQueue<Message> _pendingMessages; | |||
private readonly ConcurrentDictionary<long, DiscordWSClient> _voiceClients; | |||
private readonly Dictionary<Type, object> _singletons; | |||
private bool _sentInitialLog; | |||
private uint _nextVoiceClientId; | |||
private UserStatus _status; | |||
private int? _gameId; | |||
@@ -40,8 +38,6 @@ namespace Discord | |||
_api = new DiscordAPIClient(_config); | |||
if (Config.UseMessageQueue) | |||
_pendingMessages = new ConcurrentQueue<Message>(); | |||
if (Config.EnableVoiceMultiserver) | |||
_voiceClients = new ConcurrentDictionary<long, DiscordWSClient>(); | |||
object cacheLock = new object(); | |||
_channels = new Channels(this, cacheLock); | |||
@@ -59,22 +55,6 @@ namespace Discord | |||
_api.CancelToken = _cancelToken; | |||
await SendStatus().ConfigureAwait(false); | |||
}; | |||
VoiceDisconnected += (s, e) => | |||
{ | |||
var server = _servers[e.ServerId]; | |||
if (server != null) | |||
{ | |||
foreach (var member in server.Members) | |||
{ | |||
if (member.IsSpeaking) | |||
{ | |||
member.IsSpeaking = false; | |||
RaiseUserIsSpeaking(member, _channels[_voiceSocket.CurrentChannelId], false); | |||
} | |||
} | |||
} | |||
}; | |||
if (_config.LogLevel >= LogMessageSeverity.Info) | |||
{ | |||
@@ -169,27 +149,6 @@ namespace Discord | |||
_messageImporter = new JsonSerializer(); | |||
_messageImporter.ContractResolver = new Message.ImportResolver(); | |||
} | |||
internal override VoiceWebSocket CreateVoiceSocket() | |||
{ | |||
var socket = base.CreateVoiceSocket(); | |||
socket.IsSpeaking += (s, e) => | |||
{ | |||
if (_voiceSocket.State == WebSocketState.Connected) | |||
{ | |||
var user = _users[e.UserId, socket.CurrentServerId]; | |||
bool value = e.IsSpeaking; | |||
if (user.IsSpeaking != value) | |||
{ | |||
user.IsSpeaking = value; | |||
var channel = _channels[_voiceSocket.CurrentChannelId]; | |||
RaiseUserIsSpeaking(user, channel, value); | |||
if (Config.TrackActivity) | |||
user.UpdateActivity(); | |||
} | |||
} | |||
}; | |||
return socket; | |||
} | |||
/// <summary> Connects to the Discord server with the provided email and password. </summary> | |||
/// <returns> Returns a token for future connections. </returns> | |||
@@ -242,18 +201,6 @@ namespace Discord | |||
{ | |||
await base.Cleanup().ConfigureAwait(false); | |||
if (_config.VoiceMode != DiscordVoiceMode.Disabled) | |||
{ | |||
if (Config.EnableVoiceMultiserver) | |||
{ | |||
var tasks = _voiceClients | |||
.Select(x => x.Value.Disconnect()) | |||
.ToArray(); | |||
_voiceClients.Clear(); | |||
await Task.WhenAll(tasks).ConfigureAwait(false); | |||
} | |||
} | |||
if (Config.UseMessageQueue) | |||
{ | |||
Message ignored; | |||
@@ -309,7 +256,7 @@ namespace Discord | |||
return base.GetTasks(); | |||
} | |||
internal override async Task OnReceivedEvent(WebSocketEventEventArgs e) | |||
protected override async Task OnReceivedEvent(WebSocketEventEventArgs e) | |||
{ | |||
try | |||
{ | |||
@@ -623,12 +570,12 @@ namespace Discord | |||
var user = _users[data.UserId, data.GuildId]; | |||
if (user != null) | |||
{ | |||
var voiceChannel = user.VoiceChannel; | |||
/*var voiceChannel = user.VoiceChannel; | |||
if (voiceChannel != null && data.ChannelId != voiceChannel.Id && user.IsSpeaking) | |||
{ | |||
user.IsSpeaking = false; | |||
RaiseUserIsSpeaking(user, _channels[voiceChannel.Id], false); | |||
} | |||
}*/ | |||
user.Update(data); | |||
RaiseUserVoiceStateUpdated(user); | |||
} | |||
@@ -10,9 +10,6 @@ | |||
private int _messageCacheLength = 100; | |||
//Experimental Features | |||
/// <summary> (Experimental) Enables the client to be simultaneously connected to multiple channels at once (Discord still limits you to one channel per server). </summary> | |||
public bool EnableVoiceMultiserver { get { return _enableVoiceMultiserver; } set { SetValue(ref _enableVoiceMultiserver, value); } } | |||
private bool _enableVoiceMultiserver = false; | |||
/// <summary> (Experimental) Enables or disables the internal message queue. This will allow SendMessage to return immediately and handle messages internally. Messages will set the IsQueued and HasFailed properties to show their progress. </summary> | |||
public bool UseMessageQueue { get { return _useMessageQueue; } set { SetValue(ref _useMessageQueue, value); } } | |||
private bool _useMessageQueue = false; | |||
@@ -23,9 +20,6 @@ | |||
public bool AckMessages { get { return _ackMessages; } set { SetValue(ref _ackMessages, value); } } | |||
private bool _ackMessages = false; | |||
//Internal | |||
internal override bool EnableVoice => (base.EnableVoice && !EnableVoiceMultiserver) || base.VoiceOnly; | |||
public new DiscordClientConfig Clone() | |||
{ | |||
var config = MemberwiseClone() as DiscordClientConfig; | |||
@@ -26,22 +26,12 @@ namespace Discord | |||
public readonly bool WasUnexpected; | |||
public readonly Exception Error; | |||
internal DisconnectedEventArgs(bool wasUnexpected, Exception error) | |||
public DisconnectedEventArgs(bool wasUnexpected, Exception error) | |||
{ | |||
WasUnexpected = wasUnexpected; | |||
Error = error; | |||
} | |||
} | |||
public class VoiceDisconnectedEventArgs : DisconnectedEventArgs | |||
{ | |||
public readonly long ServerId; | |||
internal VoiceDisconnectedEventArgs(long serverId, DisconnectedEventArgs e) | |||
: base(e.WasUnexpected, e.Error) | |||
{ | |||
ServerId = serverId; | |||
} | |||
} | |||
public sealed class LogMessageEventArgs : EventArgs | |||
{ | |||
public LogMessageSeverity Severity { get; } | |||
@@ -49,7 +39,7 @@ namespace Discord | |||
public string Message { get; } | |||
public Exception Exception { get; } | |||
internal LogMessageEventArgs(LogMessageSeverity severity, LogMessageSource source, string msg, Exception exception) | |||
public LogMessageEventArgs(LogMessageSeverity severity, LogMessageSource source, string msg, Exception exception) | |||
{ | |||
Severity = severity; | |||
Source = source; | |||
@@ -66,7 +56,7 @@ namespace Discord | |||
public int Offset { get; } | |||
public int Count { get; } | |||
internal VoicePacketEventArgs(long userId, long channelId, byte[] buffer, int offset, int count) | |||
public VoicePacketEventArgs(long userId, long channelId, byte[] buffer, int offset, int count) | |||
{ | |||
UserId = userId; | |||
Buffer = buffer; | |||
@@ -90,30 +80,10 @@ namespace Discord | |||
RaiseEvent(nameof(Disconnected), () => Disconnected(this, e)); | |||
} | |||
public event EventHandler<LogMessageEventArgs> LogMessage; | |||
internal void RaiseOnLog(LogMessageSeverity severity, LogMessageSource source, string message, Exception exception = null) | |||
protected void RaiseOnLog(LogMessageSeverity severity, LogMessageSource source, string message, Exception exception = null) | |||
{ | |||
if (LogMessage != null) | |||
RaiseEvent(nameof(LogMessage), () => LogMessage(this, new LogMessageEventArgs(severity, source, message, exception))); | |||
} | |||
public event EventHandler VoiceConnected; | |||
private void RaiseVoiceConnected() | |||
{ | |||
if (VoiceConnected != null) | |||
RaiseEvent(nameof(VoiceConnected), () => VoiceConnected(this, EventArgs.Empty)); | |||
} | |||
public event EventHandler<VoiceDisconnectedEventArgs> VoiceDisconnected; | |||
private void RaiseVoiceDisconnected(long serverId, DisconnectedEventArgs e) | |||
{ | |||
if (VoiceDisconnected != null) | |||
RaiseEvent(nameof(VoiceDisconnected), () => VoiceDisconnected(this, new VoiceDisconnectedEventArgs(serverId, e))); | |||
} | |||
public event EventHandler<VoicePacketEventArgs> OnVoicePacket; | |||
internal void RaiseOnVoicePacket(VoicePacketEventArgs e) | |||
{ | |||
if (OnVoicePacket != null) | |||
OnVoicePacket(this, e); | |||
} | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
using Discord.Audio; | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public partial class DiscordWSClient : IDiscordVoiceClient | |||
{ | |||
IDiscordVoiceBuffer IDiscordVoiceClient.OutputBuffer => _voiceSocket.OutputBuffer; | |||
long? IDiscordVoiceClient.ServerId => _voiceSocket.CurrentServerId; | |||
long? IDiscordVoiceClient.ChannelId => _voiceSocket.CurrentChannelId; | |||
async Task IDiscordVoiceClient.JoinChannel(long channelId) | |||
{ | |||
CheckReady(checkVoice: true); | |||
if (channelId <= 0) throw new ArgumentOutOfRangeException(nameof(channelId)); | |||
await _voiceSocket.Disconnect().ConfigureAwait(false); | |||
await _voiceSocket.SetChannel(_voiceServerId.Value, channelId).ConfigureAwait(false); | |||
_dataSocket.SendJoinVoice(_voiceServerId.Value, channelId); | |||
await _voiceSocket.WaitForConnection(_config.ConnectionTimeout).ConfigureAwait(false); | |||
} | |||
/// <summary> Sends a PCM frame to the voice server. Will block until space frees up in the outgoing buffer. </summary> | |||
/// <param name="data">PCM frame to send. This must be a single or collection of uncompressed 48Kz monochannel 20ms PCM frames. </param> | |||
/// <param name="count">Number of bytes in this frame. </param> | |||
void IDiscordVoiceClient.SendVoicePCM(byte[] data, int count) | |||
{ | |||
if (data == null) throw new ArgumentException(nameof(data)); | |||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); | |||
CheckReady(checkVoice: true); | |||
if (count != 0) | |||
_voiceSocket.SendPCMFrames(data, count); | |||
} | |||
/// <summary> Clears the PCM buffer. </summary> | |||
void IDiscordVoiceClient.ClearVoicePCM() | |||
{ | |||
CheckReady(checkVoice: true); | |||
_voiceSocket.ClearPCMFrames(); | |||
} | |||
/// <summary> Returns a task that completes once the voice output buffer is empty. </summary> | |||
async Task IDiscordVoiceClient.WaitVoice() | |||
{ | |||
CheckReady(checkVoice: true); | |||
_voiceSocket.WaitForQueue(); | |||
await TaskHelper.CompletedTask.ConfigureAwait(false); | |||
} | |||
} | |||
} |
@@ -24,10 +24,9 @@ namespace Discord | |||
protected readonly ManualResetEvent _disconnectedEvent; | |||
protected readonly ManualResetEventSlim _connectedEvent; | |||
protected ExceptionDispatchInfo _disconnectReason; | |||
internal readonly DataWebSocket _dataSocket; | |||
internal readonly VoiceWebSocket _voiceSocket; | |||
protected readonly DataWebSocket _dataSocket; | |||
protected string _gateway, _token; | |||
protected long? _userId, _voiceServerId; | |||
protected long? _userId; | |||
private Task _runTask; | |||
private bool _wasDisconnectUnexpected; | |||
@@ -86,13 +85,6 @@ namespace Discord | |||
#endif | |||
_dataSocket = CreateDataSocket(); | |||
if (_config.EnableVoice) | |||
_voiceSocket = CreateVoiceSocket(); | |||
} | |||
internal DiscordWSClient(DiscordWSClientConfig config = null, long? voiceServerId = null) | |||
: this(config) | |||
{ | |||
_voiceServerId = voiceServerId; | |||
} | |||
internal virtual DataWebSocket CreateDataSocket() | |||
@@ -120,26 +112,6 @@ namespace Discord | |||
socket.ReceivedEvent += async (s, e) => await OnReceivedEvent(e).ConfigureAwait(false); | |||
return socket; | |||
} | |||
internal virtual VoiceWebSocket CreateVoiceSocket() | |||
{ | |||
var socket = new VoiceWebSocket(this); | |||
socket.Connected += (s, e) => RaiseVoiceConnected(); | |||
socket.Disconnected += async (s, e) => | |||
{ | |||
RaiseVoiceDisconnected(socket.CurrentServerId.Value, e); | |||
if (e.WasUnexpected) | |||
await socket.Reconnect().ConfigureAwait(false); | |||
}; | |||
socket.LogMessage += (s, e) => RaiseOnLog(e.Severity, LogMessageSource.VoiceWebSocket, e.Message, e.Exception); | |||
if (_config.LogLevel >= LogMessageSeverity.Info) | |||
{ | |||
socket.Connected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Connected"); | |||
socket.Disconnected += (s, e) => RaiseOnLog(LogMessageSeverity.Info, LogMessageSource.VoiceWebSocket, "Disconnected"); | |||
} | |||
return socket; | |||
} | |||
//Connection | |||
public async Task<string> Connect(string gateway, string token) | |||
@@ -160,8 +132,6 @@ namespace Discord | |||
_dataSocket.Host = gateway; | |||
_dataSocket.ParentCancelToken = _cancelToken; | |||
if (_config.EnableVoice) | |||
_voiceSocket.ParentCancelToken = _cancelToken; | |||
await _dataSocket.Login(token).ConfigureAwait(false); | |||
_runTask = RunTasks(); | |||
@@ -268,13 +238,6 @@ namespace Discord | |||
protected virtual async Task Cleanup() | |||
{ | |||
if (_config.EnableVoice) | |||
{ | |||
var voiceServerId = _voiceSocket.CurrentServerId; | |||
if (voiceServerId != null) | |||
_dataSocket.SendLeaveVoice(voiceServerId.Value); | |||
await _voiceSocket.Disconnect().ConfigureAwait(false); | |||
} | |||
await _dataSocket.Disconnect().ConfigureAwait(false); | |||
_userId = null; | |||
@@ -310,9 +273,6 @@ namespace Discord | |||
case (int)DiscordClientState.Connecting: | |||
throw new InvalidOperationException("The client is connecting."); | |||
} | |||
if (checkVoice && _config.VoiceMode == DiscordVoiceMode.Disabled) | |||
throw new InvalidOperationException("Voice is not enabled for this client."); | |||
} | |||
protected void RaiseEvent(string name, Action action) | |||
{ | |||
@@ -325,7 +285,7 @@ namespace Discord | |||
} | |||
} | |||
internal virtual async Task OnReceivedEvent(WebSocketEventEventArgs e) | |||
protected virtual Task OnReceivedEvent(WebSocketEventEventArgs e) | |||
{ | |||
try | |||
{ | |||
@@ -334,24 +294,13 @@ namespace Discord | |||
case "READY": | |||
_userId = IdConvert.ToLong(e.Payload["user"].Value<string>("id")); | |||
break; | |||
case "VOICE_SERVER_UPDATE": | |||
{ | |||
long guildId = IdConvert.ToLong(e.Payload.Value<string>("guild_id")); | |||
if (_config.EnableVoice && guildId == _voiceSocket.CurrentServerId) | |||
{ | |||
string token = e.Payload.Value<string>("token"); | |||
_voiceSocket.Host = "wss://" + e.Payload.Value<string>("endpoint").Split(':')[0]; | |||
await _voiceSocket.Login(_userId.Value, _dataSocket.SessionId, token, _cancelToken).ConfigureAwait(false); | |||
} | |||
} | |||
break; | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
RaiseOnLog(LogMessageSeverity.Error, LogMessageSource.Client, $"Error handling {e.Type} event: {ex.GetBaseException().Message}"); | |||
} | |||
return TaskHelper.CompletedTask; | |||
} | |||
} | |||
} |
@@ -3,15 +3,6 @@ using System.Reflection; | |||
namespace Discord | |||
{ | |||
[Flags] | |||
public enum DiscordVoiceMode | |||
{ | |||
Disabled = 0x00, | |||
Incoming = 0x01, | |||
Outgoing = 0x02, | |||
Both = Outgoing | Incoming | |||
} | |||
public class DiscordWSClientConfig : DiscordAPIClientConfig | |||
{ | |||
/// <summary> Max time in milliseconds to wait for DiscordClient to connect and initialize. </summary> | |||
@@ -23,36 +14,15 @@ namespace Discord | |||
/// <summary> Gets or sets the time (in milliseconds) to wait after an reconnect fails before retrying. </summary> | |||
public int FailedReconnectDelay { get { return _failedReconnectDelay; } set { SetValue(ref _failedReconnectDelay, value); } } | |||
private int _failedReconnectDelay = 10000; | |||
/// <summary> Gets or sets the time (in milliseconds) to wait when the websocket's message queue is empty before checking again. </summary> | |||
public int WebSocketInterval { get { return _webSocketInterval; } set { SetValue(ref _webSocketInterval, value); } } | |||
private int _webSocketInterval = 100; | |||
/// <summary> Gets or sets the max buffer length (in milliseconds) for outgoing voice packets. This value is the target maximum but is not guaranteed, the buffer will often go slightly above this value. </summary> | |||
public int VoiceBufferLength { get { return _voiceBufferLength; } set { SetValue(ref _voiceBufferLength, value); } } | |||
private int _voiceBufferLength = 1000; | |||
/// <summary> Gets or sets the bitrate used (in kbit/s, between 1 and 512 inclusively) for outgoing voice packets. A null value will use default Opus settings. </summary> | |||
public int? VoiceBitrate { get { return _voiceBitrate; } set { SetValue(ref _voiceBitrate, value); } } | |||
private int? _voiceBitrate = null; | |||
//Experimental Features | |||
/// <summary> (Experimental) Enables the voice websocket and UDP client and specifies how it will be used. Any option other than Disabled requires the opus .dll or .so be in the local or system folder. </summary> | |||
public DiscordVoiceMode VoiceMode { get { return _voiceMode; } set { SetValue(ref _voiceMode, value); } } | |||
private DiscordVoiceMode _voiceMode = DiscordVoiceMode.Disabled; | |||
/// <summary> (Experimental) Enables the voice websocket and UDP client. This option requires the libsodium .dll or .so be in the local or system folder. </summary> | |||
public bool EnableVoiceEncryption { get { return _enableVoiceEncryption; } set { SetValue(ref _enableVoiceEncryption, value); } } | |||
private bool _enableVoiceEncryption = true; | |||
/// <summary> (Experimental) Instructs Discord to not send send information about offline users, for servers with more than 50 users. </summary> | |||
public bool UseLargeThreshold { get { return _useLargeThreshold; } set { SetValue(ref _useLargeThreshold, value); } } | |||
private bool _useLargeThreshold = false; | |||
//Internals | |||
internal bool VoiceOnly { get { return _voiceOnly; } set { SetValue(ref _voiceOnly, value); } } | |||
private bool _voiceOnly; | |||
internal uint VoiceClientId { get { return _voiceClientId; } set { SetValue(ref _voiceClientId, value); } } | |||
private uint _voiceClientId; | |||
internal virtual bool EnableVoice => _voiceMode != DiscordVoiceMode.Disabled; | |||
public new DiscordWSClientConfig Clone() | |||
{ | |||
var config = MemberwiseClone() as DiscordWSClientConfig; | |||
@@ -1,10 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
namespace Discord | |||
{ | |||
internal static class BitHelper | |||
{ | |||
@@ -0,0 +1,33 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public enum EditMode : byte | |||
{ | |||
Set, | |||
Add, | |||
Remove | |||
} | |||
internal static class Extensions | |||
{ | |||
public static IEnumerable<T> Modify<T>(this IEnumerable<T> original, IEnumerable<T> modified, EditMode mode) | |||
{ | |||
if (original == null) return null; | |||
switch (mode) | |||
{ | |||
case EditMode.Set: | |||
default: | |||
return modified; | |||
case EditMode.Add: | |||
return original.Concat(modified); | |||
case EditMode.Remove: | |||
return original.Except(modified); | |||
} | |||
} | |||
} | |||
} |
@@ -1,6 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text.RegularExpressions; | |||
namespace Discord | |||
@@ -10,17 +9,21 @@ namespace Discord | |||
private static readonly Regex _userRegex = new Regex(@"<@([0-9]+?)>", RegexOptions.Compiled); | |||
private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+?)>", RegexOptions.Compiled); | |||
private static readonly Regex _roleRegex = new Regex(@"@everyone", RegexOptions.Compiled); | |||
/// <summary> Returns the string used to create a user mention. </summary> | |||
[Obsolete("Use User.Mention instead")] | |||
public static string User(User user) | |||
=> $"<@{user.Id}>"; | |||
/// <summary> Returns the string used to create a user mention. </summary> | |||
[Obsolete("Use GlobalUser.Mention instead")] | |||
public static string User(GlobalUser user) | |||
=> $"<@{user.Id}>"; | |||
/// <summary> Returns the string used to create a channel mention. </summary> | |||
[Obsolete("Use Channel.Mention instead")] | |||
public static string Channel(Channel channel) | |||
=> $"<#{channel.Id}>"; | |||
/// <summary> Returns the string used to create a mention to everyone in a channel. </summary> | |||
[Obsolete("Use Role.Mention instead")] | |||
public static string Everyone() | |||
=> $"@everyone"; | |||
@@ -72,7 +75,7 @@ namespace Discord | |||
if (source == null) throw new ArgumentNullException(nameof(source)); | |||
if (text == null) throw new ArgumentNullException(nameof(text)); | |||
return Resolve(source?.Server, text); | |||
return Resolve(source.Server, text); | |||
} | |||
/// <summary>Resolves all mentions in a provided string to those users, channels or roles' names.</summary> | |||
@@ -80,12 +83,12 @@ namespace Discord | |||
{ | |||
if (text == null) throw new ArgumentNullException(nameof(text)); | |||
var client = server.Client; | |||
text = Mention.CleanUserMentions(client, server, text); | |||
var client = server?.Client; | |||
text = CleanUserMentions(client, server, text); | |||
if (server != null) | |||
{ | |||
text = Mention.CleanChannelMentions(client, server, text); | |||
//text = Mention.CleanRoleMentions(_client, User, channel, text); | |||
text = CleanChannelMentions(client, server, text); | |||
//text = CleanRoleMentions(_client, User, channel, text); | |||
} | |||
return text; | |||
} | |||
@@ -1,19 +1,10 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
public enum EditMode : byte | |||
{ | |||
Set, | |||
Add, | |||
Remove | |||
} | |||
internal static class Extensions | |||
public static class TaskExtensions | |||
{ | |||
public static async Task Timeout(this Task task, int milliseconds) | |||
{ | |||
@@ -73,20 +64,5 @@ namespace Discord | |||
try { await Task.Delay(-1, token).ConfigureAwait(false); } | |||
catch (OperationCanceledException) { } //Expected | |||
} | |||
public static IEnumerable<T> Modify<T>(this IEnumerable<T> original, IEnumerable<T> modified, EditMode mode) | |||
{ | |||
if (original == null) return null; | |||
switch (mode) | |||
{ | |||
case EditMode.Set: | |||
default: | |||
return modified; | |||
case EditMode.Add: | |||
return original.Concat(modified); | |||
case EditMode.Remove: | |||
return original.Except(modified); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -2,6 +2,7 @@ | |||
{ | |||
public interface IService | |||
{ | |||
void Install(DiscordClient client); | |||
} | |||
} |
@@ -6,9 +6,9 @@ using System.Threading.Tasks; | |||
namespace Discord.Net.WebSockets | |||
{ | |||
internal partial class DataWebSocket : WebSocket | |||
public partial class DataWebSocket : WebSocket | |||
{ | |||
public enum OpCodes : byte | |||
internal enum OpCodes : byte | |||
{ | |||
Dispatch = 0, | |||
Heartbeat = 1, | |||
@@ -155,7 +155,7 @@ namespace Discord.Net.WebSockets | |||
leaveVoice.Payload.ServerId = serverId; | |||
QueueMessage(leaveVoice); | |||
} | |||
public void SendGetUsers(long serverId, string query = "", int limit = 0) | |||
public void SendRequestUsers(long serverId, string query = "", int limit = 0) | |||
{ | |||
var getOfflineUsers = new GetUsersCommand(); | |||
getOfflineUsers.Payload.ServerId = serverId; | |||
@@ -3,7 +3,7 @@ using System; | |||
namespace Discord.Net.WebSockets | |||
{ | |||
internal sealed class WebSocketEventEventArgs : EventArgs | |||
public sealed class WebSocketEventEventArgs : EventArgs | |||
{ | |||
public readonly string Type; | |||
public readonly JToken Payload; | |||
@@ -14,7 +14,7 @@ namespace Discord.Net.WebSockets | |||
} | |||
} | |||
internal partial class DataWebSocket | |||
public partial class DataWebSocket | |||
{ | |||
internal event EventHandler<WebSocketEventEventArgs> ReceivedEvent; | |||
private void RaiseReceivedEvent(string type, JToken payload) | |||
@@ -5,18 +5,18 @@ using System.Threading.Tasks; | |||
namespace Discord.Net.WebSockets | |||
{ | |||
internal class WebSocketBinaryMessageEventArgs : EventArgs | |||
public class WebSocketBinaryMessageEventArgs : EventArgs | |||
{ | |||
public readonly byte[] Data; | |||
public WebSocketBinaryMessageEventArgs(byte[] data) { Data = data; } | |||
} | |||
internal class WebSocketTextMessageEventArgs : EventArgs | |||
public class WebSocketTextMessageEventArgs : EventArgs | |||
{ | |||
public readonly string Message; | |||
public WebSocketTextMessageEventArgs(string msg) { Message = msg; } | |||
} | |||
internal interface IWebSocketEngine | |||
public interface IWebSocketEngine | |||
{ | |||
event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||
event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||
@@ -2,7 +2,7 @@ | |||
namespace Discord.Net.WebSockets | |||
{ | |||
internal abstract partial class WebSocket | |||
public abstract partial class WebSocket | |||
{ | |||
public event EventHandler Connected; | |||
private void RaiseConnected() | |||
@@ -10,7 +10,7 @@ using System.Threading.Tasks; | |||
namespace Discord.Net.WebSockets | |||
{ | |||
internal enum WebSocketState : byte | |||
public enum WebSocketState : byte | |||
{ | |||
Disconnected, | |||
Connecting, | |||
@@ -18,7 +18,7 @@ namespace Discord.Net.WebSockets | |||
Disconnecting | |||
} | |||
internal abstract partial class WebSocket | |||
public abstract partial class WebSocket | |||
{ | |||
protected readonly IWebSocketEngine _engine; | |||
protected readonly DiscordWSClient _client; | |||
@@ -4,7 +4,7 @@ using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using WSSharpNWebSocket = WebSocketSharp.WebSocket; | |||
using WSSharpWebSocket = WebSocketSharp.WebSocket; | |||
namespace Discord.Net.WebSockets | |||
{ | |||
@@ -13,7 +13,7 @@ namespace Discord.Net.WebSockets | |||
private readonly DiscordWSClientConfig _config; | |||
private readonly ConcurrentQueue<string> _sendQueue; | |||
private readonly WebSocket _parent; | |||
private WSSharpNWebSocket _webSocket; | |||
private WSSharpWebSocket _webSocket; | |||
public event EventHandler<WebSocketBinaryMessageEventArgs> BinaryMessage; | |||
public event EventHandler<WebSocketTextMessageEventArgs> TextMessage; | |||
@@ -37,7 +37,7 @@ namespace Discord.Net.WebSockets | |||
public Task Connect(string host, CancellationToken cancelToken) | |||
{ | |||
_webSocket = new WSSharpNWebSocket(host); | |||
_webSocket = new WSSharpWebSocket(host); | |||
_webSocket.EmitOnPing = false; | |||
_webSocket.EnableRedirection = true; | |||
_webSocket.Compression = WebSocketSharp.CompressionMethod.Deflate; | |||
@@ -1,32 +1,30 @@ | |||
{ | |||
"version": "0.8.1-beta2", | |||
"description": "An unofficial .Net API wrapper for the Discord client.", | |||
"authors": [ | |||
"RogueException" | |||
], | |||
"version": "0.8.1-beta2", | |||
"description": "An unofficial .Net API wrapper for the Discord client.", | |||
"authors": [ | |||
"RogueException" | |||
], | |||
"tags": [ | |||
"discord", | |||
"discordapp" | |||
], | |||
"projectUrl": "https://github.com/RogueException/Discord.Net", | |||
"licenseUrl": "http://opensource.org/licenses/MIT", | |||
"repository": { | |||
"type": "git", | |||
"url": "git://github.com/RogueException/Discord.Net" | |||
}, | |||
"compilationOptions": { | |||
"allowUnsafe": true | |||
}, | |||
"projectUrl": "https://github.com/RogueException/Discord.Net", | |||
"licenseUrl": "http://opensource.org/licenses/MIT", | |||
"repository": { | |||
"type": "git", | |||
"url": "git://github.com/RogueException/Discord.Net" | |||
}, | |||
"configurations": { | |||
"TestResponses": { | |||
"compilationOptions": { | |||
"define": [ | |||
"DEBUG", | |||
"TRACE", | |||
"TEST_RESPONSES" | |||
] | |||
} | |||
} | |||
"TestResponses": { | |||
"compilationOptions": { | |||
"define": [ | |||
"DEBUG", | |||
"TRACE", | |||
"TEST_RESPONSES" | |||
] | |||
} | |||
} | |||
}, | |||
"dependencies": { | |||