Merge remote-tracking branch 'refs/remotes/Kwoth/master' into selfassignedtoggle

merging branch
This commit is contained in:
appelemac 2016-07-17 20:49:25 +02:00
commit 3a85e5e3eb
33 changed files with 886 additions and 402 deletions

View File

@ -47,50 +47,4 @@ ________________________________________________________________________________
- On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key.
- Open up credentials.json. For "GoogleAPIKey", fill in with the new key.
- Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID.
________________________________________________________________________________
#### Setting Up NadekoBot Permissions
###### NadekoBot's permissions can be set up to be very specific through commands in the Permissions module.
Each command or module can be turned on or off at:
- a user level (so specific users can or cannot use a command/module)
- a role level (so only certain roles have access to certain commands/module)
- a channel level (so certain commands can be limited to certain channels, which can prevent music / trivia / NSFW spam in serious channels)
- a server level.
Use .modules to see a list of modules (sets of commands).
Use .commands [module_name] to see a list of commands in a certain module.
Permissions use a semicolon as the prefix, so always start the command with a ;.
Follow the semicolon with the letter of the level which you want to edit.
- "u" for Users.
- "r" for Roles.
- "c" for Channels.
- "s" for Servers.
Follow the level with whether you want to edit the permissions of a command or a module.
- "c" for Command.
- "m" for Module.
Follow with a space and then the command or module name (surround the command with quotation marks if there is a space within the command, for example "!m q" or "!m n").
Follow that with another space and, to enable it, type one of the following: [1, true, t, enable], or to disable it, one of the following: [0, false, f, disable].
Follow that with another space and the name of the user, role, channel. (depending on the first letter you picked)
###### Examples
- **;rm NSFW 0 [Role_Name]** Disables the NSFW module for the role, <Role_Name>.
- **;cc "!m n" 0 [Channel_Name]** Disables skipping to the next song in the channel, <Channel_Name>.
- **;uc "!m q" 1 [User_Name]** Enables queuing of songs for the user, <User_Name>.
- **;sm Gambling 0** Disables gambling in the server.
Check permissions by using the letter of the level you want to check followed by a p, and then the name of the level in which you want to check. If there is no name, it will default to yourself for users, the @everyone role for roles, and the channel in which the command is sent for channels.
###### Examples
- ;cp [Channel_Name]
- ;rp [Role_Name]
Insert an **a** before the level to edit the permission for all commands / modules for all users / roles / channels / server.
Reference the Help command (-h) for more Permissions related commands.
- Restart your computer.

View File

@ -121,12 +121,10 @@ Note if the command is not be initiated, hit **Enter**
######NOW WE NEED TO IMPORT SOME DISCORD CERTS
**13)**
<pre><code class="language-bash">mozroots --import --ask-remove --machine
</code></pre>
`certmgr -ssl https://discordapp.com`
**14)**
<pre><code class="language-bash">certmgr --ssl https://gateway.discord.gg
</code></pre>
`certmgr --ssl https://gateway.discord.gg`
Type `yes` and hit Enter **(three times - as it will ask for three times)**

View File

@ -41,32 +41,32 @@ Global
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.ActiveCfg = NadekoRelease|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.Build.0 = NadekoRelease|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|Any CPU.Build.0 = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.ActiveCfg = NadekoRelease|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.Build.0 = NadekoRelease|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Release|Any CPU.Build.0 = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.ActiveCfg = NadekoRelease|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.Build.0 = NadekoRelease|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.Build.0 = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|Any CPU.Build.0 = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.ActiveCfg = NadekoRelease|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.Build.0 = NadekoRelease|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.Build.0 = Release|Any CPU
{45B2545D-C612-4919-B34C-D65EA1371C51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

View File

@ -198,7 +198,7 @@ Limit 20 OFFSET ?", num * 20);
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Table<CurrencyState>().Take(n).ToList().OrderBy(cs => -cs.Value);
return conn.Table<CurrencyState>().OrderByDescending(cs => cs.Value).Take(n).ToList();
}
}
}

View File

@ -352,5 +352,15 @@ namespace NadekoBot.Extensions
await Task.Run(() => images.Merge(reverseScaleFactor)).ConfigureAwait(false);
public static string Unmention(this string str) => str.Replace("@", "ම");
public static Stream ToStream(this string str)
{
var sw = new StreamWriter(new MemoryStream());
sw.Write(str);
sw.Flush();
sw.BaseStream.Position = 0;
return sw.BaseStream;
}
}
}

View File

@ -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
{
/// <summary>
/// Provides a thread-safe dictionary for use with data binding.
/// </summary>
/// <typeparam name="TKey">Specifies the type of the keys in this collection.</typeparam>
/// <typeparam name="TValue">Specifies the type of the values in this collection.</typeparam>
[DebuggerDisplay("Count={Count}")]
public class ObservableConcurrentDictionary<TKey, TValue> :
ICollection<KeyValuePair<TKey, TValue>>, IDictionary<TKey, TValue>,
INotifyCollectionChanged, INotifyPropertyChanged
{
private readonly SynchronizationContext _context;
private readonly ConcurrentDictionary<TKey, TValue> _dictionary;
/// <summary>
/// Initializes an instance of the ObservableConcurrentDictionary class.
/// </summary>
public ObservableConcurrentDictionary()
{
_context = AsyncOperationManager.SynchronizationContext;
_dictionary = new ConcurrentDictionary<TKey, TValue>();
}
/// <summary>Event raised when the collection changes.</summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>Event raised when a property on the collection changes.</summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary.
/// </summary>
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);
}
}
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
/// <param name="item">The item to be added.</param>
/// <returns>Whether the add was successful.</returns>
private bool TryAddWithNotification(KeyValuePair<TKey, TValue> item)
{
return TryAddWithNotification(item.Key, item.Value);
}
/// <summary>Attempts to add an item to the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be added.</param>
/// <param name="value">The value of the item to be added.</param>
/// <returns>Whether the add was successful.</returns>
private bool TryAddWithNotification(TKey key, TValue value)
{
bool result = _dictionary.TryAdd(key, value);
if (result) NotifyObserversOfChange();
return result;
}
/// <summary>Attempts to remove an item from the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be removed.</param>
/// <param name="value">The value of the item removed.</param>
/// <returns>Whether the removal was successful.</returns>
private bool TryRemoveWithNotification(TKey key, out TValue value)
{
bool result = _dictionary.TryRemove(key, out value);
if (result) NotifyObserversOfChange();
return result;
}
/// <summary>Attempts to add or update an item in the dictionary, notifying observers of any changes.</summary>
/// <param name="key">The key of the item to be updated.</param>
/// <param name="value">The new value to set for the item.</param>
/// <returns>Whether the update was successful.</returns>
private void UpdateWithNotification(TKey key, TValue value)
{
_dictionary[key] = value;
NotifyObserversOfChange();
}
#region ICollection<KeyValuePair<TKey,TValue>> Members
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
TryAddWithNotification(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.Clear()
{
((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Clear();
NotifyObserversOfChange();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
}
int ICollection<KeyValuePair<TKey, TValue>>.Count {
get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Count; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
TValue temp;
return TryRemoveWithNotification(item.Key, out temp);
}
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value)
{
TryAddWithNotification(key, value);
}
public bool ContainsKey(TKey key)
{
return _dictionary.ContainsKey(key);
}
public ICollection<TKey> 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<TValue> 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
}
}

View File

@ -377,5 +377,13 @@ namespace NadekoBot.Classes
return url;
}
}
public static string ShowInPrettyCode<T>(IEnumerable<T> items, Func<T, string> howToPrint, int cols = 3)
{
var i = 0;
return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols)
.Select(ig => string.Join("", ig.Select(el => howToPrint(el)))))
+ $"\n```";
}
}
}

View File

@ -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<ulong, ulong> voiceChannelLog;
public ObservableConcurrentDictionary<ulong, ulong> VoiceChannelLog {
get { return voiceChannelLog; }
set {
voiceChannelLog = value;
if (value != null)
voiceChannelLog.CollectionChanged += (s, e) =>
{
if (!SpecificConfigurations.Instantiated) return;
OnPropertyChanged();
};
}
}
[JsonIgnore]
private ObservableCollection<ulong> listOfSelfAssignableRoles;
public ObservableCollection<ulong> ListOfSelfAssignableRoles {
@ -111,7 +150,19 @@ namespace NadekoBot.Classes
}
[JsonIgnore]
private ObservableCollection<StreamNotificationConfig> observingStreams;
private ObservableCollection<ulong> generateCurrencyChannels;
public ObservableCollection<ulong> GenerateCurrencyChannels {
get { return generateCurrencyChannels; }
set {
generateCurrencyChannels = value;
if (value != null)
generateCurrencyChannels.CollectionChanged += (s, e) =>
{
if (!SpecificConfigurations.Instantiated) return;
OnPropertyChanged();
};
}
}
[JsonIgnore]
private bool autoDeleteMessagesOnCommand = false;
@ -137,6 +188,9 @@ namespace NadekoBot.Classes
}
}
[JsonIgnore]
private ObservableCollection<StreamNotificationConfig> observingStreams;
public ObservableCollection<StreamNotificationConfig> ObservingStreams {
get { return observingStreams; }
set {
@ -165,6 +219,8 @@ namespace NadekoBot.Classes
{
ListOfSelfAssignableRoles = new ObservableCollection<ulong>();
ObservingStreams = new ObservableCollection<StreamNotificationConfig>();
GenerateCurrencyChannels = new ObservableCollection<ulong>();
VoiceChannelLog = new ObservableConcurrentDictionary<ulong, ulong>();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); };

View File

@ -6,8 +6,11 @@ using NadekoBot.DataModels;
using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Commands;
using NadekoBot.Modules.Permissions.Classes;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
@ -76,7 +79,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "restart")
.Description("Restarts the bot. Might not work.")
.Description("Restarts the bot. Might not work. **Bot Owner Only**")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -902,6 +905,33 @@ namespace NadekoBot.Modules.Administration
await e.Channel.SendMessage("`Done.`").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "savechat")
.Description("Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150`")
.Parameter("cnt", ParameterType.Required)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
var cntstr = e.GetArg("cnt")?.Trim();
int cnt;
if (!int.TryParse(cntstr, out cnt))
return;
ulong? lastmsgId = null;
var sb = new StringBuilder();
var msgs = new List<Message>(cnt);
while (cnt > 0)
{
var dlcnt = cnt < 100 ? cnt : 100;
var dledMsgs = await e.Channel.DownloadMessages(dlcnt, lastmsgId);
if (!dledMsgs.Any())
break;
msgs.AddRange(dledMsgs);
lastmsgId = msgs[msgs.Count - 1].Id;
cnt -= 100;
}
await e.User.SendFile($"Chatlog-{e.Server.Name}/#{e.Channel.Name}-{DateTime.Now}.txt", JsonConvert.SerializeObject(new { Messages = msgs.Select(s => s.ToString()) }, Formatting.Indented).ToStream());
});
});
}
}

View File

@ -2,6 +2,7 @@
using Discord.Commands;
using NadekoBot.Classes;
using NadekoBot.Modules.Permissions.Classes;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -45,14 +46,88 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Prefix + "listcustreact")
.Alias(Prefix + "lcr")
.Description($"Lists all current custom reactions (paginated with 5 commands per page).\n**Usage**:{Prefix}lcr 1")
.Description($"Lists all current custom reactions (paginated with 30 commands per page).\n**Usage**:{Prefix}lcr 1")
.Parameter("num", ParameterType.Required)
.Do(async e =>
{
int num;
if (!int.TryParse(e.GetArg("num"), out num) || num <= 0) return;
string result = GetCustomsOnPage(num - 1); //People prefer starting with 1
await e.Channel.SendMessage(result).ConfigureAwait(false);
if (!int.TryParse(e.GetArg("num"), out num) || num <= 0) num = 1;
var cmds = GetCustomsOnPage(num - 1);
if (!cmds.Any())
{
await e.Channel.SendMessage("");
}
else
{
string result = SearchHelper.ShowInPrettyCode<string>(cmds, s => $"{s,-25}"); //People prefer starting with 1
await e.Channel.SendMessage($"`Showing page {num}:`\n" + result).ConfigureAwait(false);
}
});
cgb.CreateCommand(Prefix + "showcustreact")
.Alias(Prefix + "scr")
.Description($"Shows all possible responses from a single custom reaction.\n**Usage**:{Prefix}scr %mention% bb")
.Parameter("name", ParameterType.Unparsed)
.Do(async e =>
{
var name = e.GetArg("name")?.Trim();
if (string.IsNullOrWhiteSpace(name))
return;
if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
{
await e.Channel.SendMessage("`Can't find that custom reaction.`").ConfigureAwait(false);
return;
}
var items = NadekoBot.Config.CustomReactions[name];
var message = new StringBuilder($"Responses for {Format.Bold(name)}:\n");
var last = items.Last();
int i = 1;
foreach (var reaction in items)
{
message.AppendLine($"[{i++}] " + Format.Code(reaction));
}
await e.Channel.SendMessage(message.ToString());
});
cgb.CreateCommand(Prefix + "editcustreact")
.Alias(Prefix + "ecr")
.Description("Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `.ecr \"%mention% disguise\" 2 Test 123`")
.Parameter("name", ParameterType.Required)
.Parameter("index", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
var name = e.GetArg("name")?.Trim();
if (string.IsNullOrWhiteSpace(name))
return;
var indexstr = e.GetArg("index")?.Trim();
if (string.IsNullOrWhiteSpace(indexstr))
return;
var msg = e.GetArg("message")?.Trim();
if (string.IsNullOrWhiteSpace(msg))
return;
if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
{
await e.Channel.SendMessage("`Could not find given commandname`").ConfigureAwait(false);
return;
}
int index;
if (!int.TryParse(indexstr, out index) || index < 1 || index > NadekoBot.Config.CustomReactions[name].Count)
{
await e.Channel.SendMessage("`Invalid index.`").ConfigureAwait(false);
return;
}
index = index - 1;
NadekoBot.Config.CustomReactions[name][index] = msg;
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false);
await e.Channel.SendMessage($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "delcustreact")
@ -99,19 +174,21 @@ namespace NadekoBot.Modules.Administration.Commands
});
}
private readonly int ItemsPerPage = 5;
private readonly int ItemsPerPage = 30;
private string GetCustomsOnPage(int page)
private IEnumerable<string> GetCustomsOnPage(int page)
{
var items = NadekoBot.Config.CustomReactions.Skip(page * ItemsPerPage).Take(ItemsPerPage);
if (!items.Any())
{
return $"No items on page {page + 1}.";
return Enumerable.Empty<string>();
}
return items.Select(kvp => kvp.Key);
/*
var message = new StringBuilder($"--- Custom reactions - page {page + 1} ---\n");
foreach (var cr in items)
{
message.Append($"{ Format.Code(cr.Key)}\n");
message.Append($"{Format.Code(cr.Key)}\n");
int i = 1;
var last = cr.Value.Last();
foreach (var reaction in cr.Value)
@ -123,6 +200,7 @@ namespace NadekoBot.Modules.Administration.Commands
}
}
return message.ToString() + "\n";
*/
}
}
}

View File

@ -4,19 +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
{
private readonly ConcurrentDictionary<Server, Channel> logs = new ConcurrentDictionary<Server, Channel>();
private readonly ConcurrentDictionary<Server, Channel> loggingPresences = new ConcurrentDictionary<Server, Channel>();
private readonly ConcurrentDictionary<Channel, Channel> voiceChannelLog = new ConcurrentDictionary<Channel, Channel>();
private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
public LogCommand(DiscordModule module) : base(module)
@ -58,8 +51,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
if (e.Before.Name != e.After.Name)
await ch.SendMessage($@"`{prettyCurrentTime}` **Channel Name Changed** `#{e.Before.Name}` (*{e.After.Id}*)
@ -76,8 +72,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"❗`{prettyCurrentTime}`❗`Channel Deleted:` #{e.Channel.Name} (*{e.Channel.Id}*)").ConfigureAwait(false);
}
@ -88,8 +87,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"`{prettyCurrentTime}`🆕`Channel Created:` #{e.Channel.Mention} (*{e.Channel.Id}*)").ConfigureAwait(false);
}
@ -100,8 +102,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"`{prettyCurrentTime}`♻`User was unbanned:` **{e.User.Name}** ({e.User.Id})").ConfigureAwait(false);
}
@ -112,8 +117,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"`{prettyCurrentTime}`✅`User joined:` **{e.User.Name}** ({e.User.Id})").ConfigureAwait(false);
}
@ -124,8 +132,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"`{prettyCurrentTime}`❗`User left:` **{e.User.Name}** ({e.User.Id})").ConfigureAwait(false);
}
@ -136,35 +147,28 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage($"❗`{prettyCurrentTime}`❌`User banned:` **{e.User.Name}** ({e.User.Id})").ConfigureAwait(false);
}
catch { }
}
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
Channel ch;
if (!logs.TryRemove(e.Server, out ch))
{
logs.TryAdd(e.Server, e.Channel);
await e.Channel.SendMessage($"❗**I WILL BEGIN LOGGING SERVER ACTIVITY IN THIS CHANNEL**❗").ConfigureAwait(false);
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;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch) || e.Channel == ch)
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
if (!string.IsNullOrWhiteSpace(e.Message.Text))
{
@ -188,8 +192,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch) || e.Channel == ch)
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
if (!string.IsNullOrWhiteSpace(e.Message.Text))
{
@ -212,8 +219,11 @@ namespace NadekoBot.Modules.Administration.Commands
{
if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null || e.Channel.Id == chId)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch) || e.Channel == ch)
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage(
$@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
@ -225,19 +235,28 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
}
private async void UsrUpdtd(object sender, UserUpdatedEventArgs e)
{
var config = SpecificConfigurations.Default.Of(e.Server.Id);
try
{
Channel ch;
if (loggingPresences.TryGetValue(e.Server, out ch))
if (e.Before.Status != e.After.Status)
var chId = config.LogPresenceChannel;
if (chId != null)
{
Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) != null)
{
await ch.SendMessage($"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**.").ConfigureAwait(false);
if (e.Before.Status != e.After.Status)
{
await ch.SendMessage($"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**.").ConfigureAwait(false);
}
}
}
}
catch { }
try
{
ulong notifyChBeforeId;
ulong notifyChAfterId;
Channel notifyChBefore = null;
Channel notifyChAfter = null;
var beforeVch = e.Before.VoiceChannel;
@ -246,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, out notifyChBefore))
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, out notifyChAfter))
if (afterVch != null && config.VoiceChannelLog.TryGetValue(afterVch.Id, out notifyChAfterId) && (notifyChAfter = e.After.Server.TextChannels.FirstOrDefault(tc => tc.Id == notifyChAfterId)) != null)
{
notifyJoin = true;
}
@ -272,8 +291,11 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
try
{
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel;
if (chId == null)
return;
Channel ch;
if (!logs.TryGetValue(e.Server, out ch))
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
string str = $"🕔`{prettyCurrentTime}`";
if (e.Before.Name != e.After.Name)
@ -331,21 +353,36 @@ $@"🕔`{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;
SpecificConfigurations.Default.Of (e.Server.Id).LogServerChannel = null;
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 =>
{
Channel ch;
if (!loggingPresences.TryRemove(e.Server, out ch))
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel;
if (chId == null)
{
loggingPresences.TryAdd(e.Server, e.Channel);
SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel = e.Channel.Id;
await e.Channel.SendMessage($"**User presence notifications enabled.**").ConfigureAwait(false);
return;
}
SpecificConfigurations.Default.Of(e.Server.Id).LogPresenceChannel = null;
await e.Channel.SendMessage($"**User presence notifications disabled.**").ConfigureAwait(false);
});
@ -356,11 +393,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, e.Channel);
config.VoiceChannelLog.TryAdd(voiceChannel.Id, e.Channel.Id);
}
await e.Channel.SendMessage("Started logging user presence for **ALL** voice channels!").ConfigureAwait(false);
return;
@ -371,10 +409,10 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
await e.Channel.SendMessage("💢 You are not in a voice channel right now. If you are, please rejoin it.").ConfigureAwait(false);
return;
}
Channel throwaway;
if (!voiceChannelLog.TryRemove(e.User.VoiceChannel, out throwaway))
ulong throwaway;
if (!config.VoiceChannelLog.TryRemove(e.User.VoiceChannel.Id, out throwaway))
{
voiceChannelLog.TryAdd(e.User.VoiceChannel, e.Channel);
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

View File

@ -51,11 +51,9 @@ namespace NadekoBot.Modules.Administration.Commands
{
if (PlayingPlaceholders.Count == 0
|| NadekoBot.Config.RotatingStatuses.Count == 0
|| i >= PlayingPlaceholders.Count
|| i >= NadekoBot.Config.RotatingStatuses.Count)
{
i = -1;
return;
i = 0;
}
status = NadekoBot.Config.RotatingStatuses[i];
status = PlayingPlaceholders.Aggregate(status,

View File

@ -1,62 +0,0 @@
using Discord.Commands;
using NadekoBot.Modules;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace NadekoBot.Classes.Conversations.Commands
{
internal class CopyCommand : DiscordCommand
{
private readonly HashSet<ulong> CopiedUsers = new HashSet<ulong>();
public CopyCommand(DiscordModule module) : base(module)
{
NadekoBot.Client.MessageReceived += Client_MessageReceived;
}
private async void Client_MessageReceived(object sender, Discord.MessageEventArgs e)
{
if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
try
{
if (string.IsNullOrWhiteSpace(e.Message.Text))
return;
if (CopiedUsers.Contains(e.User.Id))
{
await e.Channel.SendMessage(e.Message.Text).ConfigureAwait(false);
}
}
catch { }
}
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
if (CopiedUsers.Contains(e.User.Id)) return;
CopiedUsers.Add(e.User.Id);
await e.Channel.SendMessage(" I'll start copying you now.").ConfigureAwait(false);
};
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand("copyme")
.Alias("cm")
.Description("Nadeko starts copying everything you say. Disable with cs")
.Do(DoFunc());
cgb.CreateCommand("cs")
.Alias("copystop")
.Description("Nadeko stops copying you")
.Do(StopCopy());
}
private Func<CommandEventArgs, Task> StopCopy() => async e =>
{
if (!CopiedUsers.Contains(e.User.Id)) return;
CopiedUsers.Remove(e.User.Id);
await e.Channel.SendMessage(" I wont copy anymore.").ConfigureAwait(false);
};
}
}

View File

@ -1,7 +1,6 @@
using Discord;
using Discord.Commands;
using Discord.Modules;
using NadekoBot.Classes.Conversations.Commands;
using NadekoBot.DataModels;
using NadekoBot.Extensions;
using NadekoBot.Modules.Conversations.Commands;
@ -19,7 +18,6 @@ namespace NadekoBot.Modules.Conversations
private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥";
public Conversations()
{
commands.Add(new CopyCommand(this));
commands.Add(new RipCommand(this));
}
@ -255,21 +253,6 @@ namespace NadekoBot.Modules.Conversations
await e.Channel.SendMessage(construct).ConfigureAwait(false);
});
cgb.CreateCommand("av")
.Alias("avatar")
.Parameter("mention", ParameterType.Required)
.Description("Shows a mentioned person's avatar.\n**Usage**: ~av @X")
.Do(async e =>
{
var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();
if (usr == null)
{
await e.Channel.SendMessage("Invalid user specified.").ConfigureAwait(false);
return;
}
await e.Channel.SendMessage(await usr.AvatarUrl.ShortenUrl()).ConfigureAwait(false);
});
});
}

View File

@ -79,7 +79,7 @@ namespace NadekoBot.Modules.Gambling
await e.Channel.SendFile(images.Count + " cards.jpg", bitmap.ToStream()).ConfigureAwait(false);
if (cardObjects.Count == 5)
{
await e.Channel.SendMessage(Cards.GetHandValue(cardObjects)).ConfigureAwait(false);
await e.Channel.SendMessage($"{e.User.Mention} `{Cards.GetHandValue(cardObjects)}`").ConfigureAwait(false);
}
}
catch (Exception ex)

View File

@ -1,10 +1,12 @@
using Discord;
using Discord.Commands;
using NadekoBot.Classes;
using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands
@ -18,11 +20,31 @@ namespace NadekoBot.Modules.Games.Commands
/// </summary>
class PlantPick : DiscordCommand
{
private Random rng;
public PlantPick(DiscordModule module) : base(module)
{
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
rng = new Random();
}
private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e)
{
if (e.Server == null || e.Channel.IsPrivate)
return;
var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id);
if (config.GenerateCurrencyChannels.Contains(e.Channel.Id))
{
var rnd = Math.Abs(GetRandomNumber());
if ((rnd % 50) == 0)
{
var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath());
await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`");
plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; });
}
}
}
//channelid/messageid pair
ConcurrentDictionary<ulong, Message> plantedFlowerChannels = new ConcurrentDictionary<ulong, Message>();
@ -65,8 +87,7 @@ namespace NadekoBot.Modules.Games.Commands
return;
}
var rng = new Random();
var file = Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
var file = GetRandomCurrencyImagePath();
Message msg;
//todo send message after, not in lock
if (file == null)
@ -80,6 +101,38 @@ namespace NadekoBot.Modules.Games.Commands
await Task.Delay(20000).ConfigureAwait(false);
await msg2.Delete().ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "gencurrency")
.Alias(Prefix + "gc")
.Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Requires Manage Messages permission. | `>gc`")
.AddCheck(SimpleCheckers.ManageMessages())
.Do(async e =>
{
var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (config.GenerateCurrencyChannels.Remove(e.Channel.Id))
{
await e.Channel.SendMessage("`Currency generation disabled on this channel.`");
}
else
{
config.GenerateCurrencyChannels.Add(e.Channel.Id);
await e.Channel.SendMessage("`Currency generation enabled on this channel.`");
}
});
}
private string GetRandomCurrencyImagePath() =>
Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
int GetRandomNumber()
{
using (RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider())
{
byte[] rno = new byte[4];
rg.GetBytes(rno);
int randomvalue = BitConverter.ToInt32(rno, 0);
return randomvalue;
}
}
}
}

View File

@ -86,7 +86,7 @@ Version: `{NadekoStats.Instance.BotVersion}`";
.Description("Sends a readme and a guide links to the channel.")
.Do(async e =>
await e.Channel.SendMessage(
@"**FULL README**: <https://github.com/Kwoth/NadekoBot/blob/master/README.md>
@"**Wiki with all info**: <https://github.com/Kwoth/NadekoBot/wiki>
**WINDOWS SETUP GUIDE**: <https://github.com/Kwoth/NadekoBot/blob/master/ComprehensiveGuide.md>

View File

@ -1,5 +1,6 @@
using Discord.Commands;
using Discord.Modules;
using NadekoBot.Classes;
using NadekoBot.Classes.Help.Commands;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Classes;
@ -54,10 +55,8 @@ namespace NadekoBot.Modules.Help
}
var i = 0;
if (module != "customreactions" && module != "conversations")
await e.Channel.SendMessage("`List Of Commands:`\n```xl\n" +
string.Join("\n", cmdsArray.GroupBy(item => (i++) / 3)
.Select(ig => string.Join("", ig.Select(el => $"{el.Text,-15}" + $"{"[" + el.Aliases.FirstOrDefault() + "]",-8}"))))
+ $"\n```")
await e.Channel.SendMessage("`List Of Commands:`\n" + SearchHelper.ShowInPrettyCode<Command>(cmdsArray,
el => $"{el.Text,-15}{"[" + el.Aliases.FirstOrDefault() + "]",-8}"))
.ConfigureAwait(false);
else
await e.Channel.SendMessage("`List Of Commands:`\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Text}")));

View File

@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Music.Classes
private bool Destroyed { get; set; } = false;
public bool RepeatSong { get; private set; } = false;
public bool RepeatPlaylist { get; private set; } = false;
public bool Autoplay { get; private set; } = false;
public bool Autoplay { get; set; } = false;
public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume)
{
@ -174,6 +174,7 @@ namespace NadekoBot.Modules.Music.Classes
throw new ArgumentNullException(nameof(s));
lock (playlistLock)
{
s.MusicPlayer = this;
playlist.Add(s);
}
}

View File

@ -54,7 +54,7 @@ namespace NadekoBot.Modules.Music.Classes
}
}
private Song(SongInfo songInfo)
public Song(SongInfo songInfo)
{
this.SongInfo = songInfo;
}
@ -67,6 +67,12 @@ namespace NadekoBot.Modules.Music.Classes
return s;
}
public Song SetMusicPlayer(MusicPlayer mp)
{
this.MusicPlayer = mp;
return this;
}
private Task BufferSong(CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{

View File

@ -1,4 +1,5 @@
using NadekoBot.Classes;
using Newtonsoft.Json;
using System;
using System.Threading.Tasks;
@ -34,18 +35,22 @@ namespace NadekoBot.Modules.Music.Classes
public class SoundCloudVideo
{
public string Kind = "";
public long Id = 0;
public SoundCloudUser User = new SoundCloudUser();
public string Title = "";
public string Kind { get; set; } = "";
public long Id { get; set; } = 0;
public SoundCloudUser User { get; set; } = new SoundCloudUser();
public string Title { get; set; } = "";
[JsonIgnore]
public string FullName => User.Name + " - " + Title;
public bool Streamable = false;
public bool Streamable { get; set; } = false;
[JsonProperty("permalink_url")]
public string TrackLink { get; set; } = "";
[JsonIgnore]
public string StreamLink => $"https://api.soundcloud.com/tracks/{Id}/stream?client_id={NadekoBot.Creds.SoundCloudClientID}";
}
public class SoundCloudUser
{
[Newtonsoft.Json.JsonProperty("username")]
public string Name;
public string Name { get; set; }
}
/*
{"kind":"track",

View File

@ -6,6 +6,7 @@ using NadekoBot.DataModels;
using NadekoBot.Extensions;
using NadekoBot.Modules.Music.Classes;
using NadekoBot.Modules.Permissions.Classes;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -52,30 +53,27 @@ namespace NadekoBot.Modules.Music
cgb.CreateCommand("stop")
.Alias("s")
.Description("Stops the music and clears the playlist. Stays in the channel.\n**Usage**: `!m s`")
.Do(async e =>
.Do(e =>
{
await Task.Run(() =>
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return;
if (e.User.VoiceChannel == musicPlayer.PlaybackVoiceChannel)
{
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) return;
if (e.User.VoiceChannel == musicPlayer.PlaybackVoiceChannel)
musicPlayer.Stop();
}).ConfigureAwait(false);
musicPlayer.Autoplay = false;
musicPlayer.Stop();
}
});
cgb.CreateCommand("destroy")
.Alias("d")
.Description("Completely stops the music and unbinds the bot from the channel. " +
"(may cause weird behaviour)\n**Usage**: `!m d`")
.Do(async e =>
.Do(e =>
{
await Task.Run(() =>
{
MusicPlayer musicPlayer;
if (!MusicPlayers.TryRemove(e.Server, out musicPlayer)) return;
if (e.User.VoiceChannel == musicPlayer.PlaybackVoiceChannel)
musicPlayer.Destroy();
}).ConfigureAwait(false);
MusicPlayer musicPlayer;
if (!MusicPlayers.TryRemove(e.Server, out musicPlayer)) return;
if (e.User.VoiceChannel == musicPlayer.PlaybackVoiceChannel)
musicPlayer.Destroy();
});
cgb.CreateCommand("pause")
@ -110,6 +108,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`")
@ -188,7 +201,7 @@ namespace NadekoBot.Modules.Music
cgb.CreateCommand("defvol")
.Alias("dv")
.Description("Sets the default music volume when music playback is started (0-100)." +
" Does not persist through restarts.\n**Usage**: `!m dv 80`")
" Persists through restarts.\n**Usage**: `!m dv 80`")
.Parameter("val", ParameterType.Required)
.Do(async e =>
{
@ -302,6 +315,37 @@ namespace NadekoBot.Modules.Music
await msg.Edit("🎵 `Playlist queue complete.`").ConfigureAwait(false);
});
cgb.CreateCommand("soundcloudpl")
.Alias("scpl")
.Description("Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony`")
.Parameter("pl", ParameterType.Unparsed)
.Do(async e =>
{
var pl = e.GetArg("pl")?.Trim();
if (string.IsNullOrWhiteSpace(pl))
return;
var scvids = JObject.Parse(await SearchHelper.GetResponseStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Creds.SoundCloudClientID}"))["tracks"].ToObject<SoundCloudVideo[]>();
await QueueSong(e.Channel, e.User.VoiceChannel, scvids[0].TrackLink);
MusicPlayer mp;
if (!MusicPlayers.TryGetValue(e.Server, out mp))
return;
foreach (var svideo in scvids.Skip(1))
{
mp.AddSong(new Song(new Classes.SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
ProviderType = MusicType.Normal,
Query = svideo.TrackLink,
}));
}
});
cgb.CreateCommand("localplaylst")
.Alias("lopl")
.Description("Queues all songs from a directory. **Bot Owner Only!**\n**Usage**: `!m lopl C:/music/classical`")
@ -757,8 +801,6 @@ namespace NadekoBot.Modules.Music
return mp;
});
var resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false);
resolvedSong.MusicPlayer = musicPlayer;
musicPlayer.AddSong(resolvedSong);
if (!silent)
{

View File

@ -0,0 +1,46 @@
using Discord.Commands;
using NadekoBot.Classes;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace NadekoBot.Modules.Searches.Commands
{
class MemegenCommands : DiscordCommand
{
public MemegenCommands(DiscordModule module) : base(module)
{
}
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Prefix + "memelist")
.Description("Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/")
.Do(async e =>
{
int i = 0;
await e.Channel.SendMessage("`List Of Commands:`\n```xl\n" +
string.Join("\n", JsonConvert.DeserializeObject<Dictionary<string, string>>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/"))
.Select(kvp => Path.GetFileName(kvp.Value))
.GroupBy(item => (i++) / 4)
.Select(ig => string.Join("", ig.Select(el => $"{el,-17}"))))
+ $"\n```").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "memegen")
.Description("Generates a meme from memelist with top and bottom text. | `~memegen biw \"gets iced coffee\" \"in the winter\"`")
.Parameter("meme", ParameterType.Required)
.Parameter("toptext", ParameterType.Required)
.Parameter("bottext", ParameterType.Required)
.Do(async e =>
{
var meme = e.GetArg("meme");
var top = Uri.EscapeDataString(e.GetArg("toptext").Replace(' ', '-'));
var bot = Uri.EscapeDataString(e.GetArg("bottext").Replace(' ', '-'));
await e.Channel.SendMessage($"http://memegen.link/{meme}/{top}/{bot}.jpg");
});
}
}
}

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "osu")
.Description("Shows osu stats for a player.\n**Usage**: `~osu Name` or `~osu Name `")
.Description("Shows osu stats for a player.\n**Usage**: `~osu Name` or `~osu Name taiko`")
.Parameter("usr", ParameterType.Required)
.Parameter("mode", ParameterType.Unparsed)
.Do(async e =>

View File

@ -73,7 +73,7 @@ namespace NadekoBot.Modules.Searches.Commands
checkTimer.Start();
}
private async Task<Tuple<bool, string>> GetStreamStatus(StreamNotificationConfig stream)
private async Task<Tuple<bool, string>> GetStreamStatus(StreamNotificationConfig stream, bool checkCache = true)
{
bool isLive;
string response;
@ -83,7 +83,7 @@ namespace NadekoBot.Modules.Searches.Commands
{
case StreamNotificationConfig.StreamType.Hitbox:
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}";
if (cachedStatuses.TryGetValue(hitboxUrl, out result))
if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result))
return result;
response = await SearchHelper.GetResponseStringAsync(hitboxUrl).ConfigureAwait(false);
data = JObject.Parse(response);
@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Searches.Commands
return result;
case StreamNotificationConfig.StreamType.Twitch:
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}";
if (cachedStatuses.TryGetValue(twitchUrl, out result))
if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result))
return result;
response = await SearchHelper.GetResponseStringAsync(twitchUrl).ConfigureAwait(false);
data = JObject.Parse(response);
@ -103,7 +103,7 @@ namespace NadekoBot.Modules.Searches.Commands
return result;
case StreamNotificationConfig.StreamType.Beam:
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}";
if (cachedStatuses.TryGetValue(beamUrl, out result))
if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result))
return result;
response = await SearchHelper.GetResponseStringAsync(beamUrl).ConfigureAwait(false);
data = JObject.Parse(response);
@ -143,6 +143,93 @@ namespace NadekoBot.Modules.Searches.Commands
.Parameter("username", ParameterType.Unparsed)
.Do(TrackStream(StreamNotificationConfig.StreamType.Beam));
cgb.CreateCommand(Module.Prefix + "checkhitbox")
.Alias(Module.Prefix + "chhb")
.Description("Checks if a certain user is streaming on the hitbox platform." +
"\n**Usage**: ~chhb SomeStreamer")
.Parameter("username", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
var stream = e.GetArg("username")?.Trim();
if (string.IsNullOrWhiteSpace(stream))
return;
try
{
var streamStatus = (await GetStreamStatus(new StreamNotificationConfig
{
Username = stream,
Type = StreamNotificationConfig.StreamType.Hitbox
}));
if (streamStatus.Item1)
{
await e.Channel.SendMessage($"`Streamer {streamStatus.Item2} is online.`");
}
}
catch
{
await e.Channel.SendMessage("No channel found.");
}
});
cgb.CreateCommand(Module.Prefix + "checktwitch")
.Alias(Module.Prefix + "chtw")
.Description("Checks if a certain user is streaming on the twitch platform." +
"\n**Usage**: ~chtw SomeStreamer")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(async e =>
{
var stream = e.GetArg("username")?.Trim();
if (string.IsNullOrWhiteSpace(stream))
return;
try
{
var streamStatus = (await GetStreamStatus(new StreamNotificationConfig
{
Username = stream,
Type = StreamNotificationConfig.StreamType.Twitch
}));
if (streamStatus.Item1)
{
await e.Channel.SendMessage($"`Streamer {streamStatus.Item2} is online.`");
}
}
catch
{
await e.Channel.SendMessage("No channel found.");
}
});
cgb.CreateCommand(Module.Prefix + "checkbeam")
.Alias(Module.Prefix + "chbm")
.Description("Checks if a certain user is streaming on the beam platform." +
"\n**Usage**: ~chbm SomeStreamer")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(async e =>
{
var stream = e.GetArg("username")?.Trim();
if (string.IsNullOrWhiteSpace(stream))
return;
try
{
var streamStatus = (await GetStreamStatus(new StreamNotificationConfig
{
Username = stream,
Type = StreamNotificationConfig.StreamType.Beam
}));
if (streamStatus.Item1)
{
await e.Channel.SendMessage($"`Streamer {streamStatus.Item2} is online.`");
}
}
catch
{
await e.Channel.SendMessage("No channel found.");
}
});
cgb.CreateCommand(Module.Prefix + "removestream")
.Alias(Module.Prefix + "rms")
.Description("Removes notifications of a certain streamer on this channel." +

View File

@ -31,6 +31,7 @@ namespace NadekoBot.Modules.Searches
commands.Add(new CalcCommand(this));
commands.Add(new OsuCommands(this));
commands.Add(new PokemonSearchCommands(this));
commands.Add(new MemegenCommands(this));
rng = new Random();
}
@ -184,29 +185,30 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "ir")
.Description("Pulls a random image using a search parameter.\n**Usage**: ~ir cute kitten")
.Parameter("query", ParameterType.Unparsed)
.Do(async e =>
{
if (string.IsNullOrWhiteSpace(e.GetArg("query")))
return;
try
{
var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(e.GetArg("query"))}&cx=018084019232060951019%3Ahs5piey28-e&num=50&searchType=image&start={ rng.Next(1, 50) }&fields=items%2Flink&key={NadekoBot.Creds.GoogleAPIKey}";
var obj = JObject.Parse(await SearchHelper.GetResponseStringAsync(reqString).ConfigureAwait(false));
var items = obj["items"] as JArray;
await e.Channel.SendMessage(items[rng.Next(0, items.Count)]["link"].ToString()).ConfigureAwait(false);
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("403 (Forbidden)"))
{
await e.Channel.SendMessage("Daily limit reached!");
}
else
{
await e.Channel.SendMessage("Something went wrong.");
}
}
});
.Do(async e =>
{
if (string.IsNullOrWhiteSpace(e.GetArg("query")))
return;
try
{
var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(e.GetArg("query"))}&cx=018084019232060951019%3Ahs5piey28-e&num=1&searchType=image&start={ rng.Next(1, 50) }&fields=items%2Flink&key={NadekoBot.Creds.GoogleAPIKey}";
var obj = JObject.Parse(await SearchHelper.GetResponseStringAsync(reqString).ConfigureAwait(false));
var items = obj["items"] as JArray;
await e.Channel.SendMessage(items[0]["link"].ToString()).ConfigureAwait(false);
}
catch (HttpRequestException exception)
{
if (exception.Message.Contains("403 (Forbidden)"))
{
await e.Channel.SendMessage("Daily limit reached!");
}
else
{
await e.Channel.SendMessage("Something went wrong.");
}
}
});
cgb.CreateCommand(Prefix + "lmgtfy")
.Description("Google something for an idiot.")
.Parameter("ffs", ParameterType.Unparsed)
@ -475,6 +477,21 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}
});
cgb.CreateCommand(Prefix + "av")
.Alias(Prefix + "avatar")
.Parameter("mention", ParameterType.Required)
.Description("Shows a mentioned person's avatar.\n**Usage**: ~av @X")
.Do(async e =>
{
var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();
if (usr == null)
{
await e.Channel.SendMessage("Invalid user specified.").ConfigureAwait(false);
return;
}
await e.Channel.SendMessage(await usr.AvatarUrl.ShortenUrl()).ConfigureAwait(false);
});
});
}
}

View File

@ -2,10 +2,12 @@
// License: Code Project Open License
// http://www.codeproject.com/info/cpol10.aspx
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
namespace NadekoBot.Modules.Translator.Helpers
@ -26,32 +28,6 @@ namespace NadekoBot.Modules.Translator.Helpers
return GoogleTranslator._languageModeMap.Keys.OrderBy(p => p);
}
}
/// <summary>
/// Gets the time taken to perform the translation.
/// </summary>
public TimeSpan TranslationTime {
get;
private set;
}
/// <summary>
/// Gets the url used to speak the translation.
/// </summary>
/// <value>The url used to speak the translation.</value>
public string TranslationSpeechUrl {
get;
private set;
}
/// <summary>
/// Gets the error.
/// </summary>
public Exception Error {
get;
private set;
}
#endregion
#region Public methods
@ -63,92 +39,28 @@ namespace NadekoBot.Modules.Translator.Helpers
/// <param name="sourceLanguage">The source language.</param>
/// <param name="targetLanguage">The target language.</param>
/// <returns>The translation.</returns>
public string Translate
public async Task<string> Translate
(string sourceText,
string sourceLanguage,
string targetLanguage)
{
// Initialize
this.Error = null;
this.TranslationSpeechUrl = null;
this.TranslationTime = TimeSpan.Zero;
DateTime tmStart = DateTime.Now;
string translation = string.Empty;
string text = string.Empty;
try
// Download translation
string url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
GoogleTranslator.LanguageEnumToIdentifier(sourceLanguage),
GoogleTranslator.LanguageEnumToIdentifier(targetLanguage),
HttpUtility.UrlEncode(sourceText));
using (HttpClient http = new HttpClient())
{
// Download translation
string url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
GoogleTranslator.LanguageEnumToIdentifier(sourceLanguage),
GoogleTranslator.LanguageEnumToIdentifier(targetLanguage),
HttpUtility.UrlEncode(sourceText));
using (WebClient wc = new WebClient())
{
wc.Headers.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
text = wc.DownloadString(url);
}
// Get translated text
// Get phrase collection
// string text = File.ReadAllText(outputFile);
int index = text.IndexOf(string.Format(",,\"{0}\"", GoogleTranslator.LanguageEnumToIdentifier(sourceLanguage)));
if (index == -1)
{
// Translation of single word
int startQuote = text.IndexOf('\"');
if (startQuote != -1)
{
int endQuote = text.IndexOf('\"', startQuote + 1);
if (endQuote != -1)
{
translation = text.Substring(startQuote + 1, endQuote - startQuote - 1);
}
}
else
{
// Translation of phrase
text = text.Substring(0, index);
text = text.Replace("],[", ",");
text = text.Replace("]", string.Empty);
text = text.Replace("[", string.Empty);
text = text.Replace("\",\"", "\"");
// Get translated phrases
string[] phrases = text.Split(new[] { '\"' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; (i < phrases.Count()); i += 2)
{
string translatedPhrase = phrases[i];
if (translatedPhrase.StartsWith(",,"))
{
i--;
continue;
}
translation += translatedPhrase + " ";
}
}
// Fix up translation
translation = translation.Trim();
translation = translation.Replace(" ?", "?");
translation = translation.Replace(" !", "!");
translation = translation.Replace(" ,", ",");
translation = translation.Replace(" .", ".");
translation = translation.Replace(" ;", ";");
// And translation speech URL
this.TranslationSpeechUrl = string.Format("https://translate.googleapis.com/translate_tts?ie=UTF-8&q={0}&tl={1}&total=1&idx=0&textlen={2}&client=gtx",
HttpUtility.UrlEncode(translation), GoogleTranslator.LanguageEnumToIdentifier(targetLanguage), translation.Length);
}
}
catch (Exception ex)
{
this.Error = ex;
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
text = await http.GetStringAsync(url).ConfigureAwait(false);
}
// Return result
this.TranslationTime = DateTime.Now - tmStart;
return translation;
return JArray.Parse(text)[0][0][0].ToString();
}
#endregion

View File

@ -27,13 +27,17 @@ namespace NadekoBot.Modules.Translator
await e.Channel.SendIsTyping().ConfigureAwait(false);
string from = e.GetArg("langs").ToLowerInvariant().Split('>')[0];
string to = e.GetArg("langs").ToLowerInvariant().Split('>')[1];
var text = e.GetArg("text")?.Trim();
if (string.IsNullOrWhiteSpace(text))
return;
string translation = t.Translate(e.GetArg("text"), from, to);
string translation = await t.Translate(text, from, to).ConfigureAwait(false);
await e.Channel.SendMessage(translation).ConfigureAwait(false);
}
catch
catch (Exception ex)
{
await e.Channel.SendMessage("Bad input format, or sth went wrong...").ConfigureAwait(false);
Console.WriteLine(ex);
await e.Channel.SendMessage("Bad input format, or something went wrong...").ConfigureAwait(false);
}
};

View File

@ -134,6 +134,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Classes\ObservableConcurrentDictionary.cs" />
<Compile Include="Modules\Administration\Commands\AutoAssignRole.cs" />
<Compile Include="Modules\Administration\Commands\CustomReactionsCommands.cs" />
<Compile Include="Modules\Administration\Commands\SelfAssignedRolesCommand.cs" />
@ -149,6 +150,7 @@
<Compile Include="Modules\Searches\Commands\IMDB\ImdbMovie.cs" />
<Compile Include="Modules\Searches\Commands\IMDB\ImdbScraper.cs" />
<Compile Include="Classes\IncidentsHandler.cs" />
<Compile Include="Modules\Searches\Commands\MemegenCommands.cs" />
<Compile Include="Modules\Searches\Commands\OsuCommands.cs" />
<Compile Include="Modules\Searches\Commands\PokemonSearchCommands.cs" />
<Compile Include="Modules\Utility\UtilityModule.cs" />
@ -210,7 +212,6 @@
<Compile Include="Modules\Games\Commands\SpeedTyping.cs" />
<Compile Include="Modules\Gambling\Helpers\Cards.cs" />
<Compile Include="Classes\Extensions.cs" />
<Compile Include="Modules\Conversations\Commands\CopyCommand.cs" />
<Compile Include="Modules\Gambling\DiceRollCommand.cs" />
<Compile Include="Modules\Gambling\DrawCommand.cs" />
<Compile Include="Modules\Gambling\FlipCoinCommand.cs" />
@ -496,7 +497,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="lib\ScaredFingers.UnitsConversion.dll" />
<Content Include="Classes\lib\ScaredFingers.UnitsConversion.dll" />
<None Include="resources\images\rose_overlay.png" />
</ItemGroup>
<ItemGroup />

View File

@ -1,4 +1,5 @@
![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true)
[![Discord](https://discordapp.com/api/servers/117523346618318850/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY)
# NadekoBot
## [Click here to invite nadeko to your discord server](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303)

View File

@ -2,7 +2,7 @@
######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa`
#NadekoBot List Of Commands
Version: `NadekoBot v0.9.6015.37609`
Version: `NadekoBot v0.9.6030.3793`
### Help
Command and aliases | Description | Usage
----------------|--------------|-------
@ -44,24 +44,23 @@ Command and aliases | Description | Usage
`.lsar` | Lists all self-assignable roles.
`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | .iam Gamer
`.iamnot`, `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | .iamn Gamer
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general Start now!`
`.remindmsg` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner Only!**
`.serverinfo`, `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | .sinfo Some Server
`.channelinfo`, `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | .cinfo #some-channel
`.userinfo`, `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | .uinfo @SomeUser
`.addcustreact`, `.acr` | Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** | .acr "hello" I love saying hello to %user%
`.listcustreact`, `.lcr` | Lists all current custom reactions (paginated with 5 commands per page). | .lcr 1
`.listcustreact`, `.lcr` | Lists all current custom reactions (paginated with 30 commands per page). | .lcr 1
`.showcustreact`, `.scr` | Shows all possible responses from a single custom reaction. | .scr %mention% bb
`.editcustreact`, `.ecr` | Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `.ecr "%mention% disguise" 2 Test 123`
`.delcustreact`, `.dcr` | Deletes a custom reaction with given name (and index)
`.autoassignrole`, `.aar` | Automaticaly assigns a specified role to every user who joins the server. Type `.aar` to disable, `.aar Role Name` to enable
`.leave` | Makes Nadeko leave the server. Either name or id required. | `.leave 123123123331`
`.restart` | Restarts the bot. Might not work.
`.listincidents`, `.lin` | List all UNREAD incidents and flags them as read.
`.listallincidents`, `.lain` | Sends you a file containing all incidents and flags them as read.
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only.
`.restart` | Restarts the bot. Might not work. **Bot Owner Only**
`.setrole`, `.sr` | Sets a role for a given user. | .sr @User Guest
`.removerole`, `.rr` | Removes a role from a given user. | .rr @User Admin
`.renamerole`, `.renr` | Renames a role. Role you are renaming must be lower than bot's highest role. | `.renr "First role" SecondRole`
`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | .rar @User
`.createrole`, `.cr` | Creates a role with a given name. | `.r Awesome Role`
`.rolecolor`, `.rc` | Set a role's color to the hex or 0-255 rgb color value provided. | `.color Admin 255 200 100` or `.color Admin ffba55`
`.roles` | List all roles on this server or a single user if specified.
`.ban`, `.b` | Bans a user by id or name with an optional message. | .b "@some Guy" Your behaviour is toxic.
`.softban`, `.sb` | Bans and then unbans a user by id or name with an optional message. | .sb "@some Guy" Your behaviour is toxic.
`.kick`, `.k` | Kicks a mentioned user.
@ -75,30 +74,38 @@ Command and aliases | Description | Usage
`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name.
`.settopic`, `.st` | Sets a topic on the current channel. | `.st My new topic`
`.setchanlname`, `.schn` | Changed the name of the current channel.
`.userid`, `.uid` | Shows user ID.
`.channelid`, `.cid` | Shows current channel ID.
`.serverid`, `.sid` | Shows current server ID.
`.stats` | Shows some basic stats for Nadeko.
`.dysyd` | Shows some basic stats for Nadeko.
`.heap` | Shows allocated memory - **Bot Owner Only!**
`.prune`, `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`
`.die` | Shuts the bot down and notifies users about the restart. **Bot Owner Only!**
`.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!**
`.newavatar`, `.setavatar` | Sets a new avatar image for the NadekoBot. **Bot Owner Only!**
`.newavatar`, `.setavatar` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`
`.setgame` | Sets the bots game. **Bot Owner Only!**
`.checkmyperms` | Checks your userspecific permissions on this channel.
`.commsuser` | Sets a user for through-bot communication. Only works if server is set. Resets commschannel. **Bot Owner Only!**
`.commsserver` | Sets a server for through-bot communication. **Bot Owner Only!**
`.commschannel` | Sets a channel for through-bot communication. Only works if server is set. Resets commsuser. **Bot Owner Only!**
`.send` | Send a message to someone on a different server through the bot. **Bot Owner Only!** | .send Message text multi word!
`.send` | Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!`
`.mentionrole`, `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission.
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server.
`.unstuck` | Clears the message queue. **Bot Owner Only!**
`.donators` | List of lovely people who donated to keep this project alive.
`.donadd` | Add a donator to the database.
`.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | .announce Useless spam
`.whoplays` | Shows a list of users who are playing the specified game.
`.leave` | Leaves a server with a supplied ID. | `.leave 493243292839`
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150`
### Utility
Command and aliases | Description | Usage
----------------|--------------|-------
`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general Start now!`
`.remindmsg` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner Only!**
`.serverinfo`, `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | .sinfo Some Server
`.channelinfo`, `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | .cinfo #some-channel
`.userinfo`, `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | .uinfo @SomeUser
`.whoplays` | Shows a list of users who are playing the specified game.
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server.
`.checkmyperms` | Checks your userspecific permissions on this channel.
`.stats` | Shows some basic stats for Nadeko.
`.dysyd` | Shows some basic stats for Nadeko.
`.userid`, `.uid` | Shows user ID.
`.channelid`, `.cid` | Shows current channel ID.
`.serverid`, `.sid` | Shows current server ID.
`.roles` | List all roles on this server or a single user if specified.
### Permissions
Command and aliases | Description | Usage
@ -145,8 +152,6 @@ Command and aliases | Description | Usage
`..` | Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message
`...` | Shows a random quote with a specified name. | .. abc
`..qdel`, `..quotedelete` | Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc`
`@BotName copyme`, `@BotName cm` | Nadeko starts copying everything you say. Disable with cs
`@BotName cs`, `@BotName copystop` | Nadeko stops copying you
`@BotName rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000
`@BotName uptime` | Shows how long Nadeko has been running for.
`@BotName die` | Works only for the owner. Shuts the bot down.
@ -156,7 +161,6 @@ Command and aliases | Description | Usage
`@BotName slm` | Shows the message where you were last mentioned in this channel (checks last 10k messages)
`@BotName dump` | Dumps all of the invites it can to dump.txt.** Owner Only.**
`@BotName ab` | Try to get 'abalabahaha'
`@BotName av`, `@BotName avatar` | Shows a mentioned person's avatar. | ~av @X
### Gambling
Command and aliases | Description | Usage
@ -186,6 +190,7 @@ Command and aliases | Description | Usage
`>pollend` | Stops active poll on this server and prints the results in this channel.
`>pick` | Picks a flower planted in this channel.
`>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)
`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Requires Manage Messages permission. | `>gc`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello
`>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more
`>8ball` | Ask the 8ball a yes/no question.
@ -200,15 +205,16 @@ Command and aliases | Description | Usage
`!m destroy`, `!m d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d`
`!m pause`, `!m p` | Pauses or Unpauses the song. | `!m p`
`!m queue`, `!m q`, `!m yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!m q Dream Of Venice`
`!m listqueue`, `!m lq` | Lists up to 15 currently queued songs. | `!m lq`
`!m listqueue`, `!m lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2`
`!m nowplaying`, `!m np` | Shows the song currently playing. | `!m np`
`!m volume`, `!m vol` | Sets the music volume 0-100% | `!m vol 50`
`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Does not persist through restarts. | `!m dv 80`
`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80`
`!m mute`, `!m min` | Sets the music volume to 0% | `!m min`
`!m max` | Sets the music volume to 100% (real max is actually 150%). | `!m max`
`!m half` | Sets the music volume to 50%. | `!m half`
`!m shuffle`, `!m sh` | Shuffles the current playlist. | `!m sh`
`!m playlist`, `!m pl` | Queues up to 50 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name`
`!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony`
`!m localplaylst`, `!m lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical`
`!m radio`, `!m ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here`
`!m local`, `!m lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3`
@ -221,8 +227,10 @@ Command and aliases | Description | Usage
`!m save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1`
`!m load` | Loads a playlist under a certain name. | `!m load classical-1`
`!m playlists`, `!m pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1`
`!m deleteplaylist`, `!m delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5`
`!m goto` | Goes to a specific time in seconds in a song.
`!m getlink`, `!m gl` | Shows a link to the currently playing song.
`!m autoplay`, `!m ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty)
### Searches
Command and aliases | Description | Usage
@ -232,13 +240,22 @@ Command and aliases | Description | Usage
`~hitbox`, `~hb` | Notifies this channel when a certain user starts streaming. | ~hitbox SomeStreamer
`~twitch`, `~tw` | Notifies this channel when a certain user starts streaming. | ~twitch SomeStreamer
`~beam`, `~bm` | Notifies this channel when a certain user starts streaming. | ~beam SomeStreamer
`~checkhitbox`, `~chhb` | Checks if a certain user is streaming on the hitbox platform. | ~chhb SomeStreamer
`~checktwitch`, `~chtw` | Checks if a certain user is streaming on the twitch platform. | ~chtw SomeStreamer
`~checkbeam`, `~chbm` | Checks if a certain user is streaming on the beam platform. | ~chbm SomeStreamer
`~removestream`, `~rms` | Removes notifications of a certain streamer on this channel. | ~rms SomeGuy
`~liststreams`, `~ls` | Lists all streams you are following on this server. | ~ls
`~convert` | Convert quantities from>to. Like `~convert m>km 1000`
`~convertlist` | List of the convertable dimensions and currencies.
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes.
`~calculate`, `~calc` | Evaluate a mathematical expression. | ~calc 1+1
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes.
`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`
`~osu b` | Shows information about an osu beatmap. | ~osu b https://osu.ppy.sh/s/127712
`~osu top5` | Displays a user's top 5 plays. | ~osu top5 Name
`~pokemon`, `~poke` | Searches for a pokemon.
`~pokemonability`, `~pokab` | Searches for a pokemon ability.
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
`~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF
`~yt` | Searches youtubes and shows the first result
`~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result.
@ -249,9 +266,6 @@ Command and aliases | Description | Usage
`~ir` | Pulls a random image using a search parameter. | ~ir cute kitten
`~lmgtfy` | Google something for an idiot.
`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | ~hs Ysera
`~osu u` | Shows osu stats for a player. Optional mode. | ~osu u rrtyui std
`~osu b` | Shows osu stats for a beatmap. | ~osu b https://osu.ppy.sh/b/992685
`~osu top5` | Shows an osu player's top 5 plays. Optional mode. | ~osu top5 Dusk ctb
`~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple
`~#` | Searches Tagdef.com for a hashtag. | ~# ff
`~quote` | Shows a random quote.
@ -266,6 +280,7 @@ Command and aliases | Description | Usage
`~wiki` | Gives you back a wikipedia link
`~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00`
`~videocall` | Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message.
`~av`, `~avatar` | Shows a mentioned person's avatar. | ~av @X
### NSFW
Command and aliases | Description | Usage