diff --git a/NadekoBot/Classes/ObservableConcurrentDictionary.cs b/NadekoBot/Classes/ObservableConcurrentDictionary.cs
new file mode 100644
index 00000000..cd364328
--- /dev/null
+++ b/NadekoBot/Classes/ObservableConcurrentDictionary.cs
@@ -0,0 +1,204 @@
+//--------------------------------------------------------------------------
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// File: ObservableConcurrentDictionary.cs
+//
+//--------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Threading;
+
+namespace System.Collections.Concurrent
+{
+ ///
+ /// Provides a thread-safe dictionary for use with data binding.
+ ///
+ /// Specifies the type of the keys in this collection.
+ /// Specifies the type of the values in this collection.
+ [DebuggerDisplay("Count={Count}")]
+ public class ObservableConcurrentDictionary :
+ ICollection>, IDictionary,
+ INotifyCollectionChanged, INotifyPropertyChanged
+ {
+ private readonly SynchronizationContext _context;
+ private readonly ConcurrentDictionary _dictionary;
+
+ ///
+ /// Initializes an instance of the ObservableConcurrentDictionary class.
+ ///
+ public ObservableConcurrentDictionary()
+ {
+ _context = AsyncOperationManager.SynchronizationContext;
+ _dictionary = new ConcurrentDictionary();
+ }
+
+ /// Event raised when the collection changes.
+ public event NotifyCollectionChangedEventHandler CollectionChanged;
+ /// Event raised when a property on the collection changes.
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
+ ///
+ private void NotifyObserversOfChange()
+ {
+ var collectionHandler = CollectionChanged;
+ var propertyHandler = PropertyChanged;
+ if (collectionHandler != null || propertyHandler != null)
+ {
+ _context.Post(s =>
+ {
+ if (collectionHandler != null)
+ {
+ collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+ if (propertyHandler != null)
+ {
+ propertyHandler(this, new PropertyChangedEventArgs("Count"));
+ propertyHandler(this, new PropertyChangedEventArgs("Keys"));
+ propertyHandler(this, new PropertyChangedEventArgs("Values"));
+ }
+ }, null);
+ }
+ }
+
+ /// Attempts to add an item to the dictionary, notifying observers of any changes.
+ /// The item to be added.
+ /// Whether the add was successful.
+ private bool TryAddWithNotification(KeyValuePair item)
+ {
+ return TryAddWithNotification(item.Key, item.Value);
+ }
+
+ /// Attempts to add an item to the dictionary, notifying observers of any changes.
+ /// The key of the item to be added.
+ /// The value of the item to be added.
+ /// Whether the add was successful.
+ private bool TryAddWithNotification(TKey key, TValue value)
+ {
+ bool result = _dictionary.TryAdd(key, value);
+ if (result) NotifyObserversOfChange();
+ return result;
+ }
+
+ /// Attempts to remove an item from the dictionary, notifying observers of any changes.
+ /// The key of the item to be removed.
+ /// The value of the item removed.
+ /// Whether the removal was successful.
+ private bool TryRemoveWithNotification(TKey key, out TValue value)
+ {
+ bool result = _dictionary.TryRemove(key, out value);
+ if (result) NotifyObserversOfChange();
+ return result;
+ }
+
+ /// Attempts to add or update an item in the dictionary, notifying observers of any changes.
+ /// The key of the item to be updated.
+ /// The new value to set for the item.
+ /// Whether the update was successful.
+ private void UpdateWithNotification(TKey key, TValue value)
+ {
+ _dictionary[key] = value;
+ NotifyObserversOfChange();
+ }
+
+ #region ICollection> Members
+ void ICollection>.Add(KeyValuePair item)
+ {
+ TryAddWithNotification(item);
+ }
+
+ void ICollection>.Clear()
+ {
+ ((ICollection>)_dictionary).Clear();
+ NotifyObserversOfChange();
+ }
+
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ return ((ICollection>)_dictionary).Contains(item);
+ }
+
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ((ICollection>)_dictionary).CopyTo(array, arrayIndex);
+ }
+
+ int ICollection>.Count {
+ get { return ((ICollection>)_dictionary).Count; }
+ }
+
+ bool ICollection>.IsReadOnly {
+ get { return ((ICollection>)_dictionary).IsReadOnly; }
+ }
+
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ TValue temp;
+ return TryRemoveWithNotification(item.Key, out temp);
+ }
+ #endregion
+
+ #region IEnumerable> Members
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ return ((ICollection>)_dictionary).GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((ICollection>)_dictionary).GetEnumerator();
+ }
+ #endregion
+
+ #region IDictionary Members
+ public void Add(TKey key, TValue value)
+ {
+ TryAddWithNotification(key, value);
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return _dictionary.ContainsKey(key);
+ }
+
+ public ICollection Keys {
+ get { return _dictionary.Keys; }
+ }
+
+ public bool Remove(TKey key)
+ {
+ TValue temp;
+ return TryRemoveWithNotification(key, out temp);
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return _dictionary.TryGetValue(key, out value);
+ }
+
+ public bool TryAdd(TKey key, TValue value)
+ {
+ return TryAddWithNotification(key, value);
+ }
+
+ public ICollection Values {
+ get { return _dictionary.Values; }
+ }
+
+ public TValue this[TKey key] {
+ get { return _dictionary[key]; }
+ set { UpdateWithNotification(key, value); }
+ }
+
+ public bool TryRemove(TKey key, out TValue value)
+ {
+ return TryRemoveWithNotification(key, out value);
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs
index b8c2cc9d..bbc4eadd 100644
--- a/NadekoBot/Classes/ServerSpecificConfig.cs
+++ b/NadekoBot/Classes/ServerSpecificConfig.cs
@@ -82,6 +82,45 @@ namespace NadekoBot.Classes
}
}
+ [JsonProperty("LogChannel")]
+ private ulong? logServerChannel = null;
+ [JsonIgnore]
+ public ulong? LogServerChannel {
+ get { return logServerChannel; }
+ set {
+ logServerChannel = value;
+ if (!SpecificConfigurations.Instantiated) return;
+ OnPropertyChanged();
+ }
+ }
+
+ [JsonProperty("LogPresenceChannel")]
+ private ulong? logPresenceChannel = null;
+ [JsonIgnore]
+ public ulong? LogPresenceChannel {
+ get { return logPresenceChannel; }
+ set {
+ logPresenceChannel = value;
+ if (!SpecificConfigurations.Instantiated) return;
+ OnPropertyChanged();
+ }
+ }
+
+ [JsonIgnore]
+ private ObservableConcurrentDictionary voiceChannelLog;
+ public ObservableConcurrentDictionary VoiceChannelLog {
+ get { return voiceChannelLog; }
+ set {
+ voiceChannelLog = value;
+ if (value != null)
+ voiceChannelLog.CollectionChanged += (s, e) =>
+ {
+ if (!SpecificConfigurations.Instantiated) return;
+ OnPropertyChanged();
+ };
+ }
+ }
+
[JsonIgnore]
private ObservableCollection listOfSelfAssignableRoles;
public ObservableCollection ListOfSelfAssignableRoles {
@@ -110,7 +149,6 @@ namespace NadekoBot.Classes
[JsonIgnore]
private ObservableCollection generateCurrencyChannels;
-
public ObservableCollection GenerateCurrencyChannels {
get { return generateCurrencyChannels; }
set {
@@ -124,9 +162,6 @@ namespace NadekoBot.Classes
}
}
- [JsonIgnore]
- private ObservableCollection observingStreams;
-
[JsonIgnore]
private bool autoDeleteMessagesOnCommand = false;
public bool AutoDeleteMessagesOnCommand {
@@ -138,6 +173,9 @@ namespace NadekoBot.Classes
}
}
+
+ [JsonIgnore]
+ private ObservableCollection observingStreams;
public ObservableCollection ObservingStreams {
get { return observingStreams; }
set {
@@ -167,6 +205,7 @@ namespace NadekoBot.Classes
ListOfSelfAssignableRoles = new ObservableCollection();
ObservingStreams = new ObservableCollection();
GenerateCurrencyChannels = new ObservableCollection();
+ VoiceChannelLog = new ObservableConcurrentDictionary();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); };
diff --git a/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/NadekoBot/Modules/Administration/Commands/LogCommand.cs
index 41bbdcd5..3cd92485 100644
--- a/NadekoBot/Modules/Administration/Commands/LogCommand.cs
+++ b/NadekoBot/Modules/Administration/Commands/LogCommand.cs
@@ -4,22 +4,12 @@ using NadekoBot.Classes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Classes;
using System;
-using System.Collections.Concurrent;
using System.Linq;
-using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Commands
{
internal class LogCommand : DiscordCommand
{
-
- //server-channel
- private readonly ConcurrentDictionary logs = new ConcurrentDictionary();
- //server-channel
- private readonly ConcurrentDictionary loggingPresences = new ConcurrentDictionary();
- //channel-channel
- private readonly ConcurrentDictionary voiceChannelLog = new ConcurrentDictionary();
-
private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
public LogCommand(DiscordModule module) : base(module)
@@ -61,8 +51,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -82,8 +72,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -97,8 +87,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -112,8 +102,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -127,8 +117,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -142,8 +132,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -157,8 +147,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -168,29 +158,14 @@ namespace NadekoBot.Modules.Administration.Commands
catch { }
}
- public Func DoFunc() => async e =>
- {
- ulong chId;
- if (!logs.TryRemove(e.Server.Id, out chId))
- {
- logs.TryAdd(e.Server.Id, e.Channel.Id);
- await e.Channel.SendMessage($"❗**I WILL BEGIN LOGGING SERVER ACTIVITY IN THIS CHANNEL**❗").ConfigureAwait(false);
- return;
- }
- Channel ch;
- if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
- return;
- await e.Channel.SendMessage($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false);
- };
-
private async void MsgRecivd(object sender, MessageEventArgs e)
{
try
{
if (e.Server == null || e.Channel.IsPrivate || e.User.Id == NadekoBot.Client.CurrentUser.Id)
return;
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId) || e.Channel.Id == chId)
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -217,8 +192,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return;
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId) || e.Channel.Id == chId)
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -244,8 +219,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return;
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId) || e.Channel.Id == chId)
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -260,10 +235,11 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
}
private async void UsrUpdtd(object sender, UserUpdatedEventArgs e)
{
+ var config = SpecificConfigurations.Default.Of(e.Server.Id);
try
{
- ulong chId;
- if (!loggingPresences.TryGetValue(e.Server.Id, out chId))
+ var chId = config.LogServerChannel;
+ if (chId != null)
{
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) != null)
@@ -289,11 +265,11 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
var notifyJoin = false;
if ((beforeVch != null || afterVch != null) && (beforeVch != afterVch)) // this means we need to notify for sure.
{
- if (beforeVch != null && voiceChannelLog.TryGetValue(beforeVch.Id, out notifyChBeforeId) && (notifyChBefore = e.Before.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChBeforeId)) != null)
+ if (beforeVch != null && config.VoiceChannelLog.TryGetValue(beforeVch.Id, out notifyChBeforeId) && (notifyChBefore = e.Before.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChBeforeId)) != null)
{
notifyLeave = true;
}
- if (afterVch != null && voiceChannelLog.TryGetValue(afterVch.Id, out notifyChAfterId) && (notifyChAfter = e.After.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChAfterId)) != null)
+ if (afterVch != null && config.VoiceChannelLog.TryGetValue(afterVch.Id, out notifyChAfterId) && (notifyChAfter = e.After.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChAfterId)) != null)
{
notifyJoin = true;
}
@@ -315,8 +291,8 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
try
{
- ulong chId;
- if (!logs.TryGetValue(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
return;
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@@ -377,17 +353,30 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
.Description("Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!**")
.AddCheck(SimpleCheckers.OwnerOnly())
.AddCheck(SimpleCheckers.ManageServer())
- .Do(DoFunc());
+ .Do(async e =>
+ {
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
+ {
+ SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel = e.Channel.Id;
+ await e.Channel.SendMessage($"❗**I WILL BEGIN LOGGING SERVER ACTIVITY IN THIS CHANNEL**❗").ConfigureAwait(false);
+ return;
+ }
+ Channel ch;
+ if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
+ return;
+ await e.Channel.SendMessage($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false);
+ });
cgb.CreateCommand(Module.Prefix + "userpresence")
.Description("Starts logging to this channel when someone from the server goes online/offline/idle.")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
- ulong chId;
- if (!loggingPresences.TryRemove(e.Server.Id, out chId))
+ var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
+ if (chId == null)
{
- loggingPresences.TryAdd(e.Server.Id, e.Channel.Id);
+ SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel = e.Channel.Id;
await e.Channel.SendMessage($"**User presence notifications enabled.**").ConfigureAwait(false);
return;
}
@@ -402,11 +391,12 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
.Do(async e =>
{
+ var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (e.GetArg("all")?.ToLower() == "all")
{
foreach (var voiceChannel in e.Server.VoiceChannels)
{
- voiceChannelLog.TryAdd(voiceChannel.Id, e.Channel.Id);
+ config.VoiceChannelLog.TryAdd(voiceChannel.Id, e.Channel.Id);
}
await e.Channel.SendMessage("Started logging user presence for **ALL** voice channels!").ConfigureAwait(false);
return;
@@ -418,9 +408,9 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
return;
}
ulong throwaway;
- if (!voiceChannelLog.TryRemove(e.User.VoiceChannel.Id, out throwaway))
+ if (!config.VoiceChannelLog.TryRemove(e.User.VoiceChannel.Id, out throwaway))
{
- voiceChannelLog.TryAdd(e.User.VoiceChannel.Id, e.Channel.Id);
+ config.VoiceChannelLog.TryAdd(e.User.VoiceChannel.Id, e.Channel.Id);
await e.Channel.SendMessage($"`Logging user updates for` {e.User.VoiceChannel.Mention} `voice channel.`").ConfigureAwait(false);
}
else
diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs
index 2d0dbd86..f2e2071a 100644
--- a/NadekoBot/Modules/Music/MusicModule.cs
+++ b/NadekoBot/Modules/Music/MusicModule.cs
@@ -105,6 +105,21 @@ namespace NadekoBot.Modules.Music
}
});
+ //cgb.CreateCommand("soundcloudqueue")
+ // .Alias("sq")
+ // .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." +
+ // "**You must be in a voice channel**.\n**Usage**: `!m sq Dream Of Venice`")
+ // .Parameter("query", ParameterType.Unparsed)
+ // .Do(async e =>
+ // {
+ // await QueueSong(e.Channel, e.User.VoiceChannel, e.GetArg("query")).ConfigureAwait(false);
+ // if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages)
+ // {
+ // await Task.Delay(10000).ConfigureAwait(false);
+ // await e.Message.Delete().ConfigureAwait(false);
+ // }
+ // });
+
cgb.CreateCommand("listqueue")
.Alias("lq")
.Description("Lists 15 currently queued songs per page. Default page is 1.\n**Usage**: `!m lq` or `!m lq 2`")
diff --git a/NadekoBot/lib/ScaredFingers.UnitsConversion.dll b/NadekoBot/lib/ScaredFingers.UnitsConversion.dll
deleted file mode 100644
index 8b121811..00000000
Binary files a/NadekoBot/lib/ScaredFingers.UnitsConversion.dll and /dev/null differ
diff --git a/NadekoBot/lib/ScaredFingers.UnitsConversion.pdb b/NadekoBot/lib/ScaredFingers.UnitsConversion.pdb
deleted file mode 100644
index f63024ad..00000000
Binary files a/NadekoBot/lib/ScaredFingers.UnitsConversion.pdb and /dev/null differ