Merge pull request #9 from Kwoth/dev

Dev
This commit is contained in:
miraai 2016-08-01 20:13:25 +02:00 committed by GitHub
commit 938c8e220d
69 changed files with 769 additions and 4398 deletions

1
.gitignore vendored
View File

@ -22,6 +22,7 @@ obj/
NadekoBot/bin/debug/*.*
NadekoBot/bin/debug/data/permissions
NadekoBot/bin/debug/data/incidents
NadekoBot/bin/debug/data/musicdata
NadekoBot/bin/NadekoRelease/*.*
!NadekoBot/bin/Debug/data/currency_images/*
Tests/bin

47
DockerGuide.md Normal file
View File

@ -0,0 +1,47 @@
## Docker guide with digitalocean
#####Prerequisites:
- Digital ocean account (you can use my reflink to support the project and get 10$ after you register http://m.do.co/c/46b4d3d44795/ )
- Putty (get it here http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
- A bot account - (follow http://discord.kongslien.net/guide.html)
- Common sense
Click on the create droplet button
![img](http://i.imgur.com/g2ayOcC.png)
pick one click apps and select docker on 14.04
![img](http://imgur.com/065Xkme.png)
- pick any droplet size you want (5$ will work ok-ish on a few servers)
- pick location closest to your discord server's location
- Pick a hostname
![img](http://imgur.com/ifPKB6p.png)
- click create
You will get an email from digitalocean with your creds now.
Open putty and type ip adress **you got in your email** with port 22
![img](http://imgur.com/Mh5ehsh.png)
console will open and you will be prompted for a username, type `root`
type in the password you got in the email
confirm the password you just typed in
type in the new password
confirm new password
when you are successfully logged in, type
`docker run --name nadeko -v /nadeko:/config uirel/nadeko`
wait for it to download and at one point it is going to start throwing errors due to credentials.json being empty
CTRL+C to exit that
type `docker stop nadeko`
type `nano /nadeko/credentials.json` and type in your credentials
CTRL+X then CTRL+Y to save
type `docker start nadeko`
Your bot is running, enjoy
*When you want to update the bot, just type `docker restart nadeko` as it always downloads latest prerelease*

View File

@ -13,165 +13,101 @@ namespace NadekoBot.Classes
private string FilePath { get; } = "data/nadekobot.sqlite";
public SQLiteConnection Connection { get; set; }
static DbHandler() { }
public DbHandler()
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.CreateTable<Stats>();
conn.CreateTable<Command>();
conn.CreateTable<Announcement>();
conn.CreateTable<Request>();
conn.CreateTable<TypingArticle>();
conn.CreateTable<CurrencyState>();
conn.CreateTable<CurrencyTransaction>();
conn.CreateTable<Donator>();
conn.CreateTable<UserPokeTypes>();
conn.CreateTable<UserQuote>();
conn.CreateTable<Reminder>();
conn.CreateTable<SongInfo>();
conn.CreateTable<PlaylistSongInfo>();
conn.CreateTable<MusicPlaylist>();
conn.CreateTable<Incident>();
conn.Execute(Queries.TransactionTriggerQuery);
Connection = new SQLiteConnection(FilePath);
Connection.CreateTable<Stats>();
Connection.CreateTable<Command>();
Connection.CreateTable<Announcement>();
Connection.CreateTable<Request>();
Connection.CreateTable<TypingArticle>();
Connection.CreateTable<CurrencyState>();
Connection.CreateTable<CurrencyTransaction>();
Connection.CreateTable<Donator>();
Connection.CreateTable<UserPokeTypes>();
Connection.CreateTable<UserQuote>();
Connection.CreateTable<Reminder>();
Connection.CreateTable<SongInfo>();
Connection.CreateTable<PlaylistSongInfo>();
Connection.CreateTable<MusicPlaylist>();
Connection.CreateTable<Incident>();
Connection.Execute(Queries.TransactionTriggerQuery);
try
{
conn.Execute(Queries.DeletePlaylistTriggerQuery);
Connection.Execute(Queries.DeletePlaylistTriggerQuery);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
internal T FindOne<T>(Expression<Func<T, bool>> p) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Table<T>().Where(p).FirstOrDefault();
}
return Connection.Table<T>().Where(p).FirstOrDefault();
}
internal IList<T> FindAll<T>(Expression<Func<T, bool>> p) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Table<T>().Where(p).ToList();
}
}
internal void DeleteAll<T>() where T : IDataModel
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.DeleteAll<T>();
}
return Connection.Table<T>().Where(p).ToList();
}
internal void DeleteWhere<T>(Expression<Func<T, bool>> p) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
var id = conn.Table<T>().Where(p).FirstOrDefault()?.Id;
var id = Connection.Table<T>().Where(p).FirstOrDefault()?.Id;
if (id.HasValue)
conn.Delete<T>(id);
}
}
internal void InsertData<T>(T o) where T : IDataModel
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.Insert(o, typeof(T));
}
}
internal void InsertMany<T>(T objects) where T : IEnumerable<IDataModel>
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.InsertAll(objects);
}
}
internal void UpdateData<T>(T o) where T : IDataModel
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.Update(o, typeof(T));
}
}
internal void UpdateAll<T>(IEnumerable<T> objs) where T : IDataModel
{
using (var conn = new SQLiteConnection(FilePath))
{
conn.UpdateAll(objs);
}
Connection.Delete<T>(id);
}
internal HashSet<T> GetAllRows<T>() where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
return new HashSet<T>(conn.Table<T>());
}
return new HashSet<T>(Connection.Table<T>());
}
internal CurrencyState GetStateByUserId(long id)
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Table<CurrencyState>().Where(x => x.UserId == id).FirstOrDefault();
}
return Connection.Table<CurrencyState>().Where(x => x.UserId == id).FirstOrDefault();
}
internal T Delete<T>(int id) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
var found = conn.Find<T>(id);
var found = Connection.Find<T>(id);
if (found != null)
conn.Delete<T>(found.Id);
Connection.Delete<T>(found.Id);
return found;
}
}
/// <summary>
/// Updates an existing object or creates a new one
/// </summary>
internal void Save<T>(T o) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
var found = conn.Find<T>(o.Id);
var found = Connection.Find<T>(o.Id);
if (found == null)
conn.Insert(o, typeof(T));
Connection.Insert(o, typeof(T));
else
conn.Update(o, typeof(T));
}
Connection.Update(o, typeof(T));
}
/// <summary>
/// Updates an existing object or creates a new one
/// </summary>
internal void SaveAll<T>(IEnumerable<T> ocol) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
foreach (var o in ocol)
conn.InsertOrReplace(o);
}
Connection.InsertOrReplace(o);
}
internal T GetRandom<T>(Expression<Func<T, bool>> p) where T : IDataModel, new()
{
using (var conn = new SQLiteConnection(FilePath))
{
var r = new Random();
return conn.Table<T>().Where(p).ToList().OrderBy(x => r.Next()).FirstOrDefault();
}
return Connection.Table<T>().Where(p).ToList().OrderBy(x => r.Next()).FirstOrDefault();
}
/// <summary>
///
@ -180,24 +116,19 @@ namespace NadekoBot.Classes
/// <returns></returns>
internal List<PlaylistData> GetPlaylistData(int num)
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Query<PlaylistData>(
return Connection.Query<PlaylistData>(
@"SELECT mp.Name as 'Name',mp.Id as 'Id', mp.CreatorName as 'Creator', Count(*) as 'SongCnt' FROM MusicPlaylist as mp
INNER JOIN PlaylistSongInfo as psi
ON mp.Id = psi.PlaylistId
Group BY mp.Name
Order By mp.DateAdded desc
Limit 20 OFFSET ?", num * 20);
}
}
internal IEnumerable<CurrencyState> GetTopRichest(int n = 10)
{
using (var conn = new SQLiteConnection(FilePath))
{
return conn.Table<CurrencyState>().OrderByDescending(cs => cs.Value).Take(n).ToList();
}
return Connection.Table<CurrencyState>().OrderByDescending(cs => cs.Value).Take(n).ToList();
}
}
}

View File

@ -10,7 +10,7 @@ namespace NadekoBot.Classes
return;
await Task.Run(() =>
{
DbHandler.Instance.InsertData(new DataModels.CurrencyTransaction
DbHandler.Instance.Connection.Insert(new DataModels.CurrencyTransaction
{
Reason = reason,
UserId = (long)u.Id,
@ -36,7 +36,7 @@ namespace NadekoBot.Classes
if (state.Value < amount)
return false;
DbHandler.Instance.InsertData(new DataModels.CurrencyTransaction
DbHandler.Instance.Connection.Insert(new DataModels.CurrencyTransaction
{
Reason = reason,
UserId = (long)u.Id,

View File

@ -19,7 +19,7 @@ namespace NadekoBot.Classes
Read = false
};
DbHandler.Instance.InsertData<Incident>(incident);
DbHandler.Instance.Connection.Insert(incident, typeof(Incident));
}
}
}

View File

@ -46,20 +46,7 @@ namespace NadekoBot
commandService.CommandExecuted += StatsCollector_RanCommand;
commandService.CommandFinished += CommandService_CommandFinished;
commandService.CommandErrored += (s, e) =>
{
try
{
if (e.ErrorType == CommandErrorType.Exception)
File.AppendAllText("errors.txt", $@"Command: {e.Command}
{e.Exception}
-------------------------------------
");
}
catch {
Console.WriteLine("Command errored errorring");
}
};
commandService.CommandErrored += CommandService_CommandFinished;
Task.Run(StartCollecting);
@ -208,7 +195,7 @@ namespace NadekoBot
.ConfigureAwait(false);
var connectedServers = NadekoBot.Client.Servers.Count();
Classes.DbHandler.Instance.InsertData(new DataModels.Stats
Classes.DbHandler.Instance.Connection.Insert(new DataModels.Stats
{
OnlineUsers = onlineUsers,
RealOnlineUsers = realOnlineUsers,
@ -236,11 +223,32 @@ namespace NadekoBot
private void CommandService_CommandFinished(object sender, CommandEventArgs e)
{
DateTime dt;
if (!commandTracker.TryGetValue(e.Message.Id, out dt))
return;
try
{
if (e is CommandErrorEventArgs)
{
var er = e as CommandErrorEventArgs;
if (er.ErrorType == CommandErrorType.Exception)
{
File.AppendAllText("errors.txt", $@"Command: {er.Command}
{er.Exception}
-------------------------------------
");
Console.WriteLine($">>COMMAND ERRORED after *{(DateTime.UtcNow - dt).TotalSeconds}s*\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----");
}
}
else
{
Console.WriteLine($">>COMMAND ENDED after *{(DateTime.UtcNow - dt).TotalSeconds}s*\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----");
}
}
catch { }
}
private async void StatsCollector_RanCommand(object sender, CommandEventArgs e)
{
@ -252,7 +260,7 @@ namespace NadekoBot
try
{
commandsRan++;
Classes.DbHandler.Instance.InsertData(new DataModels.Command
Classes.DbHandler.Instance.Connection.Insert(new DataModels.Command
{
ServerId = (long)(e.Server?.Id ?? 0),
ServerName = e.Server?.Name ?? "--Direct Message--",

View File

@ -25,16 +25,13 @@ namespace NadekoBot.Classes
{
private static DateTime lastRefreshed = DateTime.MinValue;
private static string token { get; set; } = "";
private static readonly HttpClient httpClient = new HttpClient();
public static async Task<Stream> GetResponseStreamAsync(string url,
IEnumerable<KeyValuePair<string, string>> headers = null, RequestHttpMethod method = RequestHttpMethod.Get)
{
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentNullException(nameof(url));
//if its a post or there are no headers, use static httpclient
// if there are headers and it's get, it's not threadsafe
var cl = headers == null || method == RequestHttpMethod.Post ? httpClient : new HttpClient();
var cl = new HttpClient();
cl.DefaultRequestHeaders.Clear();
switch (method)
{
@ -178,7 +175,7 @@ namespace NadekoBot.Classes
return null;
}
public static async Task<string> GetRelatedVideoId(string id)
public static async Task<IEnumerable<string>> GetRelatedVideoIds(string id, int count = 1)
{
if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id));
@ -189,20 +186,14 @@ namespace NadekoBot.Classes
}
var response = await GetResponseStringAsync(
$"https://www.googleapis.com/youtube/v3/search?" +
$"part=snippet&maxResults=1&type=video" +
$"part=snippet&maxResults={count}&type=video" +
$"&relatedToVideoId={id}" +
$"&key={NadekoBot.Creds.GoogleAPIKey}").ConfigureAwait(false);
JObject obj = JObject.Parse(response);
var data = JsonConvert.DeserializeObject<YoutubeVideoSearch>(response);
if (data.items.Length > 0)
{
var toReturn = "http://www.youtube.com/watch?v=" + data.items[0].id.videoId.ToString();
return toReturn;
}
else
return null;
return data.items.Select(v => "http://www.youtube.com/watch?v=" + v.id.videoId);
}
public static async Task<string> GetPlaylistIdByKeyword(string query)

View File

@ -64,7 +64,7 @@ namespace NadekoBot.Modules.Administration
commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand(Prefix + "delmsgoncmd")
.Description("Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only.")
.Description($"Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only. | `{Prefix}delmsgoncmd`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
@ -79,7 +79,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "restart")
.Description("Restarts the bot. Might not work. **Bot Owner Only**")
.Description($"Restarts the bot. Might not work. **Bot Owner Only** | `{Prefix}restart`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Administration
{
if (!e.User.ServerPermissions.MuteMembers)
{
await e.Channel.SendMessage("You do not have permission to do that.").ConfigureAwait(false);
await e.Channel.SendMessage("I most likely don't have the permission necessary for that.").ConfigureAwait(false);
return;
}
if (!e.Message.MentionedUsers.Any())
@ -421,7 +421,7 @@ namespace NadekoBot.Modules.Administration
}
catch
{
await e.Channel.SendMessage("I do not have permission to do that most likely.").ConfigureAwait(false);
await e.Channel.SendMessage("I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
});
@ -447,7 +447,7 @@ namespace NadekoBot.Modules.Administration
}
catch
{
await e.Channel.SendMessage("I do not have permission to do that most likely.").ConfigureAwait(false);
await e.Channel.SendMessage("I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
});
@ -474,7 +474,7 @@ namespace NadekoBot.Modules.Administration
}
catch
{
await e.Channel.SendMessage("I do not have permission to do that most likely.").ConfigureAwait(false);
await e.Channel.SendMessage("I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
});
@ -501,7 +501,7 @@ namespace NadekoBot.Modules.Administration
}
catch
{
await e.Channel.SendMessage("I do not have permission to do that most likely.").ConfigureAwait(false);
await e.Channel.SendMessage("I most likely don't have the permission necessary for that.").ConfigureAwait(false);
}
});
@ -617,7 +617,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "heap")
.Description("Shows allocated memory - **Bot Owner Only!**")
.Description($"Shows allocated memory - **Bot Owner Only!** | `{Prefix}heap`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -628,19 +628,19 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "prune")
.Alias(Prefix + "clr")
.Description(
"`.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`")
"`.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. " +
$"| `{Prefix}prune` or `{Prefix}prune 5` or `{Prefix}prune @Someone` or `{Prefix}prune @Someone X`")
.Parameter("user_or_num", ParameterType.Optional)
.Parameter("num", ParameterType.Optional)
.Do(async e =>
{
Message[] msgs;
if (string.IsNullOrWhiteSpace(e.GetArg("user_or_num"))) // if nothing is set, clear nadeko's messages, no permissions required
{
msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false));//.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray();
msgs = msgs.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray();
if (!msgs.Any())
var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User?.Id == e.Server.CurrentUser.Id)?.ToArray();
if (msgs == null || !msgs.Any())
return;
await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
var toDelete = msgs as Message[] ?? msgs.ToArray();
await e.Channel.DeleteMessages(toDelete).ConfigureAwait(false);
return;
}
if (!e.User.GetPermissions(e.Channel).ManageMessages)
@ -667,14 +667,14 @@ namespace NadekoBot.Modules.Administration
val = 100;
if (!int.TryParse(e.GetArg("num"), out val))
val = 100;
msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == usr.Id).Take(val).ToArray();
if (!msgs.Any())
var mesgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User?.Id == usr.Id).Take(val);
if (mesgs == null || !mesgs.Any())
return;
await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
await e.Channel.DeleteMessages(mesgs as Message[] ?? mesgs.ToArray()).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "die")
.Description("Shuts the bot down and notifies users about the restart. **Bot Owner Only!**")
.Description($"Shuts the bot down and notifies users about the restart. **Bot Owner Only!** | `{Prefix}die`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -807,7 +807,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "unstuck")
.Description("Clears the message queue. **Bot Owner Only!**")
.Description($"Clears the message queue. **Bot Owner Only!** | `{Prefix}unstuck`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(e =>
{
@ -829,7 +829,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "donadd")
.Description($"Add a donator to the database. | `.donadd Donate Amount`")
.Description($"Add a donator to the database. | `{Prefix}donadd Donate Amount`")
.Parameter("donator")
.Parameter("amount")
.AddCheck(SimpleCheckers.OwnerOnly())
@ -842,7 +842,7 @@ namespace NadekoBot.Modules.Administration
if (donator == null) return;
try
{
DbHandler.Instance.InsertData(new Donator
DbHandler.Instance.Connection.Insert(new Donator
{
Amount = amount,
UserName = donator.Name,

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Administration.Commands
{
cgb.CreateCommand(Module.Prefix + "autoassignrole")
.Alias(Module.Prefix + "aar")
.Description($"Automaticaly assigns a specified role to every user who joins the server. Type `.aar` to disable, `.aar Role Name` to enable")
.Description($"Automaticaly assigns a specified role to every user who joins the server. |`{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
.Parameter("role", ParameterType.Unparsed)
.AddCheck(new SimpleCheckers.ManageRoles())
.Do(async e =>

View File

@ -64,8 +64,8 @@ namespace NadekoBot.Modules.Administration.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "scsc")
.Description("Starts an instance of cross server channel. You will get a token as a DM" +
"that other people will use to tune in to the same instance")
.Description("Starts an instance of cross server channel. You will get a token as a DM " +
$"that other people will use to tune in to the same instance. | `{Prefix}scsc`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -79,7 +79,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "jcsc")
.Description("Joins current channel to an instance of cross server channel using the token.")
.Description($"Joins current channel to an instance of cross server channel using the token. | `{Prefix}jcsc`")
.Parameter("token")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
@ -95,7 +95,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "lcsc")
.Description("Leaves Cross server channel instance from this channel")
.Description($"Leaves Cross server channel instance from this channel. | `{Prefix}lcsc`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{

View File

@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Prefix + "addcustreact")
.Alias(Prefix + "acr")
.Description($"Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** | {Prefix}acr \"hello\" I love saying hello to %user%")
.Description($"Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** | `{Prefix}acr \"hello\" I love saying hello to %user%`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Parameter("name", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Prefix + "listcustreact")
.Alias(Prefix + "lcr")
.Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. |{Prefix}lcr 1")
.Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. |`{Prefix}lcr 1`")
.Parameter("num", ParameterType.Required)
.Do(async e =>
{
@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Prefix + "showcustreact")
.Alias(Prefix + "scr")
.Description($"Shows all possible responses from a single custom reaction. |{Prefix}scr %mention% bb")
.Description($"Shows all possible responses from a single custom reaction. |`{Prefix}scr %mention% bb`")
.Parameter("name", ParameterType.Unparsed)
.Do(async e =>
{
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Administration.Commands
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`")
.Description($"Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `{Prefix}ecr \"%mention% disguise\" 2 Test 123`")
.Parameter("name", ParameterType.Required)
.Parameter("index", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Prefix + "delcustreact")
.Alias(Prefix + "dcr")
.Description("Deletes a custom reaction with given name (and index)")
.Description($"Deletes a custom reaction with given name (and index). | `{Prefix}dcr index`")
.Parameter("name", ParameterType.Required)
.Parameter("index", ParameterType.Optional)
.AddCheck(SimpleCheckers.OwnerOnly())

View File

@ -14,26 +14,26 @@ namespace NadekoBot.Modules.Administration.Commands
{
cgb.CreateCommand(Module.Prefix + "listincidents")
.Alias(Prefix + "lin")
.Description("List all UNREAD incidents and flags them as read.")
.Description($"List all UNREAD incidents and flags them as read. | `{Prefix}lin`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
var sid = (long)e.Server.Id;
var incs = DbHandler.Instance.FindAll<Incident>(i => i.ServerId == sid && i.Read == false);
DbHandler.Instance.UpdateAll<Incident>(incs.Select(i => { i.Read = true; return i; }));
DbHandler.Instance.Connection.UpdateAll(incs.Select(i => { i.Read = true; return i; }));
await e.User.SendMessage(string.Join("\n----------------------", incs.Select(i => i.Text)));
});
cgb.CreateCommand(Module.Prefix + "listallincidents")
.Alias(Prefix + "lain")
.Description("Sends you a file containing all incidents and flags them as read.")
.Description($"Sends you a file containing all incidents and flags them as read. | `{Prefix}lain`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
var sid = (long)e.Server.Id;
var incs = DbHandler.Instance.FindAll<Incident>(i => i.ServerId == sid);
DbHandler.Instance.UpdateAll<Incident>(incs.Select(i => { i.Read = true; return i; }));
DbHandler.Instance.Connection.UpdateAll(incs.Select(i => { i.Read = true; return i; }));
var data = string.Join("\n----------------------\n", incs.Select(i => i.Text));
MemoryStream ms = new MemoryStream();
var sw = new StreamWriter(ms);

View File

@ -4,7 +4,10 @@ using NadekoBot.Classes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Commands
{
@ -12,6 +15,8 @@ namespace NadekoBot.Modules.Administration.Commands
{
private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
private ConcurrentBag<KeyValuePair<Channel, string>> voicePresenceUpdates = new ConcurrentBag<KeyValuePair<Channel, string>>();
public LogCommand(DiscordModule module) : base(module)
{
NadekoBot.Client.MessageReceived += MsgRecivd;
@ -45,6 +50,36 @@ namespace NadekoBot.Modules.Administration.Commands
}
catch { }
};
// start the userpresence queue
NadekoBot.OnReady += () => Task.Run(async () =>
{
while (true)
{
var toSend = new Dictionary<Channel, string>();
//take everything from the queue and merge the messages which are going to the same channel
KeyValuePair<Channel, string> item;
while (voicePresenceUpdates.TryTake(out item))
{
if (toSend.ContainsKey(item.Key))
{
toSend[item.Key] = toSend[item.Key] + Environment.NewLine + item.Value;
}
else
{
toSend.Add(item.Key, item.Value);
}
}
//send merged messages to each channel
foreach (var k in toSend)
{
try { await k.Key.SendMessage(Environment.NewLine + k.Value).ConfigureAwait(false); } catch { }
}
await Task.Delay(5000);
}
});
}
private async void ChannelUpdated(object sender, ChannelUpdatedEventArgs e)
@ -232,7 +267,7 @@ namespace NadekoBot.Modules.Administration.Commands
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
await ch.SendMessage(
$@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
$@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
👤`{e.User?.ToString() ?? ("NULL")}`
`Old:` {e.Before.Text.Unmention()}
`New:` {e.After.Text.Unmention()}").ConfigureAwait(false);
@ -252,7 +287,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
{
if (e.Before.Status != e.After.Status)
{
await ch.SendMessage($"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**.").ConfigureAwait(false);
voicePresenceUpdates.Add(new KeyValuePair<Channel, string>(ch, $"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**."));
}
}
}
@ -340,7 +375,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
{
cgb.CreateCommand(Module.Prefix + "spmom")
.Description("Toggles whether mentions of other offline users on your server will send a pm to them.")
.Description($"Toggles whether mentions of other offline users on your server will send a pm to them. | `{Prefix}spmom`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
@ -356,7 +391,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
});
cgb.CreateCommand(Module.Prefix + "logserver")
.Description("Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!**")
.Description($"Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!** | `{Prefix}logserver`")
.AddCheck(SimpleCheckers.OwnerOnly())
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
@ -372,13 +407,13 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
return;
SpecificConfigurations.Default.Of (e.Server.Id).LogServerChannel = null;
SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel = null;
await e.Channel.SendMessage($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "logignore")
.Description($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.")
.Description($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. | `{Prefix}logignore`")
.AddCheck(SimpleCheckers.OwnerOnly())
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
@ -396,7 +431,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
});
cgb.CreateCommand(Module.Prefix + "userpresence")
.Description("Starts logging to this channel when someone from the server goes online/offline/idle.")
.Description($"Starts logging to this channel when someone from the server goes online/offline/idle. | `{Prefix}userpresence`")
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
{
@ -412,7 +447,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
});
cgb.CreateCommand(Module.Prefix + "voicepresence")
.Description("Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now.")
.Description("Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now. | `{Prefix}voicerpresence`")
.Parameter("all", ParameterType.Optional)
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "repeatinvoke")
.Alias(Module.Prefix + "repinv")
.Description("Immediately shows the repeat message and restarts the timer.")
.Description($"Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`")
.AddCheck(SimpleCheckers.ManageMessages())
.Do(async e =>
{
@ -73,7 +73,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "repeat")
.Description("Repeat a message every X minutes. If no parameters are specified, " +
"repeat is disabled. Requires manage messages. |`.repeat 5 Hello there`")
$"repeat is disabled. Requires manage messages. |`{Prefix}repeat 5 Hello there`")
.Parameter("minutes", ParameterType.Optional)
.Parameter("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.ManageMessages())

View File

@ -96,14 +96,14 @@ namespace NadekoBot.Modules.Administration.Commands
{
cgb.CreateCommand(Module.Prefix + "rotateplaying")
.Alias(Module.Prefix + "ropl")
.Description("Toggles rotation of playing status of the dynamic strings you specified earlier.")
.Description($"Toggles rotation of playing status of the dynamic strings you specified earlier. | `{Prefix}ropl`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(DoFunc());
cgb.CreateCommand(Module.Prefix + "addplaying")
.Alias(Module.Prefix + "adpl")
.Description("Adds a specified string to the list of playing strings to rotate. " +
"Supported placeholders: " + string.Join(", ", PlayingPlaceholders.Keys))
"Supported placeholders: " + string.Join(", ", PlayingPlaceholders.Keys)+ $" | `{Prefix}adpl`")
.Parameter("text", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -126,7 +126,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "listplaying")
.Alias(Module.Prefix + "lipl")
.Description("Lists all playing statuses with their corresponding number.")
.Description($"Lists all playing statuses with their corresponding number. | `{Prefix}lipl`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
{
@ -143,7 +143,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "removeplaying")
.Alias(Module.Prefix + "repl", Module.Prefix + "rmpl")
.Description("Removes a playing string on a given number.")
.Description($"Removes a playing string on a given number. | `{Prefix}rmpl`")
.Parameter("number", ParameterType.Required)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>

View File

@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Administration.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "slowmode")
.Description("Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds.")
.Description($"Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds. | `{Prefix}slowmode`")
.AddCheck(SimpleCheckers.ManageMessages())
.Do(async e =>
{

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Administration.Commands
{
cgb.CreateCommand(Module.Prefix + "asar")
.Description("Adds a role, or list of roles separated by whitespace" +
"(use quotations for multiword roles) to the list of self-assignable roles. | .asar Gamer")
$"(use quotations for multiword roles) to the list of self-assignable roles. | {Prefix}asar Gamer")
.Parameter("roles", ParameterType.Multiple)
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "rsar")
.Description("Removes a specified role from the list of self-assignable roles.")
.Description($"Removes a specified role from the list of self-assignable roles. | `{Prefix}rsar`")
.Parameter("role", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
@ -69,14 +69,14 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "lsar")
.Description("Lists all self-assignable roles.")
.Description($"Lists all self-assignable roles. | `{Prefix}lsar`")
.Parameter("roles", ParameterType.Multiple)
.Do(async e =>
{
var config = SpecificConfigurations.Default.Of(e.Server.Id);
var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n");
var toRemove = new HashSet<ulong>();
foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r=>r.ToString()))
foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r => r.ToString()))
{
var role = e.Server.GetRole(roleId);
if (role == null)
@ -98,8 +98,8 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "togglexclsar").Alias(Module.Prefix +"tesar")
.Description("toggle whether the self-assigned roles should be exclusive")
cgb.CreateCommand(Module.Prefix + "togglexclsar").Alias(Module.Prefix + "tesar")
.Description($"toggle whether the self-assigned roles should be exclusive | `{Prefix}tesar`")
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
{
@ -112,7 +112,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "iam")
.Description("Adds a role to you that you choose. " +
"Role must be on a list of self-assignable roles." +
" | .iam Gamer")
$" | {Prefix}iam Gamer")
.Parameter("role", ParameterType.Unparsed)
.Do(async e =>
{
@ -146,12 +146,13 @@ namespace NadekoBot.Modules.Administration.Commands
{
await e.User.AddRoles(role).ConfigureAwait(false);
}
catch(HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
}
catch (Exception)
catch (Exception ex)
{
await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
return;
}
var msg = await e.Channel.SendMessage($":ok:You now have {role.Name} role.").ConfigureAwait(false);
await Task.Delay(3000).ConfigureAwait(false);
@ -167,7 +168,7 @@ namespace NadekoBot.Modules.Administration.Commands
.Alias(Module.Prefix + "iamn")
.Description("Removes a role to you that you choose. " +
"Role must be on a list of self-assignable roles." +
" | .iamn Gamer")
$" | {Prefix}iamn Gamer")
.Parameter("role", ParameterType.Unparsed)
.Do(async e =>
{

View File

@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Administration.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "leave")
.Description("Makes Nadeko leave the server. Either name or id required. | `.leave 123123123331`")
.Description($"Makes Nadeko leave the server. Either name or id required. | `{Prefix}leave 123123123331`")
.Parameter("arg", ParameterType.Required)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>

View File

@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Administration.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "grdel")
.Description("Toggles automatic deletion of greet and bye messages.")
.Description($"Toggles automatic deletion of greet and bye messages. | `{Prefix}grdel`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageServer) return;
@ -232,7 +232,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "greet")
.Description("Toggles anouncements on the current channel when someone joins the server.")
.Description($"Toggles anouncements on the current channel when someone joins the server. | `{Prefix}greet`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageServer) return;
@ -245,7 +245,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "greetmsg")
.Description("Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. | .greetmsg Welcome to the server, %user%.")
.Description($"Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. | `{Prefix}greetmsg Welcome to the server, %user%.`")
.Parameter("msg", ParameterType.Unparsed)
.Do(async e =>
{
@ -265,7 +265,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "bye")
.Description("Toggles anouncements on the current channel when someone leaves the server.")
.Description($"Toggles anouncements on the current channel when someone leaves the server. | `{Prefix}bye`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageServer) return;
@ -278,7 +278,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "byemsg")
.Description("Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message. | .byemsg %user% has left the server.")
.Description($"Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message. | `{Prefix}byemsg %user% has left the server.`")
.Parameter("msg", ParameterType.Unparsed)
.Do(async e =>
{
@ -297,7 +297,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "byepm")
.Description("Toggles whether the good bye messages will be sent in a PM or in the text channel.")
.Description($"Toggles whether the good bye messages will be sent in a PM or in the text channel. | `{Prefix}byepm`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageServer) return;
@ -313,7 +313,7 @@ namespace NadekoBot.Modules.Administration.Commands
});
cgb.CreateCommand(Module.Prefix + "greetpm")
.Description("Toggles whether the greet messages will be sent in a PM or in the text channel.")
.Description($"Toggles whether the greet messages will be sent in a PM or in the text channel. | `{Prefix}greetpm`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageServer) return;

View File

@ -1,46 +0,0 @@
using Discord;
using Discord.Commands;
using NadekoBot.Classes;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Commands
{
internal class VoiceNotificationCommand : DiscordCommand
{
//voicechannel/text channel
private readonly ConcurrentDictionary<Channel, Channel> subscribers = new ConcurrentDictionary<Channel, Channel>();
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
var arg = e.GetArg("voice_name");
if (string.IsNullOrWhiteSpace("voice_name"))
return;
var voiceChannel = e.Server.FindChannels(arg, ChannelType.Voice).FirstOrDefault();
if (voiceChannel == null)
return;
if (subscribers.ContainsKey(voiceChannel))
{
await e.Channel.SendMessage("`Voice channel notifications disabled.`").ConfigureAwait(false);
return;
}
if (subscribers.TryAdd(voiceChannel, e.Channel))
{
await e.Channel.SendMessage("`Voice channel notifications enabled.`").ConfigureAwait(false);
}
};
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "voicenotif")
.Description("Enables notifications on who joined/left the voice channel. |.voicenotif Karaoke club")
.Parameter("voice_name", ParameterType.Unparsed)
.Do(DoFunc());
}
public VoiceNotificationCommand(DiscordModule module) : base(module) { }
}
}

View File

@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Administration.Commands
{
cgb.CreateCommand(Module.Prefix + "cleanv+t")
.Alias(Module.Prefix + "cv+t")
.Description("Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk.**")
.Description($"Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk.** | `{Prefix}cleanv+t`")
.AddCheck(SimpleCheckers.CanManageRoles)
.AddCheck(SimpleCheckers.ManageChannels())
.Do(async e =>
@ -120,7 +120,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "voice+text")
.Alias(Module.Prefix + "v+t")
.Description("Creates a text channel for each voice channel only users in that voice channel can see." +
"If you are server owner, keep in mind you will see them all the time regardless.")
$"If you are server owner, keep in mind you will see them all the time regardless. | `{Prefix}voice+text`")
.AddCheck(SimpleCheckers.ManageChannels())
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>

View File

@ -8,7 +8,6 @@ using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -42,7 +41,7 @@ namespace NadekoBot.Modules.Conversations
if (string.IsNullOrWhiteSpace(text))
return;
await Task.Run(() =>
Classes.DbHandler.Instance.InsertData(new DataModels.UserQuote()
Classes.DbHandler.Instance.Connection.Insert(new DataModels.UserQuote()
{
DateAdded = DateTime.Now,
Keyword = e.GetArg("keyword").ToLowerInvariant(),
@ -102,7 +101,7 @@ namespace NadekoBot.Modules.Conversations
commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand("die")
.Description("Works only for the owner. Shuts the bot down.")
.Description("Works only for the owner. Shuts the bot down. | `@NadekoBot die`")
.Do(async e =>
{
if (NadekoBot.IsOwner(e.User.Id))
@ -119,7 +118,7 @@ namespace NadekoBot.Modules.Conversations
randServerSw.Start();
cgb.CreateCommand("do you love me")
.Description("Replies with positive answer only to the bot owner.")
.Description("Replies with positive answer only to the bot owner. | `@NadekoBot do you love me`")
.Do(async e =>
{
if (NadekoBot.IsOwner(e.User.Id))
@ -130,7 +129,7 @@ namespace NadekoBot.Modules.Conversations
cgb.CreateCommand("how are you")
.Alias("how are you?")
.Description("Replies positive only if bot owner is online.")
.Description("Replies positive only if bot owner is online. | `@NadekoBot how are you`")
.Do(async e =>
{
if (NadekoBot.IsOwner(e.User.Id))
@ -150,7 +149,7 @@ namespace NadekoBot.Modules.Conversations
});
cgb.CreateCommand("fire")
.Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | @NadekoBot fire [x]")
.Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | `@NadekoBot fire [x]`")
.Parameter("times", ParameterType.Optional)
.Do(async e =>
{
@ -174,7 +173,7 @@ namespace NadekoBot.Modules.Conversations
});
cgb.CreateCommand("dump")
.Description("Dumps all of the invites it can to dump.txt.** Owner Only.**")
.Description("Dumps all of the invites it can to dump.txt.** Owner Only.** | `@NadekoBot dump`")
.Do(async e =>
{
if (!NadekoBot.IsOwner(e.User.Id)) return;
@ -201,7 +200,7 @@ namespace NadekoBot.Modules.Conversations
});
cgb.CreateCommand("ab")
.Description("Try to get 'abalabahaha'")
.Description("Try to get 'abalabahaha'| `@NadekoBot ab`")
.Do(async e =>
{
string[] strings = { "ba", "la", "ha" };

View File

@ -1,14 +1,13 @@
using NadekoBot.Classes;
using Discord;
using Discord.Commands;
using NadekoBot.Classes;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using System.Collections.Concurrent;
using Discord;
using NadekoBot.Extensions;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Gambling.Commands
{
@ -23,8 +22,9 @@ namespace NadekoBot.Modules.Gambling.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Prefix + "race")
.Description("Starts a new animal race.")
.Do(e => {
.Description($"Starts a new animal race. | `{Prefix}race`")
.Do(e =>
{
var ar = new AnimalRace(e.Server.Id, e.Channel);
if (ar.Fail)
{
@ -35,9 +35,10 @@ namespace NadekoBot.Modules.Gambling.Commands
cgb.CreateCommand(Prefix + "joinrace")
.Alias(Prefix + "jr")
.Description("Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`")
.Description($"Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `{Prefix}jr` or `{Prefix}jr 5`")
.Parameter("amount", ParameterType.Optional)
.Do(async e => {
.Do(async e =>
{
int amount;
if (!int.TryParse(e.GetArg("amount"), out amount) || amount < 0)
@ -55,8 +56,10 @@ namespace NadekoBot.Modules.Gambling.Commands
await FlowersHandler.RemoveFlowers(e.User, "BetRace", (int)amount, true).ConfigureAwait(false);
AnimalRace ar;
if (!AnimalRaces.TryGetValue(e.Server.Id, out ar)) {
if (!AnimalRaces.TryGetValue(e.Server.Id, out ar))
{
await e.Channel.SendMessage("No race exists on this server");
return;
}
await ar.JoinRace(e.User, amount);
@ -93,7 +96,7 @@ namespace NadekoBot.Modules.Gambling.Commands
{
try
{
await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`");
await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type {NadekoBot.Config.CommandPrefixes.Gambling}jr to join the race.`");
var t = await Task.WhenAny(Task.Delay(20000, token), fullgame);
Started = true;
cancelSource.Cancel();
@ -103,7 +106,7 @@ namespace NadekoBot.Modules.Gambling.Commands
}
else if (participants.Count > 1)
{
await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`");
await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " participants.`");
}
else
{
@ -127,7 +130,8 @@ namespace NadekoBot.Modules.Gambling.Commands
AnimalRaces.TryRemove(serverId, out throwaway);
}
private async Task StartRace() {
private async Task StartRace()
{
var rng = new Random();
Participant winner = null;
Message msg = null;
@ -163,7 +167,7 @@ namespace NadekoBot.Modules.Gambling.Commands
|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|";
if (msg == null || messagesSinceGameStarted >= 10) // also resend the message if channel was spammed
{
if(msg != null)
if (msg != null)
try { await msg.Delete(); } catch { }
msg = await raceChannel.SendMessage(text);
messagesSinceGameStarted = 0;
@ -199,10 +203,11 @@ namespace NadekoBot.Modules.Gambling.Commands
messagesSinceGameStarted++;
}
private async Task CheckForFullGameAsync(CancellationToken cancelToken) {
private async Task CheckForFullGameAsync(CancellationToken cancelToken)
{
while (animals.Count > 0)
{
await Task.Delay(100,cancelToken);
await Task.Delay(100, cancelToken);
}
}
@ -257,8 +262,8 @@ namespace NadekoBot.Modules.Gambling.Commands
public override bool Equals(object obj)
{
var p = obj as Participant;
return p == null?
false:
return p == null ?
false :
p.User == User;
}
@ -279,7 +284,8 @@ namespace NadekoBot.Modules.Gambling.Commands
{
return str + "`3rd`";
}
else {
else
{
return str + $"`{Place}th`";
}

View File

@ -21,18 +21,18 @@ namespace NadekoBot.Modules.Gambling
{
cgb.CreateCommand(Module.Prefix + "roll")
.Description("Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice." +
" If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5")
$" If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `{Prefix}roll` or `{Prefix}roll 7` or `{Prefix}roll 3d5`")
.Parameter("num", ParameterType.Optional)
.Do(RollFunc());
cgb.CreateCommand(Module.Prefix + "rolluo")
.Description("Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered)." +
" If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5")
$" If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `{Prefix}roll` or `{Prefix}roll` 7 or `{Prefix}roll 3d5`")
.Parameter("num", ParameterType.Optional)
.Do(RollFunc(false));
cgb.CreateCommand(Module.Prefix + "nroll")
.Description("Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`")
.Description($"Rolls in a given range. | `{Prefix}nroll 5` (rolls 0-5) or `{Prefix}nroll 5-15`")
.Parameter("range", ParameterType.Required)
.Do(NRollFunc());
}

View File

@ -17,13 +17,13 @@ namespace NadekoBot.Modules.Gambling
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "draw")
.Description("Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | $draw [x]")
.Description($"Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | `{Prefix}draw [x]`")
.Parameter("count", ParameterType.Optional)
.Do(DrawCardFunc());
cgb.CreateCommand(Module.Prefix + "shuffle")
.Alias(Module.Prefix + "sh")
.Description("Reshuffles all cards back into the deck.")
.Description($"Reshuffles all cards back into the deck.|`{Prefix}shuffle`")
.Do(ReshuffleTask());
}

View File

@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Gambling
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "flip")
.Description("Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`")
.Description($"Flips coin(s) - heads or tails, and shows an image. | `{Prefix}flip` or `{Prefix}flip 3`")
.Parameter("count", ParameterType.Optional)
.Do(FlipCoinFunc());

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Gambling
commands.ForEach(com => com.Init(cgb));
cgb.CreateCommand(Prefix + "raffle")
.Description($"Prints a name and ID of a random user from the online list from the (optional) role. | `{Prefix}raffle` or `{Prefix}raffle RoleName")
.Description($"Prints a name and ID of a random user from the online list from the (optional) role. | `{Prefix}raffle` or `{Prefix}raffle RoleName`")
.Parameter("role", ParameterType.Optional)
.Do(async e =>
{
@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Gambling
});
cgb.CreateCommand(Prefix + "give")
.Description(string.Format("Give someone a certain amount of {0}s", NadekoBot.Config.CurrencyName))
.Description(string.Format("Give someone a certain amount of {0}s", NadekoBot.Config.CurrencyName)+ $"|`{Prefix}give 1 \"@SomeGuy\"`")
.Parameter("amount", ParameterType.Required)
.Parameter("receiver", ParameterType.Unparsed)
.Do(async e =>
@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Gambling
cgb.CreateCommand(Prefix + "betroll")
.Alias(Prefix + "br")
.Description($"Bets a certain amount of {NadekoBot.Config.CurrencyName}s and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | {Prefix}br 5")
.Description($"Bets a certain amount of {NadekoBot.Config.CurrencyName}s and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | `{Prefix}br 5`")
.Parameter("amount",ParameterType.Required)
.Do(async e =>
{
@ -187,6 +187,7 @@ namespace NadekoBot.Modules.Gambling
cgb.CreateCommand(Prefix + "leaderboard")
.Alias(Prefix + "lb")
.Description($"Displays bot currency leaderboard | {Prefix}lb")
.Do(async e =>
{
var richestTemp = DbHandler.Instance.GetTopRichest();

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "betray")
.Description("BETRAY GAME. Betray nadeko next turn." +
"If Nadeko cooperates - you get extra points, nadeko loses a LOT." +
"If Nadeko betrays - you both lose some points.")
$"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
.Do(async e =>
{
await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false);
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "cooperate")
.Description("BETRAY GAME. Cooperate with nadeko next turn." +
"If Nadeko cooperates - you both get bonus points." +
"If Nadeko betrays - you lose A LOT, nadeko gets extra.")
$"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
.Do(async e =>
{

View File

@ -1,5 +1,6 @@
using Discord.Commands;
using NadekoBot.Classes;
using NadekoBot.Extensions;
using System.Text;
//taken from
@ -291,13 +292,13 @@ namespace NadekoBot.Modules.Games.Commands
}
#endregion
}
return sb.ToString(); // Return result.
return sb.ToString().TrimTo(1995); // Return result.
}
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "leet")
.Description($"Converts a text to leetspeak with 6 (1-6) severity levels | {Module.Prefix}leet 3 Hello")
.Description($"Converts a text to leetspeak with 6 (1-6) severity levels | `{Module.Prefix}leet 3 Hello`")
.Parameter("level", ParameterType.Required)
.Parameter("text", ParameterType.Unparsed)
.Do(async e =>

View File

@ -65,7 +65,7 @@ namespace NadekoBot.Modules.Games.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "pick")
.Description("Picks a flower planted in this channel.")
.Description($"Picks a flower planted in this channel. | `{Prefix}pick`")
.Do(async e =>
{
IEnumerable<Message> msgs;
@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Games.Commands
});
cgb.CreateCommand(Module.Prefix + "plant")
.Description("Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)")
.Description($"Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) | `{Prefix}plant`")
.Do(async e =>
{
await locker.WaitAsync().ConfigureAwait(false);
@ -124,7 +124,7 @@ namespace NadekoBot.Modules.Games.Commands
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}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60`")
.Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `{Prefix}gc` or `{Prefix}gc 60`")
.AddCheck(SimpleCheckers.ManageMessages())
.Parameter("cd", ParameterType.Unparsed)
.Do(async e =>

View File

@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Games.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "poll")
.Description("Creates a poll, only person who has manage server permission can do it. | >poll Question?;Answer1;Answ 2;A_3")
.Description($"Creates a poll, only person who has manage server permission can do it. | `{Prefix}poll Question?;Answer1;Answ 2;A_3`")
.Parameter("allargs", ParameterType.Unparsed)
.Do(async e =>
{
@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Games.Commands
}).ConfigureAwait(false);
});
cgb.CreateCommand(Module.Prefix + "pollend")
.Description("Stops active poll on this server and prints the results in this channel.")
.Description($"Stops active poll on this server and prints the results in this channel. | `{Prefix}pollend`")
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageChannels)

View File

@ -168,21 +168,21 @@ namespace NadekoBot.Modules.Games.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "typestart")
.Description("Starts a typing contest.")
.Description($"Starts a typing contest. | `{Prefix}typestart`")
.Do(DoFunc());
cgb.CreateCommand(Module.Prefix + "typestop")
.Description("Stops a typing contest on the current channel.")
.Description($"Stops a typing contest on the current channel. | `{Prefix}typestop`")
.Do(QuitFunc());
cgb.CreateCommand(Module.Prefix + "typeadd")
.Description("Adds a new article to the typing contest. Owner only.")
.Description($"Adds a new article to the typing contest. Owner only. | `{Prefix}typeadd wordswords`")
.Parameter("text", ParameterType.Unparsed)
.Do(async e =>
{
if (!NadekoBot.IsOwner(e.User.Id) || string.IsNullOrWhiteSpace(e.GetArg("text"))) return;
DbHandler.Instance.InsertData(new TypingArticle
DbHandler.Instance.Connection.Insert(new TypingArticle
{
Text = e.GetArg("text"),
DateAdded = DateTime.Now

View File

@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Games.Commands
});
cgb.CreateCommand(Module.Prefix + "tl")
.Description("Shows a current trivia leaderboard.")
.Description($"Shows a current trivia leaderboard. | `{Prefix}tl`")
.Do(async e =>
{
TriviaGame trivia;
@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Games.Commands
});
cgb.CreateCommand(Module.Prefix + "tq")
.Description("Quits current trivia after current question.")
.Description($"Quits current trivia after current question. | `{Prefix}tq`")
.Do(async e =>
{
TriviaGame trivia;

View File

@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Games
try
{
await e.Channel.SendMessage(
$":question: **Question**: `{question}` \n🎱 **8Ball Answers**: `{NadekoBot.Config._8BallResponses[rng.Next(0, NadekoBot.Config._8BallResponses.Length)]}`")
$":question: `Question` __**{question}**__ \n🎱 `8Ball Answers` __**{NadekoBot.Config._8BallResponses[rng.Next(0, NadekoBot.Config._8BallResponses.Length)]}**__")
.ConfigureAwait(false);
}
catch { }

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Classes.Help.Commands
if (alias != null)
str = $" / `{ com.Aliases.FirstOrDefault()}`";
if (com != null)
await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**",1)}").ConfigureAwait(false);
await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**", 1)}").ConfigureAwait(false);
}).ConfigureAwait(false);
};
public static string HelpString {
@ -48,11 +48,11 @@ namespace NadekoBot.Classes.Help.Commands
public Action<CommandEventArgs> DoGitFunc() => e =>
{
string helpstr =
$@"######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/**
######You can donate on paypal: `nadekodiscordbot@gmail.com`
$@"######For more information and how to setup your own NadekoBot, go to: <http://github.com/Kwoth/NadekoBot/wiki>
######You can donate on patreon: <https://patreon.com/nadekobot>
######or paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands
Version: `{NadekoStats.Instance.BotVersion}`";
#NadekoBot List Of Commands ";
string lastCategory = "";
@ -80,16 +80,16 @@ Version: `{NadekoStats.Instance.BotVersion}`";
{
cgb.CreateCommand(Module.Prefix + "h")
.Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h")
.Description("Either shows a help for a single command, or PMs you help link if no arguments are specified. | '-h !m q' or just '-h' ")
.Description($"Either shows a help for a single command, or PMs you help link if no arguments are specified. | `{Prefix}h !m q` or just `{Prefix}h` ")
.Parameter("command", ParameterType.Unparsed)
.Do(HelpFunc());
cgb.CreateCommand(Module.Prefix + "hgit")
.Description("Generates the commandlist.md file. **Bot Owner Only!**")
.Description($"Generates the commandlist.md file. **Bot Owner Only!** | `{Prefix}hgit`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(DoGitFunc());
cgb.CreateCommand(Module.Prefix + "readme")
.Alias(Module.Prefix + "guide")
.Description("Sends a readme and a guide links to the channel.")
.Description($"Sends a readme and a guide links to the channel. | `{Prefix}readme` or `{Prefix}guide`")
.Do(async e =>
await e.Channel.SendMessage(
@"**Wiki with all info**: <https://github.com/Kwoth/NadekoBot/wiki>
@ -102,16 +102,15 @@ Version: `{NadekoStats.Instance.BotVersion}`";
cgb.CreateCommand(Module.Prefix + "donate")
.Alias("~donate")
.Description("Instructions for helping the project!")
.Description($"Instructions for helping the project! | `{Prefix}donate` or `~donate`")
.Do(async e =>
{
await e.Channel.SendMessage(
$@"I've created a **paypal** email for nadeko, so if you wish to support the project, you can send your donations to `nadekodiscordbot@gmail.com`
Don't forget to leave your discord name or id in the message, so that I can reward people who help out.
You can join nadekobot server by typing {Module.Prefix}h and you will get an invite in a private message.
$@"You can support the project on patreon. <https://patreon.com/nadekobot> or
You can send donations to `nadekodiscordbot@gmail.com`
Don't forget to leave your discord name or id in the message.
*If you want to support in some other way or on a different platform, please message me*"
).ConfigureAwait(false);
**Thank you** ").ConfigureAwait(false);
});
}

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "modules")
.Alias(".modules")
.Description("List all bot modules.")
.Description($"List all bot modules. | `{Prefix}modules` or `.modules`")
.Do(async e =>
{
await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`")
@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "commands")
.Alias(".commands")
.Description("List all of the bot's commands from a certain module.")
.Description($"List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`")
.Parameter("module", ParameterType.Unparsed)
.Do(async e =>
{

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes
public IReadOnlyCollection<Song> Playlist => playlist;
public Song CurrentSong { get; private set; }
private CancellationTokenSource SongCancelSource { get; set; }
public CancellationTokenSource SongCancelSource { get; private set; }
private CancellationToken cancelToken { get; set; }
public bool Paused { get; set; }

View File

@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; }
public string QueuerName { get; set; }
private bool bufferingCompleted { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime()
@ -73,78 +72,22 @@ namespace NadekoBot.Modules.Music.Classes
return this;
}
private Task BufferSong(string filename, CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {skipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
var prebufferSize = 100ul.MiB();
using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
{
byte[] buffer = new byte[81920];
int bytesRead;
while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0)
{
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while ((ulong)outStream.Length - bytesSent > prebufferSize)
await Task.Delay(100, cancelToken);
}
}
bufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception) {
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{
var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false);
SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
bytesSent = 0;
try
{
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
var attempt = 0;
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
var sw = new Stopwatch();
sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
@ -162,7 +105,6 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed);
const int blockSize = 3840;
var attempt = 0;
byte[] buffer = new byte[blockSize];
while (!cancelToken.IsCancellationRequested)
{
@ -173,14 +115,31 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{
bytesSent += (ulong)read;
}
if (read < blockSize)
{
if (sb.IsNextFileReady())
{
inStream.Dispose();
inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
read += inStream.Read(buffer, read, buffer.Length - read);
attempt = 0;
}
if (read == 0)
{
if (sb.BufferingCompleted)
break;
if (attempt++ == 20)
{
voiceClient.Wait();
MusicPlayer.SongCancelSource.Cancel();
break;
}
else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
}
else
attempt = 0;
}
else
attempt = 0;
@ -195,14 +154,16 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{
await bufferTask;
await Task.Run(() => voiceClient.Clear());
if(inStream != null)
inStream.Dispose();
try { File.Delete(filename); } catch { }
Console.WriteLine("l");
sb.CleanFiles();
}
}
private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken)
private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
{
while (!bufferingCompleted && inStream.Length < 2.MiB())
while (!sb.BufferingCompleted && inStream.Length < 2.MiB())
{
await Task.Delay(100, cancelToken);
}

View File

@ -0,0 +1,159 @@
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes
{
/// <summary>
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
/// It also help for large music by deleting files that are already seen.
/// </summary>
class SongBuffer
{
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
{
Basename = basename;
SongInfo = songInfo;
SkipTo = skipTo;
}
private string Basename;
private SongInfo SongInfo;
private int SkipTo;
private static int MAX_FILE_SIZE = 20.MiB();
private long FileNumber = -1;
private long NextFileToRead = 0;
public bool BufferingCompleted { get; private set;} = false;
private ulong CurrentBufferSize = 0;
public Task BufferSong(CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
FileStream outStream = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
byte[] buffer = new byte[81920];
int currentFileSize = 0;
ulong prebufferSize = 100ul.MiB();
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
while (!p.HasExited) //Also fix low bandwidth
{
int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false);
if (currentFileSize >= MAX_FILE_SIZE)
{
try
{
outStream.Dispose();
}catch { }
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
currentFileSize = bytesRead;
}
else
{
currentFileSize += bytesRead;
}
CurrentBufferSize += Convert.ToUInt64(bytesRead);
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while (CurrentBufferSize > prebufferSize)
await Task.Delay(100, cancelToken);
}
BufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception)
{
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
if(outStream != null)
outStream.Dispose();
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
/// <summary>
/// Return the next file to read, and delete the old one
/// </summary>
/// <returns>Name of the file to read</returns>
public string GetNextFile()
{
string filename = Basename + "-" + NextFileToRead;
if (NextFileToRead != 0)
{
try
{
CurrentBufferSize -= Convert.ToUInt64(new FileInfo(Basename + "-" + (NextFileToRead - 1)).Length);
File.Delete(Basename + "-" + (NextFileToRead - 1));
}
catch { }
}
NextFileToRead++;
return filename;
}
public bool IsNextFileReady()
{
return NextFileToRead <= FileNumber;
}
public void CleanFiles()
{
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
{
try
{
File.Delete(Basename + "-" + i);
}
catch { }
}
}
}
}

View File

@ -614,11 +614,11 @@ namespace NadekoBot.Modules.Music
};
DbHandler.Instance.SaveAll(songInfos);
DbHandler.Instance.Save(playlist);
DbHandler.Instance.InsertMany(songInfos.Select(s => new PlaylistSongInfo
DbHandler.Instance.Connection.InsertAll(songInfos.Select(s => new PlaylistSongInfo
{
PlaylistId = playlist.Id.Value,
SongInfoId = s.Id.Value
}));
}), typeof(PlaylistSongInfo));
await e.Channel.SendMessage($"🎵 `Saved playlist as {name}-{playlist.Id}`").ConfigureAwait(false);
@ -718,7 +718,7 @@ namespace NadekoBot.Modules.Music
});
cgb.CreateCommand(Prefix + "goto")
.Description($"Goes to a specific time in seconds in a song. | {Prefix}goto 30")
.Description($"Goes to a specific time in seconds in a song. | `{Prefix}goto 30`")
.Parameter("time")
.Do(async e =>
{
@ -756,16 +756,37 @@ namespace NadekoBot.Modules.Music
cgb.CreateCommand(Prefix + "getlink")
.Alias(Prefix + "gl")
.Description("Shows a link to the currently playing song.")
.Description("Shows a link to the song in the queue by index, or the currently playing song by default.")
.Parameter("index", ParameterType.Optional)
.Do(async e =>
{
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer))
return;
int index;
string arg = e.GetArg("index")?.Trim();
if (!string.IsNullOrEmpty(arg) && int.TryParse(arg, out index))
{
var selSong = musicPlayer.Playlist.DefaultIfEmpty(null).ElementAtOrDefault(index - 1);
if (selSong == null)
{
await e.Channel.SendMessage("Could not select song, likely wrong index");
}
else
{
await e.Channel.SendMessage($"🎶`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false);
}
}
else
{
var curSong = musicPlayer.CurrentSong;
if (curSong == null)
return;
await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false);
}
});
cgb.CreateCommand(Prefix + "autoplay")
@ -818,7 +839,7 @@ namespace NadekoBot.Modules.Music
lastFinishedMessage = await textCh.SendMessage($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false);
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
{
await QueueSong(queuer, textCh, voiceCh, await SearchHelper.GetRelatedVideoId(song.SongInfo.Query), silent, musicType).ConfigureAwait(false);
await QueueSong(queuer.Server.CurrentUser, textCh, voiceCh, (await SearchHelper.GetRelatedVideoIds(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
}
}
catch (Exception e)

View File

@ -4,6 +4,8 @@ using NadekoBot.Classes;
using NadekoBot.Modules.Permissions.Classes;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.NSFW
{
@ -27,16 +29,16 @@ namespace NadekoBot.Modules.NSFW
.Do(async e =>
{
var tag = e.GetArg("tag")?.Trim() ?? "";
var gel = await SearchHelper.GetGelbooruImageLink("rating%3Aexplicit+" + tag).ConfigureAwait(false);
if (gel != null)
await e.Channel.SendMessage(":heart: Gelbooru: " + gel)
.ConfigureAwait(false);
var dan = await SearchHelper.GetDanbooruImageLink("rating%3Aexplicit+" + tag).ConfigureAwait(false);
if (dan != null)
await e.Channel.SendMessage(":heart: Danbooru: " + dan)
.ConfigureAwait(false);
if (dan == null && gel == null)
var links = await Task.WhenAll(SearchHelper.GetGelbooruImageLink("rating%3Aexplicit+" + tag), SearchHelper.GetDanbooruImageLink("rating%3Aexplicit+" + tag)).ConfigureAwait(false);
if (links.All(l => l == null))
{
await e.Channel.SendMessage("`No results.`");
return;
}
await e.Channel.SendMessage(String.Join("\n\n", links)).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "danbooru")
.Description($"Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}danbooru yuri+kissing`")
@ -84,14 +86,14 @@ namespace NadekoBot.Modules.NSFW
await e.Channel.SendMessage(await SearchHelper.GetE621ImageLink(tag).ConfigureAwait(false)).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "cp")
.Description("We all know where this will lead you to.")
.Description($"We all know where this will lead you to. | `{Prefix}cp`")
.Parameter("anything", ParameterType.Unparsed)
.Do(async e =>
{
await e.Channel.SendMessage("http://i.imgur.com/MZkY1md.jpg").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "boobs")
.Description("Real adult content.")
.Description($"Real adult content. | `{Prefix}boobs`")
.Do(async e =>
{
try
@ -106,7 +108,7 @@ namespace NadekoBot.Modules.NSFW
});
cgb.CreateCommand(Prefix + "butts")
.Alias(Prefix + "ass", Prefix + "butt")
.Description("Real adult content.")
.Description($"Real adult content. | `{Prefix}butts` or `{Prefix}ass`")
.Do(async e =>
{
try

View File

@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Permissions.Commands
.Alias(Module.Prefix + "cfi")
.Description("Enables or disables automatic deleting of invites on the channel." +
"If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." +
" | ;cfi enable #general-chat")
$" | {Prefix}cfi enable #general-chat")
.Parameter("bool")
.Parameter("channel", ParameterType.Optional)
.Do(async e =>
@ -95,7 +95,7 @@ namespace NadekoBot.Modules.Permissions.Commands
cgb.CreateCommand(Module.Prefix + "srvrfilterinv")
.Alias(Module.Prefix + "sfi")
.Description("Enables or disables automatic deleting of invites on the server. | ;sfi disable")
.Description($"Enables or disables automatic deleting of invites on the server. | {Prefix}sfi disable")
.Parameter("bool")
.Do(async e =>
{

View File

@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Permissions.Commands
.Alias(Module.Prefix + "cfw")
.Description("Enables or disables automatic deleting of messages containing banned words on the channel." +
"If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." +
" | ;cfw enable #general-chat")
$" | {Prefix}cfw enable #general-chat")
.Parameter("bool")
.Parameter("channel", ParameterType.Optional)
.Do(async e =>
@ -89,7 +89,7 @@ namespace NadekoBot.Modules.Permissions.Commands
cgb.CreateCommand(Module.Prefix + "addfilterword")
.Alias(Module.Prefix + "afw")
.Description("Adds a new word to the list of filtered words" +
" | ;afw poop")
$" | {Prefix}afw poop")
.Parameter("word", ParameterType.Unparsed)
.Do(async e =>
{
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Permissions.Commands
cgb.CreateCommand(Module.Prefix + "rmvfilterword")
.Alias(Module.Prefix + "rfw")
.Description("Removes the word from the list of filtered words" +
" | ;rw poop")
$" | {Prefix}rw poop")
.Parameter("word", ParameterType.Unparsed)
.Do(async e =>
{
@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Permissions.Commands
cgb.CreateCommand(Module.Prefix + "lstfilterwords")
.Alias(Module.Prefix + "lfw")
.Description("Shows a list of filtered words" +
" | ;lfw")
$" | {Prefix}lfw")
.Do(async e =>
{
try
@ -152,7 +152,7 @@ namespace NadekoBot.Modules.Permissions.Commands
cgb.CreateCommand(Module.Prefix + "srvrfilterwords")
.Alias(Module.Prefix + "sfw")
.Description("Enables or disables automatic deleting of messages containing forbidden words on the server. | ;sfw disable")
.Description($"Enables or disables automatic deleting of messages containing forbidden words on the server. | {Prefix}sfw disable")
.Parameter("bool")
.Do(async e =>
{

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "permrole")
.Alias(Prefix + "pr")
.Description("Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'.")
.Description($"Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `{Prefix}pr role`")
.Parameter("role", ParameterType.Unparsed)
.Do(async e =>
{
@ -158,7 +158,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "srvrperms")
.Alias(Prefix + "sp")
.Description("Shows banned permissions for this server.")
.Description($"Shows banned permissions for this server. | `{Prefix}sp`")
.Do(async e =>
{
var perms = PermissionsHandler.GetServerPermissions(e.Server);

View File

@ -196,7 +196,7 @@ namespace NadekoBot.Modules.Pokemon
cgb.CreateCommand(Prefix + "movelist")
.Alias(Prefix + "ml")
.Description("Lists the moves you are able to use")
.Description($"Lists the moves you are able to use | `{Prefix}ml`")
.Do(async e =>
{
var userType = GetPokeType(e.User.Id);
@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Pokemon
});
cgb.CreateCommand(Prefix + "heal")
.Description($"Heals someone. Revives those who fainted. Costs a {NadekoBot.Config.CurrencyName} | {Prefix}heal @someone")
.Description($"Heals someone. Revives those who fainted. Costs a {NadekoBot.Config.CurrencyName} | `{Prefix}heal @someone`")
.Parameter("target", ParameterType.Unparsed)
.Do(async e =>
{
@ -263,7 +263,7 @@ namespace NadekoBot.Modules.Pokemon
});
cgb.CreateCommand(Prefix + "type")
.Description($"Get the poketype of the target. | {Prefix}type @someone")
.Description($"Get the poketype of the target. | `{Prefix}type @someone`")
.Parameter("target", ParameterType.Unparsed)
.Do(async e =>
{
@ -282,7 +282,7 @@ namespace NadekoBot.Modules.Pokemon
});
cgb.CreateCommand(Prefix + "settype")
.Description($"Set your poketype. Costs a {NadekoBot.Config.CurrencyName}. | {Prefix}settype fire")
.Description($"Set your poketype. Costs a {NadekoBot.Config.CurrencyName}. | `{Prefix}settype fire`")
.Parameter("targetType", ParameterType.Unparsed)
.Do(async e =>
{
@ -319,11 +319,11 @@ namespace NadekoBot.Modules.Pokemon
DbHandler.Instance.Delete<UserPokeTypes>(Dict[(long)e.User.Id]);
}
DbHandler.Instance.InsertData(new UserPokeTypes
DbHandler.Instance.Connection.Insert(new UserPokeTypes
{
UserId = (long)e.User.Id,
type = targetType.Name
});
}, typeof(UserPokeTypes));
//Now for the response

View File

@ -32,7 +32,7 @@ namespace NadekoBot.Modules.Searches.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "convert")
.Description("Convert quantities from>to. Like `~convert m>km 1000`")
.Description($"Convert quantities from>to. | `{Prefix}convert m>km 1000`")
.Parameter("from-to", ParameterType.Required)
.Parameter("quantity", ParameterType.Optional)
.Do(ConvertFunc());

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches.Commands
{
cgb.CreateCommand(Module.Prefix + "calculate")
.Alias(Module.Prefix + "calc")
.Description("Evaluate a mathematical expression. | ~calc 1+1")
.Description($"Evaluate a mathematical expression. | {Prefix}calc 1+1")
.Parameter("expression", ParameterType.Unparsed)
.Do(EvalFunc());
}

View File

@ -72,7 +72,7 @@ namespace NadekoBot.Modules.Searches.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "lolchamp")
.Description("Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |~lolchamp Riven or ~lolchamp Annie sup")
.Description($"Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |`{Prefix}lolchamp Riven` or `{Prefix}lolchamp Annie sup`")
.Parameter("champ", ParameterType.Required)
.Parameter("position", ParameterType.Unparsed)
.Do(async e =>
@ -280,7 +280,7 @@ Assists: {general["assists"]} Ban: {general["banRate"]}%
});
cgb.CreateCommand(Module.Prefix + "lolban")
.Description("Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time.")
.Description($"Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time. | `{Prefix}lolban`")
.Do(async e =>
{

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches.Commands
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/")
.Description($"Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `{Prefix}memelist`")
.Do(async e =>
{
int i = 0;
@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Searches.Commands
});
cgb.CreateCommand(Prefix + "memegen")
.Description("Generates a meme from memelist with top and bottom text. | `~memegen biw \"gets iced coffee\" \"in the winter\"`")
.Description($"Generates a meme from memelist with top and bottom text. | `{Prefix}memegen biw \"gets iced coffee\" \"in the winter\"`")
.Parameter("meme", ParameterType.Required)
.Parameter("toptext", ParameterType.Required)
.Parameter("bottext", ParameterType.Required)

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. | `~osu Name` or `~osu Name taiko`")
.Description($"Shows osu stats for a player. | `{Prefix}osu Name` or `{Prefix}osu Name taiko`")
.Parameter("usr", ParameterType.Required)
.Parameter("mode", ParameterType.Unparsed)
.Do(async e =>
@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Searches.Commands
});
cgb.CreateCommand(Module.Prefix + "osu b")
.Description("Shows information about an osu beatmap. |~osu b https://osu.ppy.sh/s/127712")
.Description($"Shows information about an osu beatmap. |`{Prefix}osu b` https://osu.ppy.sh/s/127712`")
.Parameter("map", ParameterType.Unparsed)
.Do(async e =>
{
@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Searches.Commands
});
cgb.CreateCommand(Module.Prefix + "osu top5")
.Description("Displays a user's top 5 plays. |~osu top5 Name")
.Description($"Displays a user's top 5 plays. |{Prefix}osu top5 Name")
.Parameter("usr", ParameterType.Required)
.Parameter("mode", ParameterType.Unparsed)
.Do(async e =>

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Searches.Commands
{
cgb.CreateCommand(Prefix + "pokemon")
.Alias(Prefix + "poke")
.Description("Searches for a pokemon.")
.Description($"Searches for a pokemon. | `{Prefix}poke Sylveon`")
.Parameter("pokemon", ParameterType.Unparsed)
.Do(async e =>
{
@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Prefix + "pokemonability")
.Alias(Prefix + "pokeab")
.Description("Searches for a pokemon ability.")
.Description($"Searches for a pokemon ability. | `{Prefix}pokeab \"water gun\"`")
.Parameter("abil", ParameterType.Unparsed)
.Do(async e =>
{

View File

@ -122,7 +122,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "hitbox")
.Alias(Module.Prefix + "hb")
.Description("Notifies this channel when a certain user starts streaming." +
" | ~hitbox SomeStreamer")
$" | `{Prefix}hitbox SomeStreamer`")
.Parameter("username", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.ManageServer())
.Do(TrackStream(StreamNotificationConfig.StreamType.Hitbox));
@ -130,7 +130,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "twitch")
.Alias(Module.Prefix + "tw")
.Description("Notifies this channel when a certain user starts streaming." +
" | ~twitch SomeStreamer")
$" | `{Prefix}twitch SomeStreamer`")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(TrackStream(StreamNotificationConfig.StreamType.Twitch));
@ -138,7 +138,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "beam")
.Alias(Module.Prefix + "bm")
.Description("Notifies this channel when a certain user starts streaming." +
" | ~beam SomeStreamer")
$" | `{Prefix}beam SomeStreamer`")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(TrackStream(StreamNotificationConfig.StreamType.Beam));
@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "checkhitbox")
.Alias(Module.Prefix + "chhb")
.Description("Checks if a certain user is streaming on the hitbox platform." +
" | ~chhb SomeStreamer")
$" | `{Prefix}chhb SomeStreamer`")
.Parameter("username", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.ManageServer())
.Do(async e =>
@ -175,7 +175,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "checktwitch")
.Alias(Module.Prefix + "chtw")
.Description("Checks if a certain user is streaming on the twitch platform." +
" | ~chtw SomeStreamer")
$" | `{Prefix}chtw SomeStreamer`")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(async e =>
@ -204,7 +204,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "checkbeam")
.Alias(Module.Prefix + "chbm")
.Description("Checks if a certain user is streaming on the beam platform." +
" | ~chbm SomeStreamer")
$" | `{Prefix}chbm SomeStreamer`")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(async e =>
@ -233,7 +233,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "removestream")
.Alias(Module.Prefix + "rms")
.Description("Removes notifications of a certain streamer on this channel." +
" | ~rms SomeGuy")
$" | `{Prefix}rms SomeGuy`")
.AddCheck(SimpleCheckers.ManageServer())
.Parameter("username", ParameterType.Unparsed)
.Do(async e =>
@ -261,7 +261,7 @@ namespace NadekoBot.Modules.Searches.Commands
cgb.CreateCommand(Module.Prefix + "liststreams")
.Alias(Module.Prefix + "ls")
.Description("Lists all streams you are following on this server." +
" | ~ls")
$" | `{Prefix}ls`")
.Do(async e =>
{

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Searches.Commands
{
cgb.CreateCommand(Module.Prefix + "wowjoke")
.Description("Get one of Kwoth's penultimate WoW jokes.")
.Description($"Get one of Kwoth's penultimate WoW jokes. | `{Prefix}wowjoke`")
.Do(async e =>
{
if (!jokes.Any())

View File

@ -86,7 +86,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "ani")
.Alias(Prefix + "anime", Prefix + "aq")
.Parameter("query", ParameterType.Unparsed)
.Description("Queries anilist for an anime and shows the first result.")
.Description($"Queries anilist for an anime and shows the first result. | `{Prefix}aq aquerion evol`")
.Do(async e =>
{
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -341,7 +341,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
cgb.CreateCommand(Prefix + "quote")
.Description("Shows a random quote.")
.Description($"Shows a random quote. | `{Prefix}quote`")
.Do(async e =>
{
var quote = NadekoBot.Config.Quotes[rng.Next(0, NadekoBot.Config.Quotes.Count)].ToString();
@ -349,7 +349,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
cgb.CreateCommand(Prefix + "catfact")
.Description("Shows a random catfact from <http://catfacts-api.appspot.com/api/facts>")
.Description($"Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `{Prefix}catfact`")
.Do(async e =>
{
var response = await SearchHelper.GetResponseStringAsync("http://catfacts-api.appspot.com/api/facts").ConfigureAwait(false);
@ -360,7 +360,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "yomama")
.Alias(Prefix + "ym")
.Description("Shows a random joke from <http://api.yomomma.info/>")
.Description($"Shows a random joke from <http://api.yomomma.info/> | `{Prefix}ym`")
.Do(async e =>
{
var response = await SearchHelper.GetResponseStringAsync("http://api.yomomma.info/").ConfigureAwait(false);
@ -369,7 +369,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "randjoke")
.Alias(Prefix + "rj")
.Description("Shows a random joke from <http://tambal.azurewebsites.net/joke/random>")
.Description($"Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `{Prefix}rj`")
.Do(async e =>
{
var response = await SearchHelper.GetResponseStringAsync("http://tambal.azurewebsites.net/joke/random").ConfigureAwait(false);
@ -378,7 +378,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "chucknorris")
.Alias(Prefix + "cn")
.Description("Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random>")
.Description($"Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `{Prefix}cn`")
.Do(async e =>
{
var response = await SearchHelper.GetResponseStringAsync("http://api.icndb.com/jokes/random/").ConfigureAwait(false);
@ -387,7 +387,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "magicitem")
.Alias(Prefix + "mi")
.Description("Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items>")
.Description($"Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `{Prefix}mi`")
.Do(async e =>
{
var magicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json"));
@ -397,7 +397,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
cgb.CreateCommand(Prefix + "revav")
.Description($"Returns a google reverse image search for someone's avatar. | `{Prefix}revav \"@SomeGuy\"")
.Description($"Returns a google reverse image search for someone's avatar. | `{Prefix}revav \"@SomeGuy\"`")
.Parameter("user", ParameterType.Unparsed)
.Do(async e =>
{

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Translator
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "translangs")
.Description("List the valid languages for translation.")
.Description($"List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
.Parameter("search", ParameterType.Optional)
.Do(ListLanguagesFunc());
}

View File

@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Trello
});
cgb.CreateCommand(Prefix + "unbind")
.Description("Unbinds a bot from the channel and board.")
.Description($"Unbinds a bot from the channel and board. | `{Prefix}unbind`")
.Do(async e =>
{
if (!NadekoBot.IsOwner(e.User.Id)) return;
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Trello
cgb.CreateCommand(Prefix + "lists")
.Alias(Prefix + "list")
.Description("Lists all lists yo ;)")
.Description($"Lists all lists yo ;) | {Prefix}list")
.Do(async e =>
{
if (!NadekoBot.IsOwner(e.User.Id)) return;

View File

@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Utility.Commands
{
cgb.CreateCommand(Module.Prefix + "serverinfo")
.Alias(Module.Prefix + "sinfo")
.Description($"Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. |{Module.Prefix}sinfo Some Server")
.Description($"Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. |`{Module.Prefix}sinfo Some Server`")
.Parameter("server", ParameterType.Optional)
.Do(async e =>
{
@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Utility.Commands
cgb.CreateCommand(Module.Prefix + "channelinfo")
.Alias(Module.Prefix + "cinfo")
.Description($"Shows info about the channel. If no channel is supplied, it defaults to current one. |{Module.Prefix}cinfo #some-channel")
.Description($"Shows info about the channel. If no channel is supplied, it defaults to current one. |`{Module.Prefix}cinfo #some-channel`")
.Parameter("channel", ParameterType.Optional)
.Do(async e =>
{
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Utility.Commands
cgb.CreateCommand(Module.Prefix + "userinfo")
.Alias(Module.Prefix + "uinfo")
.Description($"Shows info about the user. If no user is supplied, it defaults a user running the command. |{Module.Prefix}uinfo @SomeUser")
.Description($"Shows info about the user. If no user is supplied, it defaults a user running the command. |`{Module.Prefix}uinfo @SomeUser`")
.Parameter("user", ParameterType.Optional)
.Do(async e =>
{

View File

@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Utility.Commands
.Description("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!`")
$" | `{Prefix}remind me 1d5h Do something` or `{Prefix}remind #general Start now!`")
.Parameter("meorchannel", ParameterType.Required)
.Parameter("time", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
@ -171,7 +171,7 @@ namespace NadekoBot.Modules.Utility.Commands
UserId = (long)e.User.Id,
ServerId = (long)e.Server.Id
};
DbHandler.Instance.InsertData(rem);
DbHandler.Instance.Connection.Insert(rem);
reminders.Add(StartNewReminder(rem));
@ -180,7 +180,7 @@ namespace NadekoBot.Modules.Utility.Commands
cgb.CreateCommand(Module.Prefix + "remindmsg")
.Description("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!**")
$" Message specified in the remind, %target% - target channel of the remind. **Bot Owner Only!** | `{Prefix}remindmsg do something else`")
.Parameter("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>

View File

@ -87,7 +87,7 @@ namespace NadekoBot.Modules.Utility
});
cgb.CreateCommand(Prefix + "checkmyperms")
.Description("Checks your userspecific permissions on this channel.")
.Description($"Checks your userspecific permissions on this channel. | `{Prefix}checkmyperms`")
.Do(async e =>
{
var output = "```\n";
@ -100,21 +100,21 @@ namespace NadekoBot.Modules.Utility
});
cgb.CreateCommand(Prefix + "stats")
.Description("Shows some basic stats for Nadeko.")
.Description($"Shows some basic stats for Nadeko. | `{Prefix}stats`")
.Do(async e =>
{
await e.Channel.SendMessage(await NadekoStats.Instance.GetStats()).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "dysyd")
.Description("Shows some basic stats for Nadeko.")
.Description($"Shows some basic stats for Nadeko. | `{Prefix}dysyd`")
.Do(async e =>
{
await e.Channel.SendMessage((await NadekoStats.Instance.GetStats()).Matrix().TrimTo(1990)).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "userid").Alias(Prefix + "uid")
.Description($"Shows user ID. | `{Prefix}uid` or `{Prefix}uid \"@SomeGuy\"")
.Description($"Shows user ID. | `{Prefix}uid` or `{Prefix}uid \"@SomeGuy\"`")
.Parameter("user", ParameterType.Unparsed)
.Do(async e =>
{

View File

@ -117,7 +117,7 @@ namespace NadekoBot
Client = new DiscordClient(new DiscordConfigBuilder()
{
MessageCacheSize = 10,
ConnectionTimeout = 120000,
ConnectionTimeout = 180000,
LogLevel = LogSeverity.Warning,
LogHandler = (s, e) =>
Console.WriteLine($"Severity: {e.Severity}" +
@ -197,7 +197,7 @@ namespace NadekoBot
return;
}
#if NADEKO_RELEASE
await Task.Delay(100000).ConfigureAwait(false);
await Task.Delay(120000).ConfigureAwait(false);
#else
await Task.Delay(1000).ConfigureAwait(false);
#endif

View File

@ -161,8 +161,23 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>lib\ScaredFingers.UnitsConversion.dll</HintPath>
</Reference>
<Reference Include="SQLite-net, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\sqlite-net-pcl.1.1.2\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLite-net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCL.batteries, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCL.bundle_green.0.9.2\lib\net45\SQLitePCL.batteries.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCL.raw, Version=0.9.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCL.raw.0.9.2\lib\net45\SQLitePCL.raw.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLPlugin_esqlite3, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SQLitePCL.plugin.sqlite3.net45.0.9.2\lib\net45\SQLitePCLPlugin_esqlite3.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@ -202,6 +217,7 @@
<Compile Include="Modules\Searches\Commands\OsuCommands.cs" />
<Compile Include="Modules\Searches\Commands\PokemonSearchCommands.cs" />
<Compile Include="Modules\Utility\UtilityModule.cs" />
<Compile Include="_Models\DataModels\TestDataModel.cs" />
<Compile Include="_Models\DataModels\Incident.cs" />
<Compile Include="_Models\JSONModels\AnimeResult.cs" />
<Compile Include="_Models\JSONModels\Configuration.cs" />
@ -215,6 +231,7 @@
<Compile Include="Modules\Music\Classes\MusicControls.cs" />
<Compile Include="Modules\Music\Classes\PoopyBuffer.cs" />
<Compile Include="Modules\Music\Classes\Song.cs" />
<Compile Include="Modules\Music\Classes\SongBuffer.cs" />
<Compile Include="Modules\Music\Classes\SoundCloud.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" />
@ -264,7 +281,6 @@
<Compile Include="Modules\Gambling\DrawCommand.cs" />
<Compile Include="Modules\Gambling\FlipCoinCommand.cs" />
<Compile Include="Modules\Help\Commands\HelpCommand.cs" />
<Compile Include="Modules\Administration\Commands\VoiceNotificationCommand.cs" />
<Compile Include="Modules\Administration\Commands\VoicePlusTextCommand.cs" />
<Compile Include="Modules\Administration\AdministrationModule.cs" />
<Compile Include="Modules\Conversations\Conversations.cs" />
@ -296,8 +312,6 @@
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Classes\NadekoStats.cs" />
<Compile Include="SQLite.cs" />
<Compile Include="SQLiteAsync.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@ -550,6 +564,13 @@
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\SQLitePCL.native.sqlite3.v110_xp.0.9.2\build\SQLitePCL.native.sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\SQLitePCL.native.sqlite3.v110_xp.0.9.2\build\SQLitePCL.native.sqlite3.v110_xp.targets'))" />
</Target>
<Import Project="..\packages\SQLitePCL.native.sqlite3.v110_xp.0.9.2\build\SQLitePCL.native.sqlite3.v110_xp.targets" Condition="Exists('..\packages\SQLitePCL.native.sqlite3.v110_xp.0.9.2\build\SQLitePCL.native.sqlite3.v110_xp.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

File diff suppressed because it is too large Load Diff

View File

@ -1,503 +0,0 @@
//
// Copyright (c) 2012 Krueger Systems, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace SQLite
{
public partial class SQLiteAsyncConnection
{
SQLiteConnectionString _connectionString;
SQLiteOpenFlags _openFlags;
public SQLiteAsyncConnection(string databasePath, bool storeDateTimeAsTicks = false)
: this(databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
{
}
public SQLiteAsyncConnection(string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = false)
{
_openFlags = openFlags;
_connectionString = new SQLiteConnectionString(databasePath, storeDateTimeAsTicks);
}
SQLiteConnectionWithLock GetConnection ()
{
return SQLiteConnectionPool.Shared.GetConnection (_connectionString, _openFlags);
}
public Task<CreateTablesResult> CreateTableAsync<T> ()
where T : new ()
{
return CreateTablesAsync (typeof (T));
}
public Task<CreateTablesResult> CreateTablesAsync<T, T2> ()
where T : new ()
where T2 : new ()
{
return CreateTablesAsync (typeof (T), typeof (T2));
}
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3> ()
where T : new ()
where T2 : new ()
where T3 : new ()
{
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3));
}
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4> ()
where T : new ()
where T2 : new ()
where T3 : new ()
where T4 : new ()
{
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3), typeof (T4));
}
public Task<CreateTablesResult> CreateTablesAsync<T, T2, T3, T4, T5> ()
where T : new ()
where T2 : new ()
where T3 : new ()
where T4 : new ()
where T5 : new ()
{
return CreateTablesAsync (typeof (T), typeof (T2), typeof (T3), typeof (T4), typeof (T5));
}
public Task<CreateTablesResult> CreateTablesAsync (params Type[] types)
{
return Task.Factory.StartNew (() => {
CreateTablesResult result = new CreateTablesResult ();
var conn = GetConnection ();
using (conn.Lock ()) {
foreach (Type type in types) {
int aResult = conn.CreateTable (type);
result.Results[type] = aResult;
}
}
return result;
});
}
public Task<int> DropTableAsync<T> ()
where T : new ()
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.DropTable<T> ();
}
});
}
public Task<int> InsertAsync (object item)
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Insert (item);
}
});
}
public Task<int> UpdateAsync (object item)
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Update (item);
}
});
}
public Task<int> DeleteAsync (object item)
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Delete (item);
}
});
}
public Task<T> GetAsync<T>(object pk)
where T : new()
{
return Task.Factory.StartNew(() =>
{
var conn = GetConnection();
using (conn.Lock())
{
return conn.Get<T>(pk);
}
});
}
public Task<T> FindAsync<T> (object pk)
where T : new ()
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Find<T> (pk);
}
});
}
public Task<T> GetAsync<T> (Expression<Func<T, bool>> predicate)
where T : new()
{
return Task.Factory.StartNew(() =>
{
var conn = GetConnection();
using (conn.Lock())
{
return conn.Get<T> (predicate);
}
});
}
public Task<T> FindAsync<T> (Expression<Func<T, bool>> predicate)
where T : new ()
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Find<T> (predicate);
}
});
}
public Task<int> ExecuteAsync (string query, params object[] args)
{
return Task<int>.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Execute (query, args);
}
});
}
public Task<int> InsertAllAsync (IEnumerable items)
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.InsertAll (items);
}
});
}
public Task<int> UpdateAllAsync (IEnumerable items)
{
return Task.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.UpdateAll (items);
}
});
}
[Obsolete("Will cause a deadlock if any call in action ends up in a different thread. Use RunInTransactionAsync(Action<SQLiteConnection>) instead.")]
public Task RunInTransactionAsync (Action<SQLiteAsyncConnection> action)
{
return Task.Factory.StartNew (() => {
var conn = this.GetConnection ();
using (conn.Lock ()) {
conn.BeginTransaction ();
try {
action (this);
conn.Commit ();
}
catch (Exception) {
conn.Rollback ();
throw;
}
}
});
}
public Task RunInTransactionAsync(Action<SQLiteConnection> action)
{
return Task.Factory.StartNew(() =>
{
var conn = this.GetConnection();
using (conn.Lock())
{
conn.BeginTransaction();
try
{
action(conn);
conn.Commit();
}
catch (Exception)
{
conn.Rollback();
throw;
}
}
});
}
public AsyncTableQuery<T> Table<T> ()
where T : new ()
{
//
// This isn't async as the underlying connection doesn't go out to the database
// until the query is performed. The Async methods are on the query iteself.
//
var conn = GetConnection ();
return new AsyncTableQuery<T> (conn.Table<T> ());
}
public Task<T> ExecuteScalarAsync<T> (string sql, params object[] args)
{
return Task<T>.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
var command = conn.CreateCommand (sql, args);
return command.ExecuteScalar<T> ();
}
});
}
public Task<List<T>> QueryAsync<T> (string sql, params object[] args)
where T : new ()
{
return Task<List<T>>.Factory.StartNew (() => {
var conn = GetConnection ();
using (conn.Lock ()) {
return conn.Query<T> (sql, args);
}
});
}
}
//
// TODO: Bind to AsyncConnection.GetConnection instead so that delayed
// execution can still work after a Pool.Reset.
//
public class AsyncTableQuery<T>
where T : new ()
{
TableQuery<T> _innerQuery;
public AsyncTableQuery (TableQuery<T> innerQuery)
{
_innerQuery = innerQuery;
}
public AsyncTableQuery<T> Where (Expression<Func<T, bool>> predExpr)
{
return new AsyncTableQuery<T> (_innerQuery.Where (predExpr));
}
public AsyncTableQuery<T> Skip (int n)
{
return new AsyncTableQuery<T> (_innerQuery.Skip (n));
}
public AsyncTableQuery<T> Take (int n)
{
return new AsyncTableQuery<T> (_innerQuery.Take (n));
}
public AsyncTableQuery<T> OrderBy<U> (Expression<Func<T, U>> orderExpr)
{
return new AsyncTableQuery<T> (_innerQuery.OrderBy<U> (orderExpr));
}
public AsyncTableQuery<T> OrderByDescending<U> (Expression<Func<T, U>> orderExpr)
{
return new AsyncTableQuery<T> (_innerQuery.OrderByDescending<U> (orderExpr));
}
public Task<List<T>> ToListAsync ()
{
return Task.Factory.StartNew (() => {
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) {
return _innerQuery.ToList ();
}
});
}
public Task<int> CountAsync ()
{
return Task.Factory.StartNew (() => {
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) {
return _innerQuery.Count ();
}
});
}
public Task<T> ElementAtAsync (int index)
{
return Task.Factory.StartNew (() => {
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) {
return _innerQuery.ElementAt (index);
}
});
}
public Task<T> FirstAsync ()
{
return Task<T>.Factory.StartNew(() => {
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) {
return _innerQuery.First ();
}
});
}
public Task<T> FirstOrDefaultAsync ()
{
return Task<T>.Factory.StartNew(() => {
using (((SQLiteConnectionWithLock)_innerQuery.Connection).Lock ()) {
return _innerQuery.FirstOrDefault ();
}
});
}
}
public class CreateTablesResult
{
public Dictionary<Type, int> Results { get; private set; }
internal CreateTablesResult ()
{
this.Results = new Dictionary<Type, int> ();
}
}
class SQLiteConnectionPool
{
class Entry
{
public SQLiteConnectionString ConnectionString { get; private set; }
public SQLiteConnectionWithLock Connection { get; private set; }
public Entry (SQLiteConnectionString connectionString, SQLiteOpenFlags openFlags)
{
ConnectionString = connectionString;
Connection = new SQLiteConnectionWithLock (connectionString, openFlags);
}
public void OnApplicationSuspended ()
{
Connection.Dispose ();
Connection = null;
}
}
readonly Dictionary<string, Entry> _entries = new Dictionary<string, Entry> ();
readonly object _entriesLock = new object ();
static readonly SQLiteConnectionPool _shared = new SQLiteConnectionPool ();
/// <summary>
/// Gets the singleton instance of the connection tool.
/// </summary>
public static SQLiteConnectionPool Shared
{
get
{
return _shared;
}
}
public SQLiteConnectionWithLock GetConnection (SQLiteConnectionString connectionString, SQLiteOpenFlags openFlags)
{
lock (_entriesLock) {
Entry entry;
string key = connectionString.ConnectionString;
if (!_entries.TryGetValue (key, out entry)) {
entry = new Entry (connectionString, openFlags);
_entries[key] = entry;
}
return entry.Connection;
}
}
/// <summary>
/// Closes all connections managed by this pool.
/// </summary>
public void Reset ()
{
lock (_entriesLock) {
foreach (var entry in _entries.Values) {
entry.OnApplicationSuspended ();
}
_entries.Clear ();
}
}
/// <summary>
/// Call this method when the application is suspended.
/// </summary>
/// <remarks>Behaviour here is to close any open connections.</remarks>
public void ApplicationSuspended ()
{
Reset ();
}
}
class SQLiteConnectionWithLock : SQLiteConnection
{
readonly object _lockPoint = new object ();
public SQLiteConnectionWithLock (SQLiteConnectionString connectionString, SQLiteOpenFlags openFlags)
: base (connectionString.DatabasePath, openFlags, connectionString.StoreDateTimeAsTicks)
{
}
public IDisposable Lock ()
{
return new LockWrapper (_lockPoint);
}
private class LockWrapper : IDisposable
{
object _lockPoint;
public LockWrapper (object lockPoint)
{
_lockPoint = lockPoint;
Monitor.Enter (_lockPoint);
}
public void Dispose ()
{
Monitor.Exit (_lockPoint);
}
}
}
}

View File

@ -0,0 +1,8 @@
namespace NadekoBot.DataModels
{
internal class TestDataModel : IDataModel
{
public long TestNumber { get; set; }
public string TestString { get; set; }
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -10,7 +10,12 @@
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.3" targetFramework="net46" />
<package id="RestSharp" version="105.2.3" targetFramework="net452" />
<package id="sqlite-net" version="1.0.8" targetFramework="net452" />
<package id="sqlite-net-pcl" version="1.1.2" targetFramework="net452" />
<package id="SQLitePCL.bundle_green" version="0.9.2" targetFramework="net452" />
<package id="SQLitePCL.native.sqlite3.v110_xp" version="0.9.2" targetFramework="net452" />
<package id="SQLitePCL.plugin.sqlite3.net45" version="0.9.2" targetFramework="net452" />
<package id="SQLitePCL.raw" version="0.9.2" targetFramework="net452" />
<package id="System.Data.SQLite" version="1.0.102.0" targetFramework="net452" />
<package id="taglib" version="2.1.0.0" targetFramework="net452" />
<package id="VideoLibrary" version="1.3.3" targetFramework="net452" />
</packages>

View File

@ -1,62 +1,63 @@
######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/**
######You can donate on paypal: `nadekodiscordbot@gmail.com`
######You can donate on patreon: `https://patreon.com/nadekobot`
######or paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands
Version: `NadekoBot v0.9.6051.26856`
Version: `NadekoBot v0.9.6054.4837`
### Help
Command and aliases | Description | Usage
----------------|--------------|-------
`-h`, `-help`, `@BotName help`, `@BotName h`, `~h` | Either shows a help for a single command, or PMs you help link if no arguments are specified. | '-h !m q' or just '-h'
`-hgit` | Generates the commandlist.md file. **Bot Owner Only!**
`-readme`, `-guide` | Sends a readme and a guide links to the channel.
`-donate`, `~donate` | Instructions for helping the project!
`-modules`, `.modules` | List all bot modules.
`-commands`, `.commands` | List all of the bot's commands from a certain module.
`-h`, `-help`, `@BotName help`, `@BotName h`, `~h` | Either shows a help for a single command, or PMs you help link if no arguments are specified. | `-h !m q` or just `-h`
`-hgit` | Generates the commandlist.md file. **Bot Owner Only!** | `-hgit`
`-readme`, `-guide` | Sends a readme and a guide links to the channel. | `-readme` or `-guide`
`-donate`, `~donate` | Instructions for helping the project! | `{Prefix}donate` or `~donate`
`-modules`, `.modules` | List all bot modules. | `{Prefix}modules` or `.modules`
`-commands`, `.commands` | List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`
### Administration
Command and aliases | Description | Usage
----------------|--------------|-------
`.grdel` | Toggles automatic deletion of greet and bye messages.
`.greet` | Toggles anouncements on the current channel when someone joins the server.
`.greetmsg` | Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. | .greetmsg Welcome to the server, %user%.
`.bye` | Toggles anouncements on the current channel when someone leaves the server.
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message. | .byemsg %user% has left the server.
`.byepm` | Toggles whether the good bye messages will be sent in a PM or in the text channel.
`.greetpm` | Toggles whether the greet messages will be sent in a PM or in the text channel.
`.spmom` | Toggles whether mentions of other offline users on your server will send a pm to them.
`.logserver` | Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!**
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.
`.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle.
`.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now.
`.repeatinvoke`, `.repinv` | Immediately shows the repeat message and restarts the timer.
`.grdel` | Toggles automatic deletion of greet and bye messages. | `.grdel`
`.greet` | Toggles anouncements on the current channel when someone joins the server. | `.greet`
`.greetmsg` | Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. | `.greetmsg Welcome to the server, %user%.`
`.bye` | Toggles anouncements on the current channel when someone leaves the server. | `.bye`
`.byemsg` | Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message. | `.byemsg %user% has left the server.`
`.byepm` | Toggles whether the good bye messages will be sent in a PM or in the text channel. | `.byepm`
`.greetpm` | Toggles whether the greet messages will be sent in a PM or in the text channel. | `.greetpm`
`.spmom` | Toggles whether mentions of other offline users on your server will send a pm to them. | `.spmom`
`.logserver` | Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!** | `.logserver`
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. | `.logignore`
`.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. | `.userpresence`
`.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now. | `{Prefix}voicerpresence`
`.repeatinvoke`, `.repinv` | Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`
`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. Requires manage messages. | `.repeat 5 Hello there`
`.rotateplaying`, `.ropl` | Toggles rotation of playing status of the dynamic strings you specified earlier.
`.addplaying`, `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %trivia%
`.listplaying`, `.lipl` | Lists all playing statuses with their corresponding number.
`.removeplaying`, `.repl`, `.rmpl` | Removes a playing string on a given number.
`.slowmode` | Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds.
`.cleanv+t`, `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk.**
`.voice+text`, `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless.
`.scsc` | Starts an instance of cross server channel. You will get a token as a DMthat other people will use to tune in to the same instance
`.jcsc` | Joins current channel to an instance of cross server channel using the token.
`.lcsc` | Leaves Cross server channel instance from this channel
`.rotateplaying`, `.ropl` | Toggles rotation of playing status of the dynamic strings you specified earlier. | `.ropl`
`.addplaying`, `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %trivia% | `.adpl`
`.listplaying`, `.lipl` | Lists all playing statuses with their corresponding number. | `.lipl`
`.removeplaying`, `.repl`, `.rmpl` | Removes a playing string on a given number. | `.rmpl`
`.slowmode` | Toggles slow mode. When ON, users will be able to send only 1 message every 5 seconds. | `.slowmode`
`.cleanv+t`, `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. **Use at your own risk.** | `.cleanv+t`
`.voice+text`, `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. | `.voice+text`
`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. | `.scsc`
`.jcsc` | Joins current channel to an instance of cross server channel using the token. | `.jcsc`
`.lcsc` | Leaves Cross server channel instance from this channel. | `.lcsc`
`.asar` | Adds a role, or list of roles separated by whitespace(use quotations for multiword roles) to the list of self-assignable roles. | .asar Gamer
`.rsar` | Removes a specified role from the list of self-assignable roles.
`.lsar` | Lists all self-assignable roles.
`.togglexclsar`, `.tesar` | toggle whether the self-assigned roles should be exclusive
`.rsar` | Removes a specified role from the list of self-assignable roles. | `.rsar`
`.lsar` | Lists all self-assignable roles. | `.lsar`
`.togglexclsar`, `.tesar` | toggle whether the self-assigned roles should be exclusive | `.tesar`
`.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
`.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 custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. | .lcr 1
`.showcustreact`, `.scr` | Shows all possible responses from a single custom reaction. | .scr %mention% bb
`.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 custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. | `.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)
`.delcustreact`, `.dcr` | Deletes a custom reaction with given name (and index). | `.dcr 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`
`.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**
`.listincidents`, `.lin` | List all UNREAD incidents and flags them as read. | `.lin`
`.listallincidents`, `.lain` | Sends you a file containing all incidents and flags them as read. | `.lain`
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only. | `.delmsgoncmd`
`.restart` | Restarts the bot. Might not work. **Bot Owner Only** | `.restart`
`.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`
@ -76,15 +77,15 @@ Command and aliases | Description | Usage
`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name. | `.ctch TextChannelName`
`.settopic`, `.st` | Sets a topic on the current channel. | `.st My new topic`
`.setchanlname`, `.schn` | Changed the name of the current channel.| `.schn NewName`
`.heap` | Shows allocated memory - **Bot Owner Only!**
`.heap` | Shows allocated memory - **Bot Owner Only!** | `.heap`
`.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!**
`.die` | Shuts the bot down and notifies users about the restart. **Bot Owner Only!** | `.die`
`.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!** | .newnm BotName
`.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!** | `.setgame Playing with kwoth`
`.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. | `.menro RoleName`
`.unstuck` | Clears the message queue. **Bot Owner Only!**
`.unstuck` | Clears the message queue. **Bot Owner Only!** | `.unstuck`
`.donators` | List of lovely people who donated to keep this project alive.
`.donadd` | Add a donator to the database. | `.donadd Donate Amount`
`.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | `.announce Useless spam`
@ -94,16 +95,16 @@ Command and aliases | Description | Usage
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
`.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!** | `.remindmsg do something else`
`.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. | `.whoplays Overwatch`
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role`
`.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. | `.uid` or `.uid "@SomeGuy"
`.checkmyperms` | Checks your userspecific permissions on this channel. | `.checkmyperms`
`.stats` | Shows some basic stats for Nadeko. | `.stats`
`.dysyd` | Shows some basic stats for Nadeko. | `.dysyd`
`.userid`, `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"`
`.channelid`, `.cid` | Shows current channel ID. | `.cid`
`.serverid`, `.sid` | Shows current server ID. | `.sid`
`.roles` | List all roles on this server or a single user if specified.
@ -119,12 +120,12 @@ Command and aliases | Description | Usage
`;rmvfilterword`, `;rfw` | Removes the word from the list of filtered words | ;rw poop
`;lstfilterwords`, `;lfw` | Shows a list of filtered words | ;lfw
`;srvrfilterwords`, `;sfw` | Enables or disables automatic deleting of messages containing forbidden words on the server. | ;sfw disable
`;permrole`, `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'.
`;permrole`, `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `;pr role`
`;rolepermscopy`, `;rpc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;rpc Some Role ~ Some other role`
`;chnlpermscopy`, `;cpc` | Copies BOT PERMISSIONS (not discord permissions) from one channel to another. | `;cpc Some Channel ~ Some other channel`
`;usrpermscopy`, `;upc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;upc @SomeUser ~ @SomeOtherUser`
`;verbose`, `;v` | Sets whether to show when a command/module is blocked. | `;verbose true`
`;srvrperms`, `;sp` | Shows banned permissions for this server.
`;srvrperms`, `;sp` | Shows banned permissions for this server. | `;sp`
`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | `;rp AwesomeRole`
`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | `;cp #dev`
`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | `;up Kwoth`
@ -157,48 +158,48 @@ Command and aliases | Description | Usage
`...` | 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 rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000
`@BotName die` | Works only for the owner. Shuts the bot down.
`@BotName do you love me` | Replies with positive answer only to the bot owner.
`@BotName how are you`, `@BotName how are you?` | Replies positive only if bot owner is online.
`@BotName fire` | Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | @NadekoBot fire [x]
`@BotName dump` | Dumps all of the invites it can to dump.txt.** Owner Only.**
`@BotName ab` | Try to get 'abalabahaha'
`@BotName die` | Works only for the owner. Shuts the bot down. | `@NadekoBot die`
`@BotName do you love me` | Replies with positive answer only to the bot owner. | `@NadekoBot do you love me`
`@BotName how are you`, `@BotName how are you?` | Replies positive only if bot owner is online. | `@NadekoBot how are you`
`@BotName fire` | Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | `@NadekoBot fire [x]`
`@BotName dump` | Dumps all of the invites it can to dump.txt.** Owner Only.** | `@NadekoBot dump`
`@BotName ab` | Try to get 'abalabahaha'| `@NadekoBot ab`
### Gambling
Command and aliases | Description | Usage
----------------|--------------|-------
`$draw` | Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | $draw [x]
`$shuffle`, `$sh` | Reshuffles all cards back into the deck.
`$draw` | Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | `$draw [x]`
`$shuffle`, `$sh` | Reshuffles all cards back into the deck.|`$shuffle`
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
`$betflip`, `$bf` | Bet to guess will the result be heads or tails. Guessing award you double flowers you've bet. | `$bf 5 heads` or `$bf 3 t`
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5
`$rolluo` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered). If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$roll` or `$roll 7` or `$roll 3d5`
`$rolluo` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered). If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$roll` or `$roll` 7 or `$roll 3d5`
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$race` | Starts a new animal race.
`$race` | Starts a new animal race. | `$race`
`$joinrace`, `$jr` | Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName`
`$$$` | Check how much NadekoFlowers a person has. (Defaults to yourself) | `$$$` or `$$$ @Someone`
`$give` | Give someone a certain amount of NadekoFlowers
`$give` | Give someone a certain amount of NadekoFlowers|`$give 1 "@SomeGuy"`
`$award` | Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person`
`$take` | Takes a certain amount of flowers from someone. **Bot Owner Only!** | `$take 1 "@someguy"`
`$betroll`, `$br` | Bets a certain amount of NadekoFlowers and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | $br 5
`$leaderboard`, `$lb` |
`$betroll`, `$br` | Bets a certain amount of NadekoFlowers and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | `$br 5`
`$leaderboard`, `$lb` | Displays bot currency leaderboard | $lb
### Games
Command and aliases | Description | Usage
----------------|--------------|-------
`>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t nohint` or `>t 5 nohint`
`>tl` | Shows a current trivia leaderboard.
`>tq` | Quits current trivia after current question.
`>typestart` | Starts a typing contest.
`>typestop` | Stops a typing contest on the current channel.
`>typeadd` | Adds a new article to the typing contest. Owner only.
`>poll` | Creates a poll, only person who has manage server permission can do it. | >poll Question?;Answer1;Answ 2;A_3
`>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)
`>tl` | Shows a current trivia leaderboard. | `>tl`
`>tq` | Quits current trivia after current question. | `>tq`
`>typestart` | Starts a typing contest. | `>typestart`
`>typestop` | Stops a typing contest on the current channel. | `>typestop`
`>typeadd` | Adds a new article to the typing contest. Owner only. | `>typeadd wordswords`
`>poll` | Creates a poll, only person who has manage server permission can do it. | `>poll Question?;Answer1;Answ 2;A_3`
`>pollend` | Stops active poll on this server and prints the results in this channel. | `>pollend`
`>pick` | Picks a flower planted in this channel. | `>pick`
`>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) | `>plant`
`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello
`>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. | `>8ball should i do something`
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
@ -237,37 +238,37 @@ Command and aliases | Description | Usage
`!!load` | Loads a playlist under a certain name. | `!!load classical-1`
`!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
`!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
`!!goto` | Goes to a specific time in seconds in a song. | !!goto 30
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
`!!getlink`, `!!gl` | Shows a link to the currently playing song.
`!!autoplay`, `!!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
----------------|--------------|-------
`~lolchamp` | Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. | ~lolchamp Riven or ~lolchamp Annie sup
`~lolban` | Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time.
`~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`
`~lolchamp` | Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. | `~lolchamp Riven` or `~lolchamp Annie sup`
`~lolban` | Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time. | `~lolban`
`~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. | `~convert m>km 1000`
`~convertlist` | List of the convertable dimensions and currencies.
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes.
`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke`
`~calculate`, `~calc` | Evaluate a mathematical expression. | ~calc 1+1
`~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 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`, `~pokeab` | Searches for a pokemon ability.
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/
`~pokemon`, `~poke` | Searches for a pokemon. | `~poke Sylveon`
`~pokemonability`, `~pokeab` | Searches for a pokemon ability. | `~pokeab "water gun"`
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist`
`~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 | `~yt query`
`~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result.
`~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result. | `~aq aquerion evol`
`~imdb` | Queries imdb for movies or series, show first result. | `~imdb query`
`~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the first result. | `~mq query`
`~randomcat`, `~meow` | Shows a random cat image.
@ -279,13 +280,13 @@ Command and aliases | Description | Usage
`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
`~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~quote` | Shows a random quote.
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts>
`~yomama`, `~ym` | Shows a random joke from <http://api.yomomma.info/>
`~randjoke`, `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random>
`~chucknorris`, `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random>
`~magicitem`, `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items>
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"
`~quote` | Shows a random quote. | `~quote`
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact`
`~yomama`, `~ym` | Shows a random joke from <http://api.yomomma.info/> | `~ym`
`~randjoke`, `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> | `~rj`
`~chucknorris`, `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> | `~cn`
`~magicitem`, `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> | `~mi`
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"`
`~revimg` | Returns a google reverse image search for an image from a link. | `~revav Image link`
`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~safebooru yuri+kissing`
`~wiki` | Gives you back a wikipedia link | `~wiki query`
@ -301,15 +302,15 @@ Command and aliases | Description | Usage
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~rule34 yuri+kissing`
`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | `~e621 yuri kissing`
`~cp` | We all know where this will lead you to.
`~boobs` | Real adult content.
`~butts`, `~ass`, `~butt` | Real adult content.
`~cp` | We all know where this will lead you to. | `~cp`
`~boobs` | Real adult content. | `~boobs`
`~butts`, `~ass`, `~butt` | Real adult content. | `~butts` or `~ass`
### ClashOfClans
Command and aliases | Description | Usage
----------------|--------------|-------
`,createwar`, `,cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. | ,cw 15 The Enemy Clan
`,startwar`, `,sw` | Starts a war with a given number. | `,sw 1`
`,startwar`, `,sw` | Starts a war with a given number.
`,listwar`, `,lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | ,lw [war_number] or ,lw
`,claim`, `,call`, `,c` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | ,call [war_number] [base_number] [optional_other_name]
`,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
@ -322,16 +323,16 @@ Command and aliases | Description | Usage
Command and aliases | Description | Usage
----------------|--------------|-------
`>attack` | Attacks a target with the given move. Use `>movelist` to see a list of moves your type can use. | `>attack "vine whip" @someguy`
`>movelist`, `>ml` | Lists the moves you are able to use
`>heal` | Heals someone. Revives those who fainted. Costs a NadekoFlower | >heal @someone
`>type` | Get the poketype of the target. | >type @someone
`>settype` | Set your poketype. Costs a NadekoFlower. | >settype fire
`>movelist`, `>ml` | Lists the moves you are able to use | `>ml`
`>heal` | Heals someone. Revives those who fainted. Costs a NadekoFlower | `>heal @someone`
`>type` | Get the poketype of the target. | `>type @someone`
`>settype` | Set your poketype. Costs a NadekoFlower. | `>settype fire`
### Translator
Command and aliases | Description | Usage
----------------|--------------|-------
`~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | `~trans en>fr Hello`
`~translangs` | List the valid languages for translation.
`~translangs` | List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`
### Customreactions
Command and aliases | Description | Usage