Merge pull request #53 from Kwoth/dev

ups
This commit is contained in:
samvaio 2016-12-28 19:09:54 +05:30 committed by GitHub
commit 9cd81f4e39
32 changed files with 1546 additions and 1360 deletions

View File

@ -59,8 +59,8 @@ Command and aliases | Description | Usage
`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` `.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall`
`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` `.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable`
`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` `.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore`
`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** | `.logevents` `.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents`
`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a lit of all events you can subscribe to. **Requires Administrator server permission.** | `.log userpresence` or `.log userbanned` `.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a lit of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned`
`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` `.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv`
`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there`
`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` `.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata`
@ -137,7 +137,7 @@ Command and aliases | Description | Usage
`$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb` `$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb`
`$race` | Starts a new animal race. | `$race` `$race` | Starts a new animal race. | `$race`
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` `$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$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` `$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. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` `$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` `$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` `$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
@ -195,6 +195,7 @@ Command and aliases | Description | Usage
`!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s` `!!stop` `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s`
`!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d` `!!destroy` `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d`
`!!pause` `!!p` | Pauses or Unpauses the song. | `!!p` `!!pause` `!!p` | Pauses or Unpauses the song. | `!!p`
`!!fairplay` `!!fp` | Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. | `!!fp`
`!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice` `!!queue` `!!q` `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice`
`!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice` `!!soundcloudqueue` `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice`
`!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2` `!!listqueue` `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2`
@ -211,6 +212,7 @@ Command and aliases | Description | Usage
`!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5` `!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
`!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3` `!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3`
`!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq` `!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq`
`!!setmaxplaytime` `!!smp` | Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. | `!!smp 0` or `!!smp 270`
`!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs` `!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs`
`!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl` `!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl`
`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1` `!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1`
@ -218,7 +220,6 @@ Command and aliases | Description | Usage
`!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 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` `!!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 song in the queue by index, or the currently playing song by default. | `!!gl`
`!!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) | `!!ap` `!!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) | `!!ap`
###### [Back to TOC](#table-of-contents) ###### [Back to TOC](#table-of-contents)
@ -228,8 +229,8 @@ Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri` `~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` `~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` `~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri` `~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri`
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing` `~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing` `~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing`
@ -302,6 +303,7 @@ Command and aliases | Description | Usage
`~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera` `~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
`~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt` `~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt`
`~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple` `~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~define` `~def` | Finds a definition of a word. | `~def heresy`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff` `~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact` `~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> | `~catfact`
`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"` `~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"`
@ -349,6 +351,7 @@ Command and aliases | Description | Usage
### Utility ### Utility
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`.togethertube` `.totube` | Creates a new room on <https://togethertube.com> and shows the link in the chat. | `.totube`
`.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch` `.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl 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` `.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 user-specific permissions on this channel. | `.checkmyperms` `.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms`

View File

@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Administration
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
} }
private static async Task DelMsgOnCmd_Handler(IUserMessage msg, Command cmd) private static async Task DelMsgOnCmd_Handler(IUserMessage msg, Command cmd)
@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Administration
PermRole = config.PermissionRole, PermRole = config.PermissionRole,
Verbose = config.VerbosePermissions, Verbose = config.VerbosePermissions,
}; };
Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id, Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id,
toAdd, (id, old) => toAdd); toAdd, (id, old) => toAdd);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Administration
return; return;
} }
var roleName = args[0].ToUpperInvariant(); var roleName = args[0].ToUpperInvariant();
var role = channel.Guild.Roles.Where(r=>r.Name.ToUpperInvariant() == roleName).FirstOrDefault(); var role = channel.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault();
if (role == null) if (role == null)
{ {
@ -234,7 +234,7 @@ namespace NadekoBot.Modules.Administration
var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16)); var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16));
var green = Convert.ToByte(rgb ? int.Parse(args[2]) : Convert.ToInt32(arg1.Substring(2, 2), 16)); var green = Convert.ToByte(rgb ? int.Parse(args[2]) : Convert.ToInt32(arg1.Substring(2, 2), 16));
var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16)); var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
await role.ModifyAsync(r => r.Color = new Discord.Color(red, green, blue).RawValue).ConfigureAwait(false); await role.ModifyAsync(r => r.Color = new Discord.Color(red, green, blue).RawValue).ConfigureAwait(false);
await channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false); await channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false);
} }
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Administration
{ {
msg = "❗No reason provided."; msg = "❗No reason provided.";
} }
if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r=>r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max()) if (umsg.Author.Id != user.Guild.OwnerId && user.Roles.Select(r => r.Position).Max() >= ((IGuildUser)umsg.Author).Roles.Select(r => r.Position).Max())
{ {
await channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.");
return; return;
@ -367,7 +367,7 @@ namespace NadekoBot.Modules.Administration
{ {
foreach (var u in users) foreach (var u in users)
{ {
await u.ModifyAsync(usr=>usr.Deaf = true).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
} }
await channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false); await channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false);
} }
@ -390,7 +390,7 @@ namespace NadekoBot.Modules.Administration
{ {
foreach (var u in users) foreach (var u in users)
{ {
await u.ModifyAsync(usr=> usr.Deaf = false).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
} }
await channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false); await channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false);
} }
@ -467,9 +467,9 @@ namespace NadekoBot.Modules.Administration
public async Task Prune(IUserMessage umsg) public async Task Prune(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
var user = channel.Guild.GetCurrentUser(); var user = channel.Guild.GetCurrentUser();
var enumerable = (await umsg.Channel.GetMessagesAsync()).AsEnumerable(); var enumerable = (await umsg.Channel.GetMessagesAsync()).AsEnumerable();
enumerable = enumerable.Where(x => x.Author.Id == user.Id); enumerable = enumerable.Where(x => x.Author.Id == user.Id);
await umsg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); await umsg.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
@ -650,8 +650,10 @@ namespace NadekoBot.Modules.Administration
cnt -= 100; cnt -= 100;
} }
var title = $"Chatlog-{channel.Guild.Name}/#{channel.Name}-{DateTime.Now}.txt"; var title = $"Chatlog-{channel.Guild.Name}/#{channel.Name}-{DateTime.Now}.txt";
var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}")
.Select(g => new { date = g.Key, messages = g.OrderBy(x=>x.CreatedAt).Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) });
await (umsg.Author as IGuildUser).SendFileAsync( await (umsg.Author as IGuildUser).SendFileAsync(
await JsonConvert.SerializeObject(new { Messages = msgs.Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }, Formatting.Indented).ToStream().ConfigureAwait(false), await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false),
title, title).ConfigureAwait(false); title, title).ConfigureAwait(false);
} }
@ -665,9 +667,9 @@ namespace NadekoBot.Modules.Administration
string send = $"❕{umsg.Author.Mention} has invoked a mention on the following roles ❕"; string send = $"❕{umsg.Author.Mention} has invoked a mention on the following roles ❕";
foreach (var role in roles) foreach (var role in roles)
{ {
send += $"\n**{role.Name}**\n"; send += $"\n**{role.Name}**\n";
send += string.Join(", ", (await channel.Guild.GetUsersAsync()).Where(u => u.Roles.Contains(role)).Distinct().Select(u=>u.Mention)); send += string.Join(", ", (await channel.Guild.GetUsersAsync()).Where(u => u.Roles.Contains(role)).Distinct().Select(u => u.Mention));
} }
while (send.Length > 2000) while (send.Length > 2000)
@ -692,7 +694,7 @@ namespace NadekoBot.Modules.Administration
donatorsOrdered = uow.Donators.GetDonatorsOrdered(); donatorsOrdered = uow.Donators.GetDonatorsOrdered();
} }
await umsg.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); await umsg.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
if (nadekoSupportServer == null) if (nadekoSupportServer == null)
@ -721,4 +723,4 @@ namespace NadekoBot.Modules.Administration
await umsg.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false); await umsg.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false);
} }
} }
} }

View File

@ -81,52 +81,46 @@ namespace NadekoBot.Modules.Administration
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (imsg) => NadekoBot.Client.MessageReceived += async (imsg) =>
{ {
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
var channel = msg.Channel as ITextChannel; try
if (channel == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
try var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return;
AntiSpamSetting spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return;
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
if (stats.Count >= spamSettings.MessageThreshold)
{ {
AntiSpamSetting spamSettings; if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings))
return;
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content),
(id, old) => { old.ApplyNextMessage(msg.Content); return old; });
if (stats.Count >= spamSettings.MessageThreshold)
{ {
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
{ .ConfigureAwait(false);
await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author)
.ConfigureAwait(false);
}
} }
} }
catch { } }
}); catch { }
return Task.CompletedTask;
}; };
NadekoBot.Client.UserJoined += (usr) => NadekoBot.Client.UserJoined += async (usr) =>
{ {
if (usr.IsBot) try
return Task.CompletedTask;
AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
if (usr.IsBot)
return;
AntiRaidSetting settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return;
if (!settings.RaidUsers.Add(usr)) if (!settings.RaidUsers.Add(usr))
return; return;
@ -143,9 +137,9 @@ namespace NadekoBot.Modules.Administration
settings.RaidUsers.TryRemove(usr); settings.RaidUsers.TryRemove(usr);
--settings.UsersCount; --settings.UsersCount;
});
return Task.CompletedTask; }
catch { }
}; };
} }
@ -241,7 +235,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.Administrator)] [RequirePermission(GuildPermission.Administrator)]
public async Task AntiSpam(IUserMessage imsg, int messageCount=3, PunishmentAction action = PunishmentAction.Mute) public async Task AntiSpam(IUserMessage imsg, int messageCount = 3, PunishmentAction action = PunishmentAction.Mute)
{ {
var channel = (ITextChannel)imsg.Channel; var channel = (ITextChannel)imsg.Channel;
@ -273,10 +267,10 @@ namespace NadekoBot.Modules.Administration
Action = action, Action = action,
MessageThreshold = messageCount, MessageThreshold = messageCount,
})) }))
await channel.SendConfirmAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false); await channel.SendConfirmAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false);
} }
} }
} }
} }
} }

View File

@ -26,26 +26,22 @@ namespace NadekoBot.Modules.Administration
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId)); .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.UserJoined += (user) => NadekoBot.Client.UserJoined += async (user) =>
{ {
var t = Task.Run(async () => try
{ {
try ulong roleId = 0;
{ AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
ulong roleId = 0;
AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
if (roleId == 0) if (roleId == 0)
return; return;
var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId); var role = user.Guild.Roles.FirstOrDefault(r => r.Id == roleId);
if (role != null) if (role != null)
await user.AddRolesAsync(role).ConfigureAwait(false); await user.AddRolesAsync(role).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}; };
} }

View File

@ -19,21 +19,18 @@ namespace NadekoBot.Modules.Administration
static CrossServerTextChannel() static CrossServerTextChannel()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (imsg) => NadekoBot.Client.MessageReceived += async (imsg) =>
{ {
if (imsg.Author.IsBot) try
return Task.CompletedTask;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
Task.Run(async () =>
{ {
if (imsg.Author.IsBot)
return;
var msg = imsg as IUserMessage;
if (msg == null)
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return;
if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; if (msg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return;
foreach (var subscriber in Subscribers) foreach (var subscriber in Subscribers)
{ {
@ -45,8 +42,10 @@ namespace NadekoBot.Modules.Administration
try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
} }
}); }
return Task.CompletedTask; catch (Exception ex) {
_log.Warn(ex);
}
}; };
} }

View File

@ -75,6 +75,7 @@ namespace NadekoBot.Modules.Administration
_client.UserLeft += _client_UserLeft; _client.UserLeft += _client_UserLeft;
_client.UserPresenceUpdated += _client_UserPresenceUpdated; _client.UserPresenceUpdated += _client_UserPresenceUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
_client.UserUpdated += _client_UserUpdated; _client.UserUpdated += _client_UserUpdated;
_client.ChannelCreated += _client_ChannelCreated; _client.ChannelCreated += _client_ChannelCreated;
@ -85,51 +86,60 @@ namespace NadekoBot.Modules.Administration
MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
} }
private Task MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType) private async void _client_UserVoiceStateUpdated_TTS(IUser iusr, IVoiceState before, IVoiceState after)
{ {
LogSetting logSetting; try
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserMutedId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{ {
string mutes = ""; var usr = iusr as IGuildUser;
switch (muteType) if (usr == null)
{ return;
case MuteCommands.MuteType.Voice:
mutes = "voice chat";
break;
case MuteCommands.MuteType.Chat:
mutes = "text chat";
break;
case MuteCommands.MuteType.All:
mutes = "text and voice chat";
break;
}
try { await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔇 **| User muted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask; var beforeVch = before.VoiceChannel;
var afterVch = after.VoiceChannel;
if (beforeVch == afterVch)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.LogVoicePresenceTTSId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTTS)) == null)
return;
string str = null;
if (beforeVch?.Guild == afterVch?.Guild)
{
str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}";
}
else if (beforeVch == null)
{
str = $"{usr.Username} has joined {afterVch.Name}";
}
else if (afterVch == null)
{
str = $"{usr.Username} has left {beforeVch.Name}";
}
var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
catch { }
} }
private Task MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType) private async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType)
{ {
LogSetting logSetting; try
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserMutedId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{ {
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserMutedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return;
string mutes = ""; string mutes = "";
switch (muteType) switch (muteType)
{ {
@ -143,392 +153,404 @@ namespace NadekoBot.Modules.Administration
mutes = "text and voice chat"; mutes = "text and voice chat";
break; break;
} }
try { await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔊 **| User unmuted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔇 **| User muted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}); }
catch (Exception ex) { _log.Warn(ex); }
}
return Task.CompletedTask; private async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType)
{
try
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserMutedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return;
string mutes = "";
switch (muteType)
{
case MuteCommands.MuteType.Voice:
mutes = "voice chat";
break;
case MuteCommands.MuteType.Chat:
mutes = "text chat";
break;
case MuteCommands.MuteType.All:
mutes = "text and voice chat";
break;
}
await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔊 **| User unmuted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
} }
public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection) public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection)
{ {
if (users.Length == 0) try
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(users.First().Guild.Id, out logSetting)
|| (logSetting.LogOtherId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(users.First().Guild, logSetting, LogType.Other)) == null)
return;
var punishment = "";
if (action == PunishmentAction.Mute)
{ {
punishment = "🔇 MUTED"; if (users.Length == 0)
//punishment = "MUTED"; return;
}
else if (action == PunishmentAction.Kick)
{
punishment = "☣ SOFT-BANNED (KICKED)";
//punishment = "KICKED";
}
else if (action == PunishmentAction.Ban)
{
punishment = "⛔️ BANNED";
//punishment = "BANNED";
}
await logChannel.SendMessageAsync(String.Join("\n", users.Select(user => $"‼️ {Format.Bold(user.ToString())} got **{punishment}** due to __**{protection}**__ protection on **{user.Guild.Name}** server.")))
//await logChannel.SendMessageAsync(String.Join("\n",users.Select(user=>$"{Format.Bold(user.ToString())} was **{punishment}** due to `{protection}` protection on **{user.Guild.Name}** server.")))
.ConfigureAwait(false);
}
private Task _client_UserUpdated(IGuildUser before, IGuildUser after) LogSetting logSetting;
{ if (!GuildLogSettings.TryGetValue(users.First().Guild.Id, out logSetting)
LogSetting logSetting; || (logSetting.LogOtherId == null))
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting) return;
|| (logSetting.UserUpdatedId == null)) ITextChannel logChannel;
return Task.CompletedTask; if ((logChannel = TryGetLogChannel(users.First().Guild, logSetting, LogType.Other)) == null)
return;
ITextChannel logChannel; var punishment = "";
if ((logChannel = TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null) if (action == PunishmentAction.Mute)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try
{ {
string str = $"🕔`{prettyCurrentTime}`"; punishment = "🔇 MUTED";
if (before.Username != after.Username) //punishment = "MUTED";
//str += $"**Name Changed**`{before.Username}#{before.Discriminator}`\n\t\t`New:`{after.ToString()}`";
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**";
else if (before.Nickname != after.Nickname)
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}#{before.Discriminator}**\n\t\t`New:` **{after.Nickname}#{after.Discriminator}**";
//str += $"**Nickname Changed**`{before.Username}#{before.Discriminator}`\n\t\t`Old:` {before.Nickname}#{before.Discriminator}\n\t\t`New:` {after.Nickname}#{after.Discriminator}";
else if (before.AvatarUrl != after.AvatarUrl)
//str += $"**Avatar Changed**👤`{before.Username}#{before.Discriminator}`\n\t {await _google.ShortenUrl(before.AvatarUrl)} `=>` {await _google.ShortenUrl(after.AvatarUrl)}";
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await _google.ShortenUrl(before.AvatarUrl)} `=>` {await _google.ShortenUrl(after.AvatarUrl)}";
else if (!before.Roles.SequenceEqual(after.Roles))
{
if (before.Roles.Count() < after.Roles.Count())
{
var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => "**" + r.Name + "**");
//str += $"**User's Roles changed ⚔➕**👤`{before.ToString()}`\n\tNow has {string.Join(", ", diffRoles)} role.";
str += $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.Roles.Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
else if (before.Roles.Count() > after.Roles.Count())
{
var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r)).Select(r => "**" + r.Name + "**");
//str += $"**User's Roles changed **`{before.ToString()}`\n\tNo longer has {string.Join(", ", diffRoles)} role.";
str += $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.Roles.Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
}
else
return;
try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
catch { } else if (action == PunishmentAction.Kick)
}); {
punishment = "☣ SOFT-BANNED (KICKED)";
return Task.CompletedTask; //punishment = "KICKED";
}
else if (action == PunishmentAction.Ban)
{
punishment = "⛔️ BANNED";
//punishment = "BANNED";
}
await logChannel.SendMessageAsync(String.Join("\n", users.Select(user => $"‼️ {Format.Bold(user.ToString())} got **{punishment}** due to __**{protection}**__ protection on **{user.Guild.Name}** server.")))
//await logChannel.SendMessageAsync(String.Join("\n",users.Select(user=>$"{Format.Bold(user.ToString())} was **{punishment}** due to `{protection}` protection on **{user.Guild.Name}** server.")))
.ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
} }
private Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) private async void _client_UserUpdated(IGuildUser before, IGuildUser after)
{ {
var before = cbefore as IGuildChannel; try
if (before == null)
return Task.CompletedTask;
var after = (IGuildChannel)cafter;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting)
|| (logSetting.ChannelUpdatedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{ {
try LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting)
|| (logSetting.UserUpdatedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null)
return;
string str = $"🕔`{prettyCurrentTime}`";
if (before.Username != after.Username)
//str += $"**Name Changed**`{before.Username}#{before.Discriminator}`\n\t\t`New:`{after.ToString()}`";
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**";
else if (before.Nickname != after.Nickname)
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}#{before.Discriminator}**\n\t\t`New:` **{after.Nickname}#{after.Discriminator}**";
//str += $"**Nickname Changed**`{before.Username}#{before.Discriminator}`\n\t\t`Old:` {before.Nickname}#{before.Discriminator}\n\t\t`New:` {after.Nickname}#{after.Discriminator}";
else if (before.AvatarUrl != after.AvatarUrl)
//str += $"**Avatar Changed**👤`{before.Username}#{before.Discriminator}`\n\t {await _google.ShortenUrl(before.AvatarUrl)} `=>` {await _google.ShortenUrl(after.AvatarUrl)}";
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await _google.ShortenUrl(before.AvatarUrl)} `=>` {await _google.ShortenUrl(after.AvatarUrl)}";
else if (!before.Roles.SequenceEqual(after.Roles))
{ {
if (before.Name != after.Name) if (before.Roles.Count() < after.Roles.Count())
//await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Name Changed** `#{after.Name}` ({after.Id}) {
await logChannel.SendMessageAsync($@"🕓`{prettyCurrentTime}` **| Channel Name Changed |** #⃣ `{after.Name} ({after.Id})` var diffRoles = after.Roles.Where(r => !before.Roles.Contains(r)).Select(r => "**" + r.Name + "**");
//str += $"**User's Roles changed ⚔➕**👤`{before.ToString()}`\n\tNow has {string.Join(", ", diffRoles)} role.";
str += $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.Roles.Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
else if (before.Roles.Count() > after.Roles.Count())
{
var diffRoles = before.Roles.Where(r => !after.Roles.Contains(r)).Select(r => "**" + r.Name + "**");
//str += $"**User's Roles changed **`{before.ToString()}`\n\tNo longer has {string.Join(", ", diffRoles)} role.";
str += $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.Roles.Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
}
else
return;
try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
}
catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_ChannelUpdated(IChannel cbefore, IChannel cafter)
{
try
{
var before = cbefore as IGuildChannel;
if (before == null)
return;
var after = (IGuildChannel)cafter;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting)
|| (logSetting.ChannelUpdatedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == after.Id))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) == null)
return;
if (before.Name != after.Name)
//await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Name Changed** `#{after.Name}` ({after.Id})
await logChannel.SendMessageAsync($@"🕓`{prettyCurrentTime}` **| Channel Name Changed |** #⃣ `{after.Name} ({after.Id})`
`Old:` {before.Name} `Old:` {before.Name}
**`New:`** {after.Name}").ConfigureAwait(false); **`New:`** {after.Name}").ConfigureAwait(false);
else if ((before as ITextChannel).Topic != (after as ITextChannel).Topic) else if ((before as ITextChannel).Topic != (after as ITextChannel).Topic)
//await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Topic Changed** `#{after.Name}` ({after.Id}) //await logChannel.SendMessageAsync($@"`{prettyCurrentTime}` **Channel Topic Changed** `#{after.Name}` ({after.Id})
await logChannel.SendMessageAsync($@"🕘`{prettyCurrentTime}` **| Channel Topic Changed |** #⃣ `{after.Name} ({after.Id})` await logChannel.SendMessageAsync($@"🕘`{prettyCurrentTime}` **| Channel Topic Changed |** #⃣ `{after.Name} ({after.Id})`
`Old:` {((ITextChannel)before).Topic} `Old:` {((ITextChannel)before).Topic}
**`New:`** {((ITextChannel)after).Topic}").ConfigureAwait(false); **`New:`** {((ITextChannel)after).Topic}").ConfigureAwait(false);
}
catch { }
});
return Task.CompletedTask;
}
private Task _client_ChannelDestroyed(IChannel ich)
{
var ch = ich as IGuildChannel;
if (ch == null)
return Task.CompletedTask;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting)
|| (logSetting.ChannelDestroyedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try { await logChannel.SendMessageAsync($"🕕`{prettyCurrentTime}`🗑 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Deleted #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}
private Task _client_ChannelCreated(IChannel ich)
{
var ch = ich as IGuildChannel;
if (ch == null)
return Task.CompletedTask;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting)
|| (logSetting.ChannelCreatedId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try { await logChannel.SendMessageAsync($"🕓`{prettyCurrentTime}`🆕 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Created: #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}
private Task _client_UserVoiceStateUpdated(IUser iusr, IVoiceState before, IVoiceState after)
{
var usr = iusr as IGuildUser;
if (usr == null)
return Task.CompletedTask;
var beforeVch = before.VoiceChannel;
var afterVch = after.VoiceChannel;
if (beforeVch == afterVch)
return Task.CompletedTask;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.LogVoicePresenceId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresence)) == null)
return Task.CompletedTask;
string str = null;
if (beforeVch?.Guild == afterVch?.Guild)
{
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel.";
} }
else if (beforeVch == null) catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_ChannelDestroyed(IChannel ich)
{
try
{ {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel."; var ch = ich as IGuildChannel;
if (ch == null)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting)
|| (logSetting.ChannelDestroyedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == ch.Id))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
return;
await logChannel.SendMessageAsync($"🕕`{prettyCurrentTime}`🗑 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Deleted #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false);
} }
else if (afterVch == null) catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_ChannelCreated(IChannel ich)
{
try
{ {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel."; var ch = ich as IGuildChannel;
if (ch == null)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(ch.Guild.Id, out logSetting)
|| (logSetting.ChannelCreatedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
return;
await logChannel.SendMessageAsync($"🕓`{prettyCurrentTime}`🆕 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Created: #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false);
} }
if (str != null) catch (Exception ex) { _log.Warn(ex); }
UserPresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
return Task.CompletedTask;
} }
private Task _client_UserPresenceUpdated(IGuildUser usr, IPresence before, IPresence after) private async void _client_UserVoiceStateUpdated(IUser iusr, IVoiceState before, IVoiceState after) => await Task.Run(() =>
{ {
LogSetting logSetting; try
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.LogUserPresenceId == null)
|| before.Status == after.Status)
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserPresence)) == null)
return Task.CompletedTask;
string str;
if (before.Status != after.Status)
str = $"🔵`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**.";
else
str = $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game}**.";
UserPresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
return Task.CompletedTask;
}
private Task _client_UserLeft(IGuildUser usr)
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserLeftId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{ {
try { await logChannel.SendMessageAsync($"❗️🕛`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__❌ **| USER LEFT |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } var usr = iusr as IGuildUser;
}); if (usr == null)
return;
return Task.CompletedTask; var beforeVch = before.VoiceChannel;
} var afterVch = after.VoiceChannel;
private Task _client_UserJoined(IGuildUser usr) if (beforeVch == afterVch)
{ return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserJoinedId == null))
return Task.CompletedTask;
ITextChannel logChannel; LogSetting logSetting;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) == null) if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
return Task.CompletedTask; || (logSetting.LogVoicePresenceId == null))
return;
var task = Task.Run(async () => ITextChannel logChannel;
{ if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresence)) == null)
try { await logChannel.SendMessageAsync($"❕🕓`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__✅ **| USER JOINED |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } return;
});
return Task.CompletedTask; string str = null;
} if (beforeVch?.Guild == afterVch?.Guild)
private Task _client_UserUnbanned(IUser usr, IGuild guild)
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting)
|| (logSetting.UserUnbannedId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try { await logChannel.SendMessageAsync($"❕🕘`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__♻ **| USER UN-BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}
private Task _client_UserBanned(IUser usr, IGuild guild)
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting)
|| (logSetting.UserBannedId == null))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try { await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🚫 **| USER BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
}
private Task _client_MessageDeleted(ulong arg1, Optional<IMessage> imsg)
{
var msg = (imsg.IsSpecified ? imsg.Value : null) as IUserMessage;
if (msg == null || msg.IsAuthor())
return Task.CompletedTask;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting)
|| (logSetting.MessageDeletedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) == null || logChannel.Id == msg.Id)
return Task.CompletedTask;
var task = Task.Run(async () =>
{
try
{ {
var str = $@"🕔`{prettyCurrentTime}`👤__**{msg.Author.Username}#{msg.Author.Discriminator}**__ **| Deleted Message |** 🆔 `{msg.Author.Id}` #⃣ `{channel.Name}` str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel.";
🗑 {msg.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator)}";
if (msg.Attachments.Any())
str += $"{Environment.NewLine}📎 {string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))}";
await logChannel.SendMessageAsync(str.SanitizeMentions()).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } else if (beforeVch == null)
}); {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel.";
}
else if (afterVch == null)
{
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel.";
}
if (str != null)
UserPresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
}
catch (Exception ex)
{
_log.Warn(ex);
}
});
return Task.CompletedTask; private async void _client_UserPresenceUpdated(IGuildUser usr, IPresence before, IPresence after) => await Task.Run(() =>
{
try
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.LogUserPresenceId == null)
|| before.Status == after.Status)
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserPresence)) == null)
return;
string str;
if (before.Status != after.Status)
str = $"🔵`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**.";
else
str = $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game}**.";
UserPresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
}
catch { }
});
private async void _client_UserLeft(IGuildUser usr)
{
try
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
|| (logSetting.UserLeftId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft)) == null)
return;
await logChannel.SendMessageAsync($"❗️🕛`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__❌ **| USER LEFT |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}
catch { }
} }
private Task _client_MessageUpdated(Optional<IMessage> optmsg, IMessage imsg2) private async void _client_UserJoined(IGuildUser usr)
{ {
var after = imsg2 as IUserMessage; try
if (after == null || after.IsAuthor())
return Task.CompletedTask;
var before = (optmsg.IsSpecified ? optmsg.Value : null) as IUserMessage;
if (before == null)
return Task.CompletedTask;
var channel = after.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
if (before.Content == after.Content)
return Task.CompletedTask;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting)
|| (logSetting.MessageUpdatedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
return Task.CompletedTask;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) == null || logChannel.Id == after.Channel.Id)
return Task.CompletedTask;
var task = Task.Run(async () =>
{ {
//try { await logChannel.SendMessageAsync($@"🕔`{prettyCurrentTime}` **Message** 📝 `#{channel.Name}` LogSetting logSetting;
//👤`{before.Author.Username}` if (!GuildLogSettings.TryGetValue(usr.Guild.Id, out logSetting)
try { await logChannel.SendMessageAsync($@"🕔`{prettyCurrentTime}`👤__**{before.Author.Username}#{before.Author.Discriminator}**__ **| 📝 Edited Message |** 🆔 `{before.Author.Id}` #⃣ `{channel.Name}` || (logSetting.UserJoinedId == null))
`Old:` {before.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator).SanitizeMentions()} return;
**`New:`** {after.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator).SanitizeMentions()}").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask; ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) == null)
return;
await logChannel.SendMessageAsync($"❕🕓`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__✅ **| USER JOINED |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_UserUnbanned(IUser usr, IGuild guild)
{
try
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting)
|| (logSetting.UserUnbannedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) == null)
return;
await logChannel.SendMessageAsync($"❕🕘`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__♻ **| USER UN-BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_UserBanned(IUser usr, IGuild guild)
{
try
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(guild.Id, out logSetting)
|| (logSetting.UserBannedId == null))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null)
return;
await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🚫 **| USER BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_MessageDeleted(ulong arg1, Optional<IMessage> imsg)
{
try
{
var msg = (imsg.IsSpecified ? imsg.Value : null) as IUserMessage;
if (msg == null || msg.IsAuthor())
return;
var channel = msg.Channel as ITextChannel;
if (channel == null)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting)
|| (logSetting.MessageDeletedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) == null || logChannel.Id == msg.Id)
return;
var str = $@"🕔`{prettyCurrentTime}`👤__**{msg.Author.Username}#{msg.Author.Discriminator}**__ **| Deleted Message |** 🆔 `{msg.Author.Id}` #⃣ `{channel.Name}`
🗑 {msg.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator)}";
if (msg.Attachments.Any())
str += $"{Environment.NewLine}📎 {string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))}";
await logChannel.SendMessageAsync(str.SanitizeMentions()).ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
}
private async void _client_MessageUpdated(Optional<IMessage> optmsg, IMessage imsg2)
{
try
{
var after = imsg2 as IUserMessage;
if (after == null || after.IsAuthor())
return;
var before = (optmsg.IsSpecified ? optmsg.Value : null) as IUserMessage;
if (before == null)
return;
var channel = after.Channel as ITextChannel;
if (channel == null)
return;
if (before.Content == after.Content)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(channel.Guild.Id, out logSetting)
|| (logSetting.MessageUpdatedId == null)
|| logSetting.IgnoredChannels.Any(ilc => ilc.ChannelId == channel.Id))
return;
ITextChannel logChannel;
if ((logChannel = TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) == null || logChannel.Id == after.Channel.Id)
return;
await logChannel.SendMessageAsync($@"🕔`{prettyCurrentTime}`👤__**{before.Author.Username}#{before.Author.Discriminator}**__ **| 📝 Edited Message |** 🆔 `{before.Author.Id}` #⃣ `{channel.Name}`
`Old:` {before.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator).SanitizeMentions()}
**`New:`** {after.Resolve(userHandling: UserMentionHandling.NameAndDiscriminator).SanitizeMentions()}").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
} }
public enum LogType public enum LogType

View File

@ -53,13 +53,13 @@ namespace NadekoBot.Modules.Administration
var toSend = "🔄 " + Repeater.Message; var toSend = "🔄 " + Repeater.Message;
await Task.Delay(Repeater.Interval, token).ConfigureAwait(false); await Task.Delay(Repeater.Interval, token).ConfigureAwait(false);
var lastMsgInChannel = (await Channel.GetMessagesAsync(1)).FirstOrDefault(); //var lastMsgInChannel = (await Channel.GetMessagesAsync(2)).FirstOrDefault();
if (lastMsgInChannel.Id == oldMsg.Id) //don't send if it's the same message in the channel // if (lastMsgInChannel.Id == oldMsg?.Id) //don't send if it's the same message in the channel
continue; // continue;
if (oldMsg != null) if (oldMsg != null)
try { await oldMsg.DeleteAsync(); } catch { } try { await oldMsg.DeleteAsync(); } catch { }
try { oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); try { source.Cancel(); } catch { } } try { oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException) { }

View File

@ -24,8 +24,8 @@ namespace NadekoBot.Modules.Administration
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(); private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
public static event Func<IGuildUser, MuteType, Task> UserMuted = delegate { return Task.CompletedTask; }; public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
public static event Func<IGuildUser, MuteType, Task> UserUnmuted = delegate { return Task.CompletedTask; }; public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
public enum MuteType { public enum MuteType {
@ -51,15 +51,22 @@ namespace NadekoBot.Modules.Administration
NadekoBot.Client.UserJoined += Client_UserJoined; NadekoBot.Client.UserJoined += Client_UserJoined;
} }
private static async Task Client_UserJoined(IGuildUser usr) private static async void Client_UserJoined(IGuildUser usr)
{ {
ConcurrentHashSet<ulong> muted; try
MutedUsers.TryGetValue(usr.Guild.Id, out muted); {
ConcurrentHashSet<ulong> muted;
MutedUsers.TryGetValue(usr.Guild.Id, out muted);
if (muted == null || !muted.Contains(usr.Id)) if (muted == null || !muted.Contains(usr.Id))
return; return;
else else
await Mute(usr).ConfigureAwait(false); await Mute(usr).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
} }
@ -80,7 +87,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await UserMuted(usr, MuteType.All).ConfigureAwait(false); UserMuted(usr, MuteType.All);
} }
public static async Task Unmute(IGuildUser usr) public static async Task Unmute(IGuildUser usr)
@ -99,7 +106,7 @@ namespace NadekoBot.Modules.Administration
muted.TryRemove(usr.Id); muted.TryRemove(usr.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await UserUnmuted(usr, MuteType.All).ConfigureAwait(false); UserUnmuted(usr, MuteType.All);
} }
public static async Task<IRole> GetMuteRole(IGuild guild) public static async Task<IRole> GetMuteRole(IGuild guild)
@ -211,7 +218,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false); await user.AddRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await UserMuted(user, MuteType.Chat).ConfigureAwait(false); UserMuted(user, MuteType.Chat);
await channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false); await channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false);
} }
catch catch
@ -230,7 +237,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false); await user.RemoveRolesAsync(await GetMuteRole(channel.Guild).ConfigureAwait(false)).ConfigureAwait(false);
await UserUnmuted(user, MuteType.Chat).ConfigureAwait(false); UserUnmuted(user, MuteType.Chat);
await channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false); await channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false);
} }
catch catch
@ -249,7 +256,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false); await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
await UserMuted(user, MuteType.Voice).ConfigureAwait(false); UserMuted(user, MuteType.Voice);
await channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false); await channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false);
} }
catch catch
@ -267,7 +274,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false); await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
await UserUnmuted(user, MuteType.Voice).ConfigureAwait(false); UserUnmuted(user, MuteType.Voice);
await channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false); await channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false);
} }
catch catch

View File

@ -45,7 +45,8 @@ namespace NadekoBot.Modules.Administration
else else
{ {
usr.MessageCount++; usr.MessageCount++;
var t = Task.Run(async () => { var t = Task.Run(async () =>
{
try try
{ {
await Task.Delay(PerSeconds * 1000, cancelSource.Token); await Task.Delay(PerSeconds * 1000, cancelSource.Token);
@ -61,26 +62,26 @@ namespace NadekoBot.Modules.Administration
static RatelimitCommand() static RatelimitCommand()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.Client.MessageReceived += (umsg) => NadekoBot.Client.MessageReceived += async (umsg) =>
{ {
var t = Task.Run(async () => try
{ {
var usrMsg = umsg as IUserMessage; var usrMsg = umsg as IUserMessage;
var channel = usrMsg.Channel as ITextChannel; var channel = usrMsg.Channel as ITextChannel;
if (channel == null || usrMsg.IsAuthor()) if (channel == null || usrMsg.IsAuthor())
return; return;
Ratelimiter limiter; Ratelimiter limiter;
if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter)) if (!RatelimitingChannels.TryGetValue(channel.Id, out limiter))
return; return;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id)) if (limiter.CheckUserRatelimit(usrMsg.Author.Id))
try { await usrMsg.DeleteAsync(); } catch (Exception ex) { _log.Warn(ex); } await usrMsg.DeleteAsync();
}); }
return Task.CompletedTask; catch (Exception ex) { _log.Warn(ex); }
}; };
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -118,7 +119,7 @@ namespace NadekoBot.Modules.Administration
MaxMessages = msg, MaxMessages = msg,
PerSeconds = perSec, PerSeconds = perSec,
}; };
if(RatelimitingChannels.TryAdd(channel.Id, toAdd)) if (RatelimitingChannels.TryAdd(channel.Id, toAdd))
{ {
await channel.SendConfirmAsync("Slow mode initiated", await channel.SendConfirmAsync("Slow mode initiated",
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.") $"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.")
@ -127,4 +128,4 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }
} }

View File

@ -25,93 +25,85 @@ namespace NadekoBot.Modules.Administration
NadekoBot.Client.UserLeft += UserLeft; NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
//todo optimize ASAP
private static Task UserLeft(IGuildUser user) private static async void UserLeft(IGuildUser user)
{ {
var leftTask = Task.Run(async () => try
{ {
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel == null) //maybe warn the server owner that the channel is missing
return;
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (string.IsNullOrWhiteSpace(msg))
return;
try try
{ {
GuildConfig conf; var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
using (var uow = DbHandler.UnitOfWork()) if (conf.AutoDeleteByeMessagesTimer > 0)
{ {
conf = uow.GuildConfigs.For(user.Guild.Id, set => set); toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
} }
if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
if (channel == null) //maybe warn the server owner that the channel is missing
return;
var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (string.IsNullOrWhiteSpace(msg))
return;
try
{
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteByeMessagesTimer > 0)
{
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
}
}
catch (Exception ex) { _log.Warn(ex); }
} }
catch { } catch (Exception ex) { _log.Warn(ex); }
}); }
return Task.CompletedTask; catch { }
} }
private static Task UserJoined(IGuildUser user) private static async void UserJoined(IGuildUser user)
{ {
var joinedTask = Task.Run(async () => try
{ {
try GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{ {
GuildConfig conf; conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
using (var uow = DbHandler.UnitOfWork()) }
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (conf.SendChannelGreetMessage) if (conf.SendChannelGreetMessage)
{
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId);
if (channel != null) //maybe warn the server owner that the channel is missing
{ {
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId); var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name);
if (channel != null) //maybe warn the server owner that the channel is missing if (!string.IsNullOrWhiteSpace(msg))
{ {
var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); try
if (!string.IsNullOrWhiteSpace(msg))
{ {
try var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false);
if (conf.AutoDeleteGreetMessagesTimer > 0)
{ {
var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
if (conf.AutoDeleteGreetMessagesTimer > 0)
{
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
}
} }
catch (Exception ex) { _log.Warn(ex); }
}
}
}
if (conf.SendDmGreetMessage)
{
var channel = await user.CreateDMChannelAsync();
if (channel != null)
{
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
{
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); }
} }
} }
} }
catch { }
}); if (conf.SendDmGreetMessage)
return Task.CompletedTask; {
var channel = await user.CreateDMChannelAsync();
if (channel != null)
{
var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name);
if (!string.IsNullOrWhiteSpace(msg))
{
await channel.SendConfirmAsync(msg).ConfigureAwait(false);
}
}
}
}
catch { }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -135,7 +127,7 @@ namespace NadekoBot.Modules.Administration
{ {
if (timer < 0 || timer > 600) if (timer < 0 || timer > 600)
return; return;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(id, set => set); var conf = uow.GuildConfigs.For(id, set => set);
@ -173,7 +165,7 @@ namespace NadekoBot.Modules.Administration
} }
return enabled; return enabled;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageGuild)] [RequirePermission(GuildPermission.ManageGuild)]
@ -343,7 +335,7 @@ namespace NadekoBot.Modules.Administration
if (!sendByeEnabled) if (!sendByeEnabled)
await channel.SendConfirmAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false); await channel.SendConfirmAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false);
} }
public static bool SetByeMessage(ulong guildId, ref string message) public static bool SetByeMessage(ulong guildId, ref string message)
{ {
message = message?.SanitizeMentions(); message = message?.SanitizeMentions();
@ -394,4 +386,4 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }

View File

@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration
public class VoicePlusTextCommands public class VoicePlusTextCommands
{ {
private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; } private static ConcurrentHashSet<ulong> voicePlusTextCache { get; }
static VoicePlusTextCommands() static VoicePlusTextCommands()
{ {
@ -29,76 +29,73 @@ namespace NadekoBot.Modules.Administration
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
} }
private static Task UserUpdatedEventHandler(IUser iuser, IVoiceState before, IVoiceState after) private static async void UserUpdatedEventHandler(IUser iuser, IVoiceState before, IVoiceState after)
{ {
var user = (iuser as IGuildUser); var user = (iuser as IGuildUser);
var guild = user?.Guild; var guild = user?.Guild;
if (guild == null) if (guild == null)
return Task.CompletedTask; return;
var task = Task.Run(async () =>
try
{ {
try var botUserPerms = guild.GetCurrentUser().GuildPermissions;
if (before.VoiceChannel == after.VoiceChannel) return;
if (!voicePlusTextCache.Contains(guild.Id))
return;
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
{ {
var botUserPerms = guild.GetCurrentUser().GuildPermissions; try
if (before.VoiceChannel == after.VoiceChannel) return;
if (!voicePlusTextCache.Contains(guild.Id))
return;
if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles)
{ {
try await (await guild.GetOwnerAsync()).SendErrorAsync(
{ "⚠️ I don't have **manage server** and/or **manage channels** permission," +
await (await guild.GetOwnerAsync()).SendErrorAsync( $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
}
catch { }
using (var uow = DbHandler.UnitOfWork())
{
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false);
}
return;
} }
catch { }
using (var uow = DbHandler.UnitOfWork())
var beforeVch = before.VoiceChannel;
if (beforeVch != null)
{ {
var textChannel = guild.GetTextChannels().Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault(); uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
if (textChannel != null) voicePlusTextCache.TryRemove(guild.Id);
await textChannel.AddPermissionOverwriteAsync(user, await uow.CompleteAsync().ConfigureAwait(false);
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
} }
var afterVch = after.VoiceChannel; return;
if (afterVch != null && guild.AFKChannelId != afterVch.Id) }
{
var textChannel = guild.GetTextChannels()
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) var beforeVch = before.VoiceChannel;
.FirstOrDefault(); if (beforeVch != null)
if (textChannel == null) {
{ var textChannel = guild.GetTextChannels().Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); if (textChannel != null)
await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
await textChannel.AddPermissionOverwriteAsync(user, await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Allow, new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Allow)).ConfigureAwait(false); sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
} }
catch (Exception ex) var afterVch = after.VoiceChannel;
if (afterVch != null && guild.AFKChannelId != afterVch.Id)
{ {
Console.WriteLine(ex); var textChannel = guild.GetTextChannels()
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
.FirstOrDefault();
if (textChannel == null)
{
textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Allow,
sendMessages: PermValue.Allow)).ConfigureAwait(false);
} }
}); }
return Task.CompletedTask; catch (Exception ex)
{
Console.WriteLine(ex);
}
} }
private static string GetChannelName(string voiceName) => private static string GetChannelName(string voiceName) =>
@ -187,4 +184,4 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }
} }

View File

@ -172,7 +172,7 @@ namespace NadekoBot.Modules.CustomReactions
{ {
var txtStream = await customReactions.GroupBy(cr => cr.Trigger) var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key) .OrderBy(cr => cr.Key)
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => y.Response).ToList() }) .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
.ToJson() .ToJson()
.ToStream() .ToStream()
.ConfigureAwait(false); .ConfigureAwait(false);
@ -293,7 +293,7 @@ namespace NadekoBot.Modules.CustomReactions
{ {
await imsg.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false); await imsg.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
} }
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -302,7 +302,7 @@ namespace NadekoBot.Modules.CustomReactions
if (page < 1) if (page < 1)
return; return;
await imsg.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value) await imsg.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
.Skip((page - 1)*9) .Skip((page - 1) * 9)
.Take(9) .Take(9)
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"), .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"),
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))) (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
@ -310,4 +310,4 @@ namespace NadekoBot.Modules.CustomReactions
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }
} }

View File

@ -199,7 +199,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var wonAmount = winner.AmountBet * (participants.Count - 1); var wonAmount = winner.AmountBet * (participants.Count - 1);
await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false);
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false);
} }
else else
@ -209,15 +209,15 @@ namespace NadekoBot.Modules.Gambling
} }
private Task Client_MessageReceived(IMessage imsg) private void Client_MessageReceived(IMessage imsg)
{ {
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null) if (msg == null)
return Task.CompletedTask; return;
if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel) if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel)
return Task.CompletedTask; return;
messagesSinceGameStarted++; messagesSinceGameStarted++;
return Task.CompletedTask; return;
} }
private async Task CheckForFullGameAsync(CancellationToken cancelToken) private async Task CheckForFullGameAsync(CancellationToken cancelToken)
@ -248,7 +248,7 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
if (amount > 0) if (amount > 0)
if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, true).ConfigureAwait(false)) if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, false).ConfigureAwait(false))
{ {
try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { } try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {Gambling.CurrencyName}s.").ConfigureAwait(false); } catch { }
return; return;
@ -277,10 +277,7 @@ namespace NadekoBot.Modules.Gambling
this.AmountBet = amount; this.AmountBet = amount;
} }
public override int GetHashCode() public override int GetHashCode() => User.GetHashCode();
{
return User.GetHashCode();
}
public override bool Equals(object obj) public override bool Equals(object obj)
{ {

View File

@ -3,6 +3,7 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -73,9 +74,12 @@ namespace NadekoBot.Modules.Games
//text, votes //text, votes
private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>(); private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>();
private readonly Logger _log;
public AcrophobiaGame(ITextChannel channel, int time) public AcrophobiaGame(ITextChannel channel, int time)
{ {
this._log = LogManager.GetCurrentClassLogger();
this.channel = channel; this.channel = channel;
this.time = time; this.time = time;
this.source = new CancellationTokenSource(); this.source = new CancellationTokenSource();
@ -163,101 +167,100 @@ namespace NadekoBot.Modules.Games
await End().ConfigureAwait(false); await End().ConfigureAwait(false);
} }
private Task PotentialAcro(IMessage arg) private async void PotentialAcro(IMessage arg)
{ {
var t = Task.Run(async () => try
{ {
try var msg = arg as IUserMessage;
if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id)
return;
++spamCount;
var guildUser = (IGuildUser)msg.Author;
var input = msg.Content.ToUpperInvariant().Trim();
if (phase == AcroPhase.Submitting)
{ {
var msg = arg as IUserMessage; if (spamCount > 10)
if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id) {
spamCount = 0;
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
catch { }
}
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return;
var inputWords = input.Split(' '); //get all words
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
return; return;
++spamCount; for (int i = 0; i < startingLetters.Length; i++)
var guildUser = (IGuildUser)msg.Author;
var input = msg.Content.ToUpperInvariant().Trim();
if (phase == AcroPhase.Submitting)
{ {
if (spamCount > 10) var letter = startingLetters[i];
{
spamCount = 0; if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
catch { }
}
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return; return;
var inputWords = input.Split(' '); //get all words
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
return;
for (int i = 0; i < startingLetters.Length; i++)
{
var letter = startingLetters[i];
if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
return;
}
//try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser))
return;
// all good. valid input. answer recorded
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");
try
{
await msg.DeleteAsync();
}
catch
{
await msg.DeleteAsync(); //try twice
}
} }
else if (phase == AcroPhase.Voting)
//try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser))
return;
// all good. valid input. answer recorded
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");
try
{ {
if (spamCount > 10) await msg.DeleteAsync();
{ }
spamCount = 0; catch
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); } {
catch { } await msg.DeleteAsync(); //try twice
}
IGuildUser usr;
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
//{
// if (!usersWhoVoted.Add(guildUser.Id))
// return;
// votes.AddOrUpdate(input, 1, (key, old) => ++old);
// await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
// await msg.DeleteAsync().ConfigureAwait(false);
// return;
//}
int num;
if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count)
{
var kvp = submissions.Skip(num - 1).First();
usr = kvp.Value;
//can't vote for yourself, can't vote multiple times
if (usr.Id == guildUser.Id || !usersWhoVoted.Add(guildUser.Id))
return;
votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
await msg.DeleteAsync().ConfigureAwait(false);
return;
}
} }
} }
catch { } else if (phase == AcroPhase.Voting)
}); {
return Task.CompletedTask; if (spamCount > 10)
{
spamCount = 0;
try { await channel.EmbedAsync(GetEmbed().Build()).ConfigureAwait(false); }
catch { }
}
IGuildUser usr;
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
//{
// if (!usersWhoVoted.Add(guildUser.Id))
// return;
// votes.AddOrUpdate(input, 1, (key, old) => ++old);
// await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
// await msg.DeleteAsync().ConfigureAwait(false);
// return;
//}
int num;
if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count)
{
var kvp = submissions.Skip(num - 1).First();
usr = kvp.Value;
//can't vote for yourself, can't vote multiple times
if (usr.Id == guildUser.Id || !usersWhoVoted.Add(guildUser.Id))
return;
votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false);
await msg.DeleteAsync().ConfigureAwait(false);
return;
}
}
}
catch (Exception ex)
{
_log.Warn(ex);
}
} }
public async Task End() public async Task End()
@ -279,7 +282,7 @@ namespace NadekoBot.Modules.Games
public void EnsureStopped() public void EnsureStopped()
{ {
NadekoBot.Client.MessageReceived -= PotentialAcro; NadekoBot.Client.MessageReceived -= PotentialAcro;
if (!source.IsCancellationRequested) if (!source.IsCancellationRequested)
source.Cancel(); source.Cancel();
} }

View File

@ -2,6 +2,7 @@
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -43,7 +44,8 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
.Concat(data.Things) .Concat(data.Things)
.ToList(); .ToList();
} }
catch (Exception ex) { catch (Exception ex)
{
Console.WriteLine(ex); Console.WriteLine(ex);
} }
} }
@ -64,12 +66,14 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
default: default:
return data.All[rng.Next(0, data.All.Count)]; return data.All[rng.Next(0, data.All.Count)];
} }
} }
} }
public class HangmanGame public class HangmanGame
{ {
private readonly Logger _log;
public IMessageChannel GameChannel { get; } public IMessageChannel GameChannel { get; }
public HashSet<char> Guesses { get; } = new HashSet<char>(); public HashSet<char> Guesses { get; } = new HashSet<char>();
public HangmanObject Term { get; private set; } public HangmanObject Term { get; private set; }
@ -97,6 +101,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type) public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
{ {
_log = LogManager.GetCurrentClassLogger();
this.GameChannel = channel; this.GameChannel = channel;
this.TermType = type; this.TermType = type;
} }
@ -124,17 +129,20 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
await GameChannel.EmbedAsync(embed.WithOkColor().Build()).ConfigureAwait(false); await GameChannel.EmbedAsync(embed.WithOkColor().Build()).ConfigureAwait(false);
} }
private Task PotentialGuess(IMessage msg) private async void PotentialGuess(IMessage msg)
{ {
if (msg.Channel != GameChannel) try
return Task.CompletedTask; // message's channel has to be the same as game's
if (msg.Content.Length != 1) // message must be 1 char long
{ {
if (++MessagesSinceLastPost > 10) if (!(msg is IUserMessage))
return;
if (msg.Channel != GameChannel)
return; // message's channel has to be the same as game's
if (msg.Content.Length != 1) // message must be 1 char long
{ {
MessagesSinceLastPost = 0; if (++MessagesSinceLastPost > 10)
Task.Run(async () =>
{ {
MessagesSinceLastPost = 0;
try try
{ {
await GameChannel.SendConfirmAsync("Hangman Game", await GameChannel.SendConfirmAsync("Hangman Game",
@ -142,20 +150,12 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
footer: string.Join(" ", Guesses)).ConfigureAwait(false); footer: string.Join(" ", Guesses)).ConfigureAwait(false);
} }
catch { } catch { }
}); }
}
return Task.CompletedTask;
}
if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit
return Task.CompletedTask; return;
var guess = char.ToUpperInvariant(msg.Content[0]); var guess = char.ToUpperInvariant(msg.Content[0]);
Task.Run(async () =>
{
try
{
if (Guesses.Contains(guess)) if (Guesses.Contains(guess))
{ {
MessagesSinceLastPost = 0; MessagesSinceLastPost = 0;
@ -176,12 +176,16 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
{ {
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { } try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
await End().ConfigureAwait(false); await End().ConfigureAwait(false);
return; return;
} }
MessagesSinceLastPost = 0; MessagesSinceLastPost = 0;
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(), try
footer: string.Join(" ", Guesses)).ConfigureAwait(false); } catch { } {
await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(),
footer: string.Join(" ", Guesses)).ConfigureAwait(false);
}
catch { }
} }
else else
@ -196,22 +200,16 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
} }
} }
catch { } }
catch (Exception ex) { _log.Warn(ex); }
});
return Task.CompletedTask;
} }
public string GetHangman() public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_
{
return
$@"\_\_\_\_\_\_\_\_\_
| | | |
| | | |
{(Errors > 0 ? "😲" : " ")} | {(Errors > 0 ? "😲" : " ")} |
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} | {(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} |
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} | {(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} |
/-\"; /-\";
}
} }
} }

View File

@ -51,27 +51,27 @@ namespace NadekoBot.Modules.Games
var conf = uow.BotConfig.GetOrCreate(); var conf = uow.BotConfig.GetOrCreate();
var x = var x =
generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs
.SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj=>obj.ChannelId))); .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId)));
chance = conf.CurrencyGenerationChance; chance = conf.CurrencyGenerationChance;
cooldown = conf.CurrencyGenerationCooldown; cooldown = conf.CurrencyGenerationCooldown;
} }
} }
private static Task PotentialFlowerGeneration(IMessage imsg) private static async void PotentialFlowerGeneration(IMessage imsg)
{ {
var msg = imsg as IUserMessage; try
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return Task.CompletedTask;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
if (!generationChannels.Contains(channel.Id))
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
var msg = imsg as IUserMessage;
if (msg == null || msg.IsAuthor() || msg.Author.IsBot)
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return;
if (!generationChannels.Contains(channel.Id))
return;
var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
var rng = new NadekoRandom(); var rng = new NadekoRandom();
@ -83,20 +83,19 @@ namespace NadekoBot.Modules.Games
if (num > 100) if (num > 100)
{ {
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
try
{ var sent = await channel.SendFileAsync(
var sent = await channel.SendFileAsync( GetRandomCurrencyImagePath(),
GetRandomCurrencyImagePath(), $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") .ConfigureAwait(false);
.ConfigureAwait(false); plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
}
catch { }
} }
}); }
return Task.CompletedTask; catch { }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Pick(IUserMessage imsg) public async Task Pick(IUserMessage imsg)
@ -144,7 +143,7 @@ namespace NadekoBot.Modules.Games
var file = GetRandomCurrencyImagePath(); var file = GetRandomCurrencyImagePath();
IUserMessage msg; IUserMessage msg;
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.Gambling.CurrencyName[0]); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(Gambling.Gambling.CurrencyName[0]);
var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; var msgToSend = $"Oh how Nice! **{imsg.Author.Username}** planted {(vowelFirst ? "an" : "a")} {Gambling.Gambling.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
if (file == null) if (file == null)
{ {
@ -156,7 +155,7 @@ namespace NadekoBot.Modules.Games
} }
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; }); plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; });
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.ManageMessages)] [RequirePermission(GuildPermission.ManageMessages)]

View File

@ -124,61 +124,57 @@ namespace NadekoBot.Modules.Games
} }
} }
private Task Vote(IMessage imsg) private async void Vote(IMessage imsg)
{ {
// has to be a user message try
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return Task.CompletedTask;
if (vote < 1 || vote > answers.Length)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
try // has to be a user message
var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot)
return;
// has to be an integer
int vote;
if (!int.TryParse(imsg.Content, out vote))
return;
if (vote < 1 || vote > answers.Length)
return;
IMessageChannel ch;
if (isPublic)
{ {
IMessageChannel ch; //if public, channel must be the same the poll started in
if (isPublic) if (originalMessage.Channel.Id != imsg.Channel.Id)
return;
ch = imsg.Channel;
}
else
{
//if private, channel must be dm channel
if ((ch = msg.Channel as IDMChannel) == null)
return;
// user must be a member of the guild this poll is in
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
return;
}
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
{ {
//if public, channel must be the same the poll started in await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
if (originalMessage.Channel.Id != imsg.Channel.Id)
return;
ch = imsg.Channel;
} }
else else
{ {
//if private, channel must be dm channel var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
if ((ch = msg.Channel as IDMChannel) == null) toDelete.DeleteAfter(5);
return;
// user must be a member of the guild this poll is in
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
return;
}
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
{
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
}
else
{
var toDelete = await ch.SendConfirmAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
} }
} }
catch { } }
}); catch { }
return Task.CompletedTask;
} }
} }
} }

View File

@ -105,42 +105,39 @@ namespace NadekoBot.Modules.Games
NadekoBot.Client.MessageReceived += AnswerReceived; NadekoBot.Client.MessageReceived += AnswerReceived;
} }
private Task AnswerReceived(IMessage imsg) private async void AnswerReceived(IMessage imsg)
{ {
if (imsg.Author.IsBot) try
return Task.CompletedTask;
var msg = imsg as IUserMessage;
if (msg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
try if (imsg.Author.IsBot)
return;
var msg = imsg as IUserMessage;
if (msg == null)
return;
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return;
var guess = msg.Content;
var distance = CurrentSentence.LevenshteinDistance(guess);
var decision = Judge(distance, guess.Length);
if (decision && !finishedUserIds.Contains(msg.Author.Id))
{ {
if (this.Channel == null || this.Channel.Id != this.Channel.Id) return; var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60;
finishedUserIds.Add(msg.Author.Id);
var guess = msg.Content; await Extensions.Extensions.EmbedAsync(this.Channel, (Discord.API.Embed)new EmbedBuilder().WithColor((uint)NadekoBot.OkColor)
.WithTitle((string)$"{msg.Author} finished the race!")
var distance = CurrentSentence.LevenshteinDistance(guess); .AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
var decision = Judge(distance, guess.Length); .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true))
if (decision && !finishedUserIds.Contains(msg.Author.Id)) .AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true))
.Build()).ConfigureAwait(false);
if (finishedUserIds.Count % 4 == 0)
{ {
var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60; await Extensions.Extensions.SendConfirmAsync(this.Channel, (string)$":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize((string)CurrentSentence.Replace((string)" ", (string)" \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
finishedUserIds.Add(msg.Author.Id);
await Extensions.Extensions.EmbedAsync(this.Channel, (Discord.API.Embed)new EmbedBuilder().WithColor((uint)NadekoBot.OkColor)
.WithTitle((string)$"{msg.Author} finished the race!")
.AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true))
.AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true))
.AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true))
.Build()).ConfigureAwait(false);
if (finishedUserIds.Count % 4 == 0)
{
await Extensions.Extensions.SendConfirmAsync(this.Channel, (string)$":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize((string)CurrentSentence.Replace((string)" ", (string)" \x200B")).SanitizeMentions()}**").ConfigureAwait(false);
}
} }
} }
catch { } }
}); catch (Exception ex) { _log.Warn(ex); }
return Task.CompletedTask;
} }
private bool Judge(int errors, int textLength) => errors <= textLength / 25; private bool Judge(int errors, int textLength) => errors <= textLength / 25;

View File

@ -142,49 +142,48 @@ namespace NadekoBot.Modules.Games.Trivia
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
private Task PotentialGuess(IMessage imsg) private async void PotentialGuess(IMessage imsg)
{ {
if (imsg.Author.IsBot) try
return Task.CompletedTask;
var umsg = imsg as IUserMessage;
if (umsg == null)
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
if (imsg.Author.IsBot)
return;
var umsg = imsg as IUserMessage;
if (umsg == null)
return;
var textChannel = umsg.Channel as ITextChannel;
if (textChannel == null || textChannel.Guild != guild)
return;
var guildUser = (IGuildUser)umsg.Author;
var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false);
try try
{ {
var textChannel = umsg.Channel as ITextChannel; if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested)
if (textChannel == null || textChannel.Guild != guild)
return;
var guildUser = (IGuildUser)umsg.Author;
var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false);
try
{ {
if (GameActive && CurrentQuestion.IsAnswerCorrect(umsg.Content) && !triviaCancelSource.IsCancellationRequested) Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
{ guess = true;
Users.AddOrUpdate(guildUser, 1, (gu, old) => ++old);
guess = true;
}
} }
finally { _guessLock.Release(); }
if (!guess) return;
triviaCancelSource.Cancel();
if (Users[guildUser] == WinRequirement) {
ShouldStopGame = true;
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
return;
}
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } finally { _guessLock.Release(); }
}); if (!guess) return;
return Task.CompletedTask; triviaCancelSource.Cancel();
if (Users[guildUser] == WinRequirement)
{
ShouldStopGame = true;
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
return;
}
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
}
catch (Exception ex) { _log.Warn(ex); }
} }
public string GetLeaderboard() public string GetLeaderboard()

View File

@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Games.Trivia
private static string Scramble(string word) private static string Scramble(string word)
{ {
var letters = word.ToArray(); var letters = word.ToCharArray();
var count = 0; var count = 0;
for (var i = 0; i < letters.Length; i++) for (var i = 0; i < letters.Length; i++)
{ {
@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
if (letters[i] != ' ') if (letters[i] != ' ')
letters[i] = '_'; letters[i] = '_';
} }
return string.Join(" ", letters); return string.Join(" \x200B", new string(letters).Replace(" ", " \x200B").AsEnumerable());
} }
} }
} }

View File

@ -34,7 +34,16 @@ namespace NadekoBot.Modules.Music.Classes
/// Player will prioritize different queuer name /// Player will prioritize different queuer name
/// over the song position in the playlist /// over the song position in the playlist
/// </summary> /// </summary>
public bool FairPlay { get; set; } = true; public bool FairPlay { get; set; } = false;
/// <summary>
/// Song will stop playing after this amount of time.
/// To prevent people queueing radio or looped songs
/// while other people want to listen to other songs too.
/// </summary>
public uint MaxPlaytimeSeconds { get; set; } = 0;
public TimeSpan TotalPlaytime => new TimeSpan(playlist.Sum(s => s.TotalTime.Ticks));
/// <summary> /// <summary>
/// Users who recently got their music wish /// Users who recently got their music wish
@ -64,7 +73,9 @@ namespace NadekoBot.Modules.Music.Classes
public bool Autoplay { get; set; } = false; public bool Autoplay { get; set; } = false;
public uint MaxQueueSize { get; set; } = 0; public uint MaxQueueSize { get; set; } = 0;
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>(); private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>();
public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%";
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume) public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
{ {

View File

@ -47,29 +47,11 @@ namespace NadekoBot.Modules.Music.Classes
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;
//public string PrettyName => $"**[{SongInfo.Title.TrimTo(70)}]({SongInfo.Query})**"; public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({songUrl})**";
public string PrettyName {
get {
switch (SongInfo.ProviderType)
{
case MusicType.Normal:
return $"**[{SongInfo.Title.TrimTo(70)}]({SongInfo.Query})**";
case MusicType.Soundcloud:
return $"**[{SongInfo.Title.TrimTo(70)}]({SongInfo.Query})**";
case MusicType.Local:
return $"**{SongInfo.Title.TrimTo(70)}**";
case MusicType.Radio:
return $"**{SongInfo.Title.TrimTo(70)}**";
default:
return "";
}
}
}
public string PrettyInfo => $"{PrettyTotalTime} | {PrettyProvider} | {QueuerName}"; public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
public string PrettyFullName => $"{PrettyName}\n\t\t*{PrettyInfo}*"; public string PrettyFullName => $"{PrettyName}\n\t\t`{PrettyTotalTime} | {PrettyProvider} | {QueuerName}`";
public string PrettyCurrentTime => CurrentTime.ToString(@"mm\:ss"); public string PrettyCurrentTime => CurrentTime.ToString(@"mm\:ss");
@ -86,36 +68,36 @@ namespace NadekoBot.Modules.Music.Classes
public string Thumbnail { public string Thumbnail {
get { get {
switch (SongInfo.Provider) switch (SongInfo.ProviderType)
{ {
case "YouTube": case MusicType.Radio:
//todo have videoid in songinfo from the start //todo have videoid in songinfo from the start
var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+"); var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+");
return $"https://img.youtube.com/vi/{ videoId }/0.jpg"; return $"https://img.youtube.com/vi/{ videoId }/0.jpg";
case "SoundCloud": case MusicType.Normal:
return SongInfo.AlbumArt; return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
case "Local File": case MusicType.Local:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
case "Radio Stream": case MusicType.Soundcloud:
return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links return SongInfo.AlbumArt;
default: default:
return ""; return "";
} }
} }
} }
public string songURL { private string songUrl {
get { get {
switch (SongInfo.ProviderType) switch (SongInfo.ProviderType)
{ {
case MusicType.Normal: case MusicType.Normal:
return SongInfo.Query; return SongInfo.Query;
case MusicType.Soundcloud: case MusicType.Soundcloud:
return SongInfo.Query; return SongInfo.Query;
case MusicType.Local: case MusicType.Local:
return $"https://google.com/search?q={ WebUtility.UrlEncode(SongInfo.Title).Replace(' ', '+') }"; return $"https://google.com/search?q={ WebUtility.UrlEncode(SongInfo.Title).Replace(' ', '+') }";
case MusicType.Radio: case MusicType.Radio:
return $"https://google.com/search?q={SongInfo.Title}"; return $"https://google.com/search?q={SongInfo.Title}";
default: default:
return ""; return "";
} }
@ -143,7 +125,7 @@ namespace NadekoBot.Modules.Music.Classes
{ {
var s = new Song(SongInfo); var s = new Song(SongInfo);
s.MusicPlayer = MusicPlayer; s.MusicPlayer = MusicPlayer;
s.QueuerName = QueuerName; s.QueuerName = QueuerName;
return s; return s;
} }
@ -203,7 +185,8 @@ namespace NadekoBot.Modules.Music.Classes
int nextTime = Environment.TickCount + milliseconds; int nextTime = Environment.TickCount + milliseconds;
byte[] buffer = new byte[frameBytes]; byte[] buffer = new byte[frameBytes];
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason
!(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime
{ {
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);

View File

@ -36,17 +36,15 @@ namespace NadekoBot.Modules.Music
Directory.CreateDirectory(MusicDataPath); Directory.CreateDirectory(MusicDataPath);
} }
private Task Client_UserVoiceStateUpdated(IUser iusr, IVoiceState oldState, IVoiceState newState) private void Client_UserVoiceStateUpdated(IUser iusr, IVoiceState oldState, IVoiceState newState)
{ {
var usr = iusr as IGuildUser; var usr = iusr as IGuildUser;
if (usr == null || if (usr == null ||
oldState.VoiceChannel == newState.VoiceChannel) oldState.VoiceChannel == newState.VoiceChannel)
return Task.CompletedTask; return;
MusicPlayer player; MusicPlayer player;
if (!MusicPlayers.TryGetValue(usr.Guild.Id, out player)) if (!MusicPlayers.TryGetValue(usr.Guild.Id, out player))
return Task.CompletedTask; return;
if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause if ((player.PlaybackVoiceChannel == newState.VoiceChannel && //if joined first, and player paused, unpause
player.Paused && player.Paused &&
player.PlaybackVoiceChannel.GetUsers().Count == 2) || // keep in mind bot is in the channel (+1) player.PlaybackVoiceChannel.GetUsers().Count == 2) || // keep in mind bot is in the channel (+1)
@ -56,7 +54,7 @@ namespace NadekoBot.Modules.Music
{ {
player.TogglePause(); player.TogglePause();
} }
return Task.CompletedTask; return;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -193,6 +191,8 @@ namespace NadekoBot.Modules.Music
int startAt = itemsPerPage * (page - 1); int startAt = itemsPerPage * (page - 1);
var number = 0 + startAt; var number = 0 + startAt;
var total = musicPlayer.TotalPlaytime;
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {page}") .WithAuthor(eab => eab.WithName($"Player Queue - Page {page}")
.WithMusicIcon()) .WithMusicIcon())
@ -200,7 +200,9 @@ namespace NadekoBot.Modules.Music
.Skip(startAt) .Skip(startAt)
.Take(10) .Take(10)
.Select(v => $"`{++number}.` {v.PrettyFullName}"))) .Select(v => $"`{++number}.` {v.PrettyFullName}")))
.WithFooter(ef => ef.WithText($"{musicPlayer.Playlist.Count} tracks currently queued.")) .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " +
(musicPlayer.FairPlay? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
.WithOkColor(); .WithOkColor();
if (musicPlayer.RepeatSong) if (musicPlayer.RepeatSong)
@ -232,15 +234,12 @@ namespace NadekoBot.Modules.Music
return; return;
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var embed = new EmbedBuilder() var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Now Playing") .WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
.WithMusicIcon()) .WithDescription(currentSong.PrettyName)
.WithTitle(currentSong.SongInfo.Title) .WithThumbnail(tn => tn.Url = currentSong.Thumbnail)
.WithDescription(currentSong.PrettyFullTime) .WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
.WithFooter(ef => ef.WithText($"{currentSong.PrettyProvider} | {currentSong.QueuerName}"))
.WithOkColor()
.WithThumbnail(tn => tn.Url = currentSong.Thumbnail)
.WithUrl(currentSong.songURL);
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false); await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
@ -327,28 +326,28 @@ namespace NadekoBot.Modules.Music
var count = ids.Count(); var count = ids.Count();
var msg = await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false); var msg = await channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
var cancelSource = new CancellationTokenSource(); var cancelSource = new CancellationTokenSource();
var gusr = (IGuildUser)umsg.Author; var gusr = (IGuildUser)umsg.Author;
while (ids.Any() && !cancelSource.IsCancellationRequested) while (ids.Any() && !cancelSource.IsCancellationRequested)
{ {
var tasks = Task.WhenAll(ids.Take(5).Select(async id => var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
{ {
if (cancelSource.Token.IsCancellationRequested) if (cancelSource.Token.IsCancellationRequested)
return; return;
try try
{ {
await QueueSong(gusr, channel, gusr.VoiceChannel, id, true).ConfigureAwait(false); await QueueSong(gusr, channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
} }
catch (SongNotFoundException) { } catch (SongNotFoundException) { }
catch { try { cancelSource.Cancel(); } catch { } } catch { try { cancelSource.Cancel(); } catch { } }
})); }));
await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token)); await Task.WhenAny(tasks, Task.Delay(Timeout.Infinite, cancelSource.Token));
ids = ids.Skip(5); ids = ids.Skip(5);
} }
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false); await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
} }
@ -404,7 +403,7 @@ namespace NadekoBot.Modules.Music
var dir = new DirectoryInfo(arg); var dir = new DirectoryInfo(arg);
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories) var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)umsg.Author; var gusr = (IGuildUser)umsg.Author;
foreach (var file in fileEnum) foreach (var file in fileEnum)
{ {
try try
@ -488,7 +487,7 @@ namespace NadekoBot.Modules.Music
.AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true)) .AddField(fb => fb.WithName("**Song Position**").WithValue($"#{num}").WithIsInline(true))
.AddField(fb => fb.WithName("**Song Name**").WithValue(song.PrettyName).WithIsInline(true)) .AddField(fb => fb.WithName("**Song Name**").WithValue(song.PrettyName).WithIsInline(true))
.WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}")) .WithFooter(ef => ef.WithText($"{song.PrettyProvider} | {song.QueuerName}"))
.WithErrorColor(); .WithErrorColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false); await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
@ -546,7 +545,7 @@ namespace NadekoBot.Modules.Music
.WithAuthor(eab => eab.WithName("Song Moved").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Song Moved").WithMusicIcon())
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true)) .AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true)) .AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
.WithFooter(ef => ef.WithText($"{s.PrettyProvider} | {s.QueuerName}")) .WithFooter(ef => ef.WithText($"{s.PrettyProvider} | {s.QueuerName}"))
.WithOkColor(); .WithOkColor();
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false); await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
@ -558,13 +557,29 @@ namespace NadekoBot.Modules.Music
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
{
return; return;
}
musicPlayer.MaxQueueSize = size; musicPlayer.MaxQueueSize = size;
await channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}."); await channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task SetMaxPlaytime(IUserMessage imsg, uint seconds)
{
if (seconds < 15 && seconds != 0)
return;
var channel = (ITextChannel)imsg.Channel;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer))
return;
musicPlayer.MaxPlaytimeSeconds = seconds;
if(seconds == 0)
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
else
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ReptCurSong(IUserMessage umsg) public async Task ReptCurSong(IUserMessage umsg)
@ -583,7 +598,7 @@ namespace NadekoBot.Modules.Music
.WithOkColor() .WithOkColor()
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track")) .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
.WithDescription(currentSong.PrettyName) .WithDescription(currentSong.PrettyName)
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo)) .WithFooter(ef => ef.WithText(currentSong.PrettyInfo))
.Build()).ConfigureAwait(false); .Build()).ConfigureAwait(false);
else else
await channel.SendConfirmAsync($"🔂 Current track repeat stopped.") await channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
@ -658,9 +673,9 @@ namespace NadekoBot.Modules.Music
} }
IUserMessage msg = null; IUserMessage msg = null;
try { msg = await channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { msg = await channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
var usr = (IGuildUser)umsg.Author; var usr = (IGuildUser)umsg.Author;
foreach (var item in mpl.Songs) foreach (var item in mpl.Songs)
{ {
try try
{ {
@ -815,7 +830,7 @@ namespace NadekoBot.Modules.Music
{ {
if (finishedMessage != null) if (finishedMessage != null)
finishedMessage.DeleteAfter(0); finishedMessage.DeleteAfter(0);
finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() finishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
.WithDescription(song.PrettyName) .WithDescription(song.PrettyName)
@ -831,13 +846,13 @@ namespace NadekoBot.Modules.Music
catch { } catch { }
}; };
IUserMessage playingMessage = null; IUserMessage playingMessage = null;
mp.OnStarted += async (player, song) => mp.OnStarted += async (player, song) =>
{ {
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var sender = player as MusicPlayer; var sender = player as MusicPlayer;
if (sender == null) if (sender == null)
return; return;
try try
{ {
if (playingMessage != null) if (playingMessage != null)
@ -846,7 +861,7 @@ namespace NadekoBot.Modules.Music
playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
.WithDescription(song.PrettyName) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText($"🔉 {(int)(sender.Volume * 100)}% | {song.PrettyInfo}")) .WithFooter(ef => ef.WithText(song.PrettyInfo))
.Build()) .Build())
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -886,7 +901,7 @@ namespace NadekoBot.Modules.Music
} }
catch (PlaylistFullException) catch (PlaylistFullException)
{ {
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**. "); } catch { } try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
throw; throw;
} }
if (!silent) if (!silent)
@ -895,9 +910,9 @@ namespace NadekoBot.Modules.Music
{ {
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Queued Song").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\nQueue #{musicPlayer.Playlist.Count + 1}") .WithDescription($"{resolvedSong.PrettyName}\nQueue ")
.WithThumbnail(tn => tn.Url = resolvedSong.Thumbnail) .WithThumbnail(tn => tn.Url = resolvedSong.Thumbnail)
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)) .WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))
.Build()) .Build())
.ConfigureAwait(false); .ConfigureAwait(false);

View File

@ -10,6 +10,7 @@ using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq; using System.Xml.Linq;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Xml;
namespace NadekoBot.Modules.NSFW namespace NadekoBot.Modules.NSFW
{ {
@ -32,7 +33,7 @@ namespace NadekoBot.Modules.NSFW
var rng = new NadekoRandom(); var rng = new NadekoRandom();
Task<string> provider = Task.FromResult(""); Task<string> provider = Task.FromResult("");
switch (rng.Next(0,4)) switch (rng.Next(0, 4))
{ {
case 0: case 0:
provider = GetDanbooruImageLink(tag); provider = GetDanbooruImageLink(tag);
@ -56,6 +57,7 @@ namespace NadekoBot.Modules.NSFW
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.SendMessageAsync(link).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null) public async Task HentaiBomb(IUserMessage umsg, [Remainder] string tag = null)
@ -65,7 +67,7 @@ namespace NadekoBot.Modules.NSFW
tag = tag?.Trim() ?? ""; tag = tag?.Trim() ?? "";
tag = "rating%3Aexplicit+" + tag; tag = "rating%3Aexplicit+" + tag;
var links = await Task.WhenAll(GetGelbooruImageLink(tag), var links = await Task.WhenAll(GetGelbooruImageLink(tag),
GetDanbooruImageLink(tag), GetDanbooruImageLink(tag),
GetKonachanImageLink(tag), GetKonachanImageLink(tag),
GetYandereImageLink(tag)).ConfigureAwait(false); GetYandereImageLink(tag)).ConfigureAwait(false);
@ -78,39 +80,7 @@ namespace NadekoBot.Modules.NSFW
await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false); await channel.SendMessageAsync(String.Join("\n\n", links)).ConfigureAwait(false);
} }
public static async Task<string> GetYandereImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"https://yande.re/post.xml?" +
$"limit=25" +
$"&page={rng.Next(0, 15)}" +
$"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
//var rating = Regex.Matches(webpage, "rating=\"(?<rate>.*?)\"");
if (matches.Count == 0)
return null;
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetYandereImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -119,54 +89,38 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; tag = tag?.Trim() ?? "";
var link = await GetDanbooruImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else else
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("Danbooru"))
.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Konachan(IUserMessage umsg, [Remainder] string tag = null) public Task Yandere(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetKonachanImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null) public Task Konachan(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Konachan);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetGelbooruImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Rule34(IUserMessage umsg, [Remainder] string tag = null) public Task Gelbooru(IUserMessage umsg, [Remainder] string tag = null)
{ => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Gelbooru);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; [NadekoCommand, Usage, Description, Aliases]
var link = await GetRule34ImageLink(tag).ConfigureAwait(false); [RequireContext(ContextType.Guild)]
if (string.IsNullOrWhiteSpace(link)) public Task Rule34(IUserMessage umsg, [Remainder] string tag = null)
await channel.SendErrorAsync("No results found.").ConfigureAwait(false); => Searches.Searches.InternalDapiCommand(umsg, tag, Searches.Searches.DapiSearchType.Rule34);
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -175,11 +129,17 @@ namespace NadekoBot.Modules.NSFW
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? ""; tag = tag?.Trim() ?? "";
var link = await GetE621ImageLink(tag).ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) var url = await GetE621ImageLink(tag).ConfigureAwait(false);
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else else
await channel.SendMessageAsync(link).ConfigureAwait(false); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText("e621"))
.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -232,99 +192,61 @@ namespace NadekoBot.Modules.NSFW
} }
} }
public static async Task<string> GetKonachanImageLink(string tag)
{
var rng = new NadekoRandom();
var link = $"http://konachan.com/post?" +
$"page={rng.Next(0, 5)}";
if (!string.IsNullOrWhiteSpace(tag))
link += $"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "<a class=\"directlink largeimg\" href=\"(?<ll>.*?)\">");
if (matches.Count == 0)
return null;
return "http:" + matches[rng.Next(0, matches.Count)].Groups["ll"].Value;
}
}
public static async Task<string> GetDanbooruImageLink(string tag) public static async Task<string> GetDanbooruImageLink(string tag)
{
var rng = new NadekoRandom();
if (tag == "loli") //loli doesn't work for some reason atm
tag = "flat_chest";
var link = $"http://danbooru.donmai.us/posts?" +
$"page={rng.Next(0, 15)}";
if (!string.IsNullOrWhiteSpace(tag))
link += $"&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(link).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "data-large-file-url=\"(?<id>.*?)\"");
if (matches.Count == 0)
return null;
return $"http://danbooru.donmai.us" +
$"{matches[rng.Next(0, matches.Count)].Groups["id"].Value}";
}
}
public static async Task<string> GetGelbooruImageLink(string tag)
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var webpage = await http.GetStringAsync("http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags="+ tag.Replace(" ", "_")).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var rng = new NadekoRandom();
var match = matches[rng.Next(0, matches.Count)];
return matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
public static async Task<string> GetRule34ImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"http://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var match = matches[rng.Next(0, matches.Count)];
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
public static async Task<string> GetE621ImageLink(string tags)
{ {
try try
{ {
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.AddFakeHeaders(); http.AddFakeHeaders();
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1"); var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&tags=" + tag);
var doc = XDocument.Load(data); var doc = new XmlDocument();
return doc.Descendants("file_url").FirstOrDefault().Value; doc.Load(data);
var nodes = doc.GetElementsByTagName("file-url");
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
return "https://danbooru.donmai.us" + node.InnerText;
} }
} }
catch (Exception ex) catch
{ {
Console.WriteLine("Error in e621 search: \n" + ex); return null;
return "Error, do you have too many tags?";
} }
} }
public static async Task<string> GetE621ImageLink(string tag)
{
try
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + tag);
var doc = new XmlDocument();
doc.Load(data);
var nodes = doc.GetElementsByTagName("file_url");
var node = nodes[new NadekoRandom().Next(0, nodes.Count)];
return node.InnerText;
}
}
catch
{
return null;
}
}
public static Task<string> GetYandereImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere);
public static Task<string> GetKonachanImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Konachan);
public static Task<string> GetGelbooruImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru);
public static Task<string> GetRule34ImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34);
} }
} }

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches.Commands.Models
{
public class Audio
{
public string url { get; set; }
}
public class Example
{
public List<Audio> audio { get; set; }
public string text { get; set; }
}
public class GramaticalInfo
{
public string type { get; set; }
}
public class Sens
{
public object Definition { get; set; }
public List<Example> Examples { get; set; }
public GramaticalInfo Gramatical_info { get; set; }
}
public class Result
{
public string Part_of_speech { get; set; }
public List<Sens> Senses { get; set; }
public string Url { get; set; }
}
public class DefineModel
{
public List<Result> Results { get; set; }
}
}

View File

@ -28,18 +28,17 @@ namespace NadekoBot.Modules.Searches
TranslatedChannels = new ConcurrentDictionary<ulong, bool>(); TranslatedChannels = new ConcurrentDictionary<ulong, bool>();
UserLanguages = new ConcurrentDictionary<UserChannelPair, string>(); UserLanguages = new ConcurrentDictionary<UserChannelPair, string>();
NadekoBot.Client.MessageReceived += (msg) => NadekoBot.Client.MessageReceived += async (msg) =>
{ {
var umsg = msg as IUserMessage; try
if(umsg == null)
return Task.CompletedTask;
bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return Task.CompletedTask;
var t = Task.Run(async () =>
{ {
var umsg = msg as IUserMessage;
if (umsg == null)
return;
bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return;
var key = new UserChannelPair() var key = new UserChannelPair()
{ {
UserId = umsg.Author.Id, UserId = umsg.Author.Id,
@ -50,18 +49,13 @@ namespace NadekoBot.Modules.Searches
if (!UserLanguages.TryGetValue(key, out langs)) if (!UserLanguages.TryGetValue(key, out langs))
return; return;
try var text = await TranslateInternal(umsg, langs, umsg.Resolve(UserMentionHandling.Ignore), true)
{ .ConfigureAwait(false);
var text = await TranslateInternal(umsg, langs, umsg.Resolve(UserMentionHandling.Ignore), true) if (autoDelete)
.ConfigureAwait(false); try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
if (autoDelete) await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } }
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` "+text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false); catch { }
}
catch { }
});
return Task.CompletedTask;
}; };
} }

View File

@ -22,6 +22,8 @@ using AngleSharp.Parser.Html;
using AngleSharp; using AngleSharp;
using AngleSharp.Dom.Html; using AngleSharp.Dom.Html;
using AngleSharp.Dom; using AngleSharp.Dom;
using System.Xml;
using System.Xml.Linq;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
@ -470,6 +472,42 @@ namespace NadekoBot.Modules.Searches
} }
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Define(IUserMessage msg, [Remainder] string word)
{
var channel = (ITextChannel)msg.Channel;
if (string.IsNullOrWhiteSpace(word))
return;
using (var http = new HttpClient())
{
var res = await http.GetStringAsync("http://api.pearson.com/v2/dictionaries/entries?headword=" + WebUtility.UrlEncode(word.Trim())).ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<DefineModel>(res);
var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0];
if (sense?.Definition == null)
return;
string definition = sense.Definition.ToString();
if (!(sense.Definition is string))
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Define: " + word)
.WithDescription(definition)
.WithFooter(efb => efb.WithText(sense.Gramatical_info?.type));
if (sense.Examples != null)
embed.AddField(efb => efb.WithName("Example").WithValue(sense.Examples.First().text));
await channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null) public async Task Hashtag(IUserMessage umsg, [Remainder] string query = null)
@ -558,17 +596,8 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Safebooru(IUserMessage umsg, [Remainder] string tag = null) public Task Safebooru(IUserMessage umsg, [Remainder] string tag = null)
{ => InternalDapiCommand(umsg, tag, DapiSearchType.Safebooru);
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var link = await GetSafebooruImageLink(tag).ConfigureAwait(false);
if (link == null)
await channel.SendErrorAsync("No results.");
else
await channel.SendMessageAsync(link).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -648,22 +677,6 @@ namespace NadekoBot.Modules.Searches
await channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false); await channel.SendMessageAsync(await NadekoBot.Google.ShortenUrl(usr.AvatarUrl).ConfigureAwait(false)).ConfigureAwait(false);
} }
public static async Task<string> GetSafebooruImageLink(string tag)
{
var rng = new NadekoRandom();
var url =
$"http://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag.Replace(" ", "_")}";
using (var http = new HttpClient())
{
var webpage = await http.GetStringAsync(url).ConfigureAwait(false);
var matches = Regex.Matches(webpage, "file_url=\"(?<url>.*?)\"");
if (matches.Count == 0)
return null;
var match = matches[rng.Next(0, matches.Count)];
return "http:" + matches[rng.Next(0, matches.Count)].Groups["url"].Value;
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Wikia(IUserMessage umsg, string target, [Remainder] string query = null) public async Task Wikia(IUserMessage umsg, string target, [Remainder] string query = null)
@ -772,6 +785,77 @@ namespace NadekoBot.Modules.Searches
} }
} }
public enum DapiSearchType
{
Safebooru,
Gelbooru,
Konachan,
Rule34,
Yandere
}
public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
{
var channel = (ITextChannel)umsg.Channel;
tag = tag?.Trim() ?? "";
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
else
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
.WithImageUrl(url)
.WithFooter(efb => efb.WithText(type.ToString()))
.Build()).ConfigureAwait(false);
}
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
{
tag = tag?.Replace(" ", "_");
string website = "";
switch (type)
{
case DapiSearchType.Safebooru:
website = $"https://safebooru.org/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Gelbooru:
website = $"http://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Rule34:
website = $"https://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Konachan:
website = $"https://konachan.com/post.xml?s=post&q=index&limit=100&tags={tag}";
break;
case DapiSearchType.Yandere:
website = $"https://yande.re/post.xml?limit=100&tags={tag}";
break;
}
try
{
using (var http = new HttpClient())
{
http.AddFakeHeaders();
var data = await http.GetStreamAsync(website);
var doc = new XmlDocument();
doc.Load(data);
var node = doc.LastChild.ChildNodes[new NadekoRandom().Next(0, doc.LastChild.ChildNodes.Count)];
var url = node.Attributes["file_url"].Value;
if (!url.StartsWith("http"))
url = "https:" + url;
return url;
}
}
catch
{
return null;
}
}
public static async Task<bool> ValidateQuery(ITextChannel ch, string query) public static async Task<bool> ValidateQuery(ITextChannel ch, string query)
{ {
if (!string.IsNullOrEmpty(query.Trim())) return true; if (!string.IsNullOrEmpty(query.Trim())) return true;

View File

@ -95,5 +95,31 @@ namespace NadekoBot.Modules.Utility
.WithOkColor(); .WithOkColor();
await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false); await msg.Channel.EmbedAsync(embed.Build()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task Activity(IUserMessage imsg, int page = 1)
{
const int activityPerPage = 15;
page -= 1;
if (page < 0)
return;
int startCount = page * activityPerPage;
StringBuilder str = new StringBuilder();
foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage))
{
str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total");
}
await imsg.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}")
.WithOkColor()
.WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total."))
.WithDescription(str.ToString())
.Build());
}
} }
} }

View File

@ -113,6 +113,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to activity.
/// </summary>
public static string activity_cmd {
get {
return ResourceManager.GetString("activity_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Checks for spammers..
/// </summary>
public static string activity_desc {
get {
return ResourceManager.GetString("activity_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}activity`.
/// </summary>
public static string activity_usage {
get {
return ResourceManager.GetString("activity_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to addcustreact acr. /// Looks up a localized string similar to addcustreact acr.
/// </summary> /// </summary>
@ -2003,6 +2030,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to define def.
/// </summary>
public static string define_cmd {
get {
return ResourceManager.GetString("define_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Finds a definition of a word..
/// </summary>
public static string define_desc {
get {
return ResourceManager.GetString("define_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}def heresy`.
/// </summary>
public static string define_usage {
get {
return ResourceManager.GetString("define_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to defvol dv. /// Looks up a localized string similar to defvol dv.
/// </summary> /// </summary>
@ -6350,6 +6404,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to setmaxplaytime smp.
/// </summary>
public static string setmaxplaytime_cmd {
get {
return ResourceManager.GetString("setmaxplaytime_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sets a maximum number of seconds (&gt;14) a song can run before being skipped automatically. Set 0 to have no limit..
/// </summary>
public static string setmaxplaytime_desc {
get {
return ResourceManager.GetString("setmaxplaytime_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}smp 0` or `{0}smp 270`.
/// </summary>
public static string setmaxplaytime_usage {
get {
return ResourceManager.GetString("setmaxplaytime_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to setmaxqueue smq. /// Looks up a localized string similar to setmaxqueue smq.
/// </summary> /// </summary>

View File

@ -2817,4 +2817,31 @@
<data name="fairplay_usage" xml:space="preserve"> <data name="fairplay_usage" xml:space="preserve">
<value>`{0}fp`</value> <value>`{0}fp`</value>
</data> </data>
<data name="define_cmd" xml:space="preserve">
<value>define def</value>
</data>
<data name="define_desc" xml:space="preserve">
<value>Finds a definition of a word.</value>
</data>
<data name="define_usage" xml:space="preserve">
<value>`{0}def heresy`</value>
</data>
<data name="setmaxplaytime_cmd" xml:space="preserve">
<value>setmaxplaytime smp</value>
</data>
<data name="setmaxplaytime_desc" xml:space="preserve">
<value>Sets a maximum number of seconds (&gt;14) a song can run before being skipped automatically. Set 0 to have no limit.</value>
</data>
<data name="setmaxplaytime_usage" xml:space="preserve">
<value>`{0}smp 0` or `{0}smp 270`</value>
</data>
<data name="activity_cmd" xml:space="preserve">
<value>activity</value>
</data>
<data name="activity_desc" xml:space="preserve">
<value>Checks for spammers.</value>
</data>
<data name="activity_usage" xml:space="preserve">
<value>`{0}activity`</value>
</data>
</root> </root>

View File

@ -16,20 +16,15 @@ using NadekoBot.Modules.Help;
using static NadekoBot.Modules.Administration.Administration; using static NadekoBot.Modules.Administration.Administration;
using NadekoBot.Modules.CustomReactions; using NadekoBot.Modules.CustomReactions;
using NadekoBot.Modules.Games; using NadekoBot.Modules.Games;
using System.Collections.Concurrent;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class IGuildUserComparer : IEqualityComparer<IGuildUser> public class IGuildUserComparer : IEqualityComparer<IGuildUser>
{ {
public bool Equals(IGuildUser x, IGuildUser y) public bool Equals(IGuildUser x, IGuildUser y) => x.Id == y.Id;
{
return x.Id == y.Id;
}
public int GetHashCode(IGuildUser obj) public int GetHashCode(IGuildUser obj) => obj.Id.GetHashCode();
{
return obj.Id.GetHashCode();
}
} }
public class CommandHandler public class CommandHandler
{ {
@ -39,7 +34,10 @@ namespace NadekoBot.Services
private List<IDMChannel> ownerChannels { get; set; } private List<IDMChannel> ownerChannels { get; set; }
public event Func<IUserMessage,Command, Task> CommandExecuted = delegate { return Task.CompletedTask; }; public event Func<IUserMessage, Command, Task> CommandExecuted = delegate { return Task.CompletedTask; };
//userid/msg count
public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>();
public CommandHandler(ShardedDiscordClient client, CommandService commandService) public CommandHandler(ShardedDiscordClient client, CommandService commandService)
{ {
@ -64,46 +62,31 @@ namespace NadekoBot.Services
_client.MessageReceived += MessageReceivedHandler; _client.MessageReceived += MessageReceivedHandler;
} }
private Task MessageReceivedHandler(IMessage msg) private async void MessageReceivedHandler(IMessage msg)
{ {
var usrMsg = msg as IUserMessage; try
if (usrMsg == null)
return Task.CompletedTask;
if (usrMsg.Author.IsBot || !NadekoBot.Ready) //no bots
return Task.CompletedTask;
var throwaway = Task.Run(async () =>
{ {
var usrMsg = msg as IUserMessage;
if (usrMsg == null)
return;
if (!usrMsg.IsAuthor())
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
if (usrMsg.Author.IsBot || !NadekoBot.Ready) //no bots
return;
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
try
var guild = (msg.Channel as ITextChannel)?.Guild;
if (guild != null && guild.OwnerId != usrMsg.Author.Id)
{ {
var guild = (msg.Channel as ITextChannel)?.Guild; if (Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) ||
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id))
if (guild != null && guild.OwnerId != usrMsg.Author.Id)
{ {
if (Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) || if (usrMsg.Content.IsDiscordInvite())
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id))
{
if (usrMsg.Content.IsDiscordInvite())
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return;
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
}
}
}
var filteredWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id).Concat(Permissions.FilterCommands.FilteredWordsForServer(guild.Id));
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredWords.Any(w => wordsInMessage.Contains(w)))
{ {
try try
{ {
@ -112,108 +95,122 @@ namespace NadekoBot.Services
} }
catch (HttpException ex) catch (HttpException ex)
{ {
_log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex); _log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
} }
} }
} }
BlacklistItem blacklistedItem; var filteredWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id).Concat(Permissions.FilterCommands.FilteredWordsForServer(guild.Id));
if ((blacklistedItem = Permissions.BlacklistCommands.BlacklistedItems.FirstOrDefault(bi => var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
(bi.Type == BlacklistItem.BlacklistType.Server && bi.ItemId == guild?.Id) || if (filteredWords.Any(w => wordsInMessage.Contains(w)))
(bi.Type == BlacklistItem.BlacklistType.Channel && bi.ItemId == msg.Channel.Id) ||
(bi.Type == BlacklistItem.BlacklistType.User && bi.ItemId == usrMsg.Author.Id))) != null)
{ {
return; try
}
try
{
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg);
if (cleverbotExecuted)
return;
}
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
try
{
// maybe this message is a custom reaction
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
//if it was, don't execute the command
if (crExecuted)
return;
}
catch { }
var t = await ExecuteCommand(usrMsg, usrMsg.Content, guild, usrMsg.Author, MultiMatchHandling.Best);
var command = t.Item1;
var permCache = t.Item2;
var result = t.Item3;
sw.Stop();
var channel = (usrMsg.Channel as ITextChannel);
if (result.IsSuccess)
{
await CommandExecuted(usrMsg, command);
_log.Info("Command Executed after {4}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}",
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content, // {3}
sw.Elapsed.TotalSeconds // {4}
);
}
else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand)
{
_log.Warn("Command Errored after {5}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}\n\t" +
"Error: {4}",
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content,// {3}
result.ErrorReason, // {4}
sw.Elapsed.TotalSeconds // {5}
);
if (guild != null && command != null && result.Error == CommandError.Exception)
{ {
if (permCache != null && permCache.Verbose) await usrMsg.DeleteAsync().ConfigureAwait(false);
try { await msg.Channel.SendMessageAsync("⚠️ " + result.ErrorReason).ConfigureAwait(false); } catch { } return;
} }
} catch (HttpException ex)
else
{
if (msg.Channel is IPrivateChannel)
{ {
//rofl, gotta do this to prevent this message from occuring on polls _log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex);
int vote;
if (int.TryParse(msg.Content, out vote)) return;
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels);
} }
} }
} }
catch (Exception ex)
BlacklistItem blacklistedItem;
if ((blacklistedItem = Permissions.BlacklistCommands.BlacklistedItems.FirstOrDefault(bi =>
(bi.Type == BlacklistItem.BlacklistType.Server && bi.ItemId == guild?.Id) ||
(bi.Type == BlacklistItem.BlacklistType.Channel && bi.ItemId == msg.Channel.Id) ||
(bi.Type == BlacklistItem.BlacklistType.User && bi.ItemId == usrMsg.Author.Id))) != null)
{ {
_log.Warn(ex, "Error in CommandHandler"); return;
if (ex.InnerException != null)
_log.Warn(ex.InnerException, "Inner Exception of the error in CommandHandler");
} }
});
return Task.CompletedTask; try
{
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg);
if (cleverbotExecuted)
return;
}
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
try
{
// maybe this message is a custom reaction
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
//if it was, don't execute the command
if (crExecuted)
return;
}
catch { }
var t = await ExecuteCommand(usrMsg, usrMsg.Content, guild, usrMsg.Author, MultiMatchHandling.Best);
var command = t.Item1;
var permCache = t.Item2;
var result = t.Item3;
sw.Stop();
var channel = (usrMsg.Channel as ITextChannel);
if (result.IsSuccess)
{
await CommandExecuted(usrMsg, command);
_log.Info("Command Executed after {4}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}",
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content, // {3}
sw.Elapsed.TotalSeconds // {4}
);
}
else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand)
{
_log.Warn("Command Errored after {5}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}\n\t" +
"Error: {4}",
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content,// {3}
result.ErrorReason, // {4}
sw.Elapsed.TotalSeconds // {5}
);
if (guild != null && command != null && result.Error == CommandError.Exception)
{
if (permCache != null && permCache.Verbose)
try { await msg.Channel.SendMessageAsync("⚠️ " + result.ErrorReason).ConfigureAwait(false); } catch { }
}
}
else
{
if (msg.Channel is IPrivateChannel)
{
//rofl, gotta do this to prevent this message from occuring on polls
int vote;
if (int.TryParse(msg.Content, out vote)) return;
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels);
}
}
}
catch (Exception ex)
{
_log.Warn(ex, "Error in CommandHandler");
if (ex.InnerException != null)
_log.Warn(ex.InnerException, "Inner Exception of the error in CommandHandler");
}
} }
public async Task<Tuple<Command, PermissionCache, IResult>> ExecuteCommand(IUserMessage message, string input, IGuild guild, IUser user, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Best) { public async Task<Tuple<Command, PermissionCache, IResult>> ExecuteCommand(IUserMessage message, string input, IGuild guild, IUser user, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Best)
{
var searchResult = _commandService.Search(message, input); var searchResult = _commandService.Search(message, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return new Tuple<Command, PermissionCache, IResult>(null, null, searchResult); return new Tuple<Command, PermissionCache, IResult>(null, null, searchResult);
@ -312,4 +309,4 @@ namespace NadekoBot.Services
Command = cmd; Command = cmd;
} }
} }
} }

View File

@ -13,20 +13,20 @@ namespace NadekoBot
private DiscordSocketConfig discordSocketConfig; private DiscordSocketConfig discordSocketConfig;
private Logger _log { get; } private Logger _log { get; }
public event Func<IGuildUser, Task> UserJoined = delegate { return Task.CompletedTask; }; public event Action<IGuildUser> UserJoined = delegate { };
public event Func<IMessage, Task> MessageReceived = delegate { return Task.CompletedTask; }; public event Action<IMessage> MessageReceived = delegate { };
public event Func<IGuildUser, Task> UserLeft = delegate { return Task.CompletedTask; }; public event Action<IGuildUser> UserLeft = delegate { };
public event Func<IGuildUser, IGuildUser, Task> UserUpdated = delegate { return Task.CompletedTask; }; public event Action<IGuildUser, IGuildUser> UserUpdated = delegate { };
public event Func<Optional<IMessage>, IMessage, Task> MessageUpdated = delegate { return Task.CompletedTask; }; public event Action<Optional<IMessage>, IMessage> MessageUpdated = delegate { };
public event Func<ulong, Optional<IMessage>, Task> MessageDeleted = delegate { return Task.CompletedTask; }; public event Action<ulong, Optional<IMessage>> MessageDeleted = delegate { };
public event Func<IUser, IGuild, Task> UserBanned = delegate { return Task.CompletedTask; }; public event Action<IUser, IGuild> UserBanned = delegate { };
public event Func<IUser, IGuild, Task> UserUnbanned = delegate { return Task.CompletedTask; }; public event Action<IUser, IGuild> UserUnbanned = delegate { };
public event Func<IGuildUser, IPresence, IPresence, Task> UserPresenceUpdated = delegate { return Task.CompletedTask; }; public event Action<IGuildUser, IPresence, IPresence> UserPresenceUpdated = delegate { };
public event Func<IUser, IVoiceState, IVoiceState, Task> UserVoiceStateUpdated = delegate { return Task.CompletedTask; }; public event Action<IUser, IVoiceState, IVoiceState> UserVoiceStateUpdated = delegate { };
public event Func<IChannel, Task> ChannelCreated = delegate { return Task.CompletedTask; }; public event Action<IChannel> ChannelCreated = delegate { };
public event Func<IChannel, Task> ChannelDestroyed = delegate { return Task.CompletedTask; }; public event Action<IChannel> ChannelDestroyed = delegate { };
public event Func<IChannel, IChannel, Task> ChannelUpdated = delegate { return Task.CompletedTask; }; public event Action<IChannel, IChannel> ChannelUpdated = delegate { };
public event Func<Exception, Task> Disconnected = delegate { return Task.CompletedTask; }; public event Action<Exception> Disconnected = delegate { };
private IReadOnlyList<DiscordSocketClient> Clients { get; } private IReadOnlyList<DiscordSocketClient> Clients { get; }
@ -41,18 +41,19 @@ namespace NadekoBot
discordSocketConfig.ShardId = i; discordSocketConfig.ShardId = i;
var client = new DiscordSocketClient(discordSocketConfig); var client = new DiscordSocketClient(discordSocketConfig);
clientList.Add(client); clientList.Add(client);
client.UserJoined += async arg1 => await UserJoined(arg1); client.UserJoined += arg1 => { UserJoined(arg1); return Task.CompletedTask; };
client.MessageReceived += async arg1 => await MessageReceived(arg1); client.MessageReceived += arg1 => { MessageReceived(arg1); return Task.CompletedTask; };
client.UserLeft += async arg1 => await UserLeft(arg1); client.UserLeft += arg1 => { UserLeft(arg1); return Task.CompletedTask; };
client.UserUpdated += async (arg1, gu2) => await UserUpdated(arg1, gu2); client.UserUpdated += (arg1, gu2) => { UserUpdated(arg1, gu2); return Task.CompletedTask; };
client.MessageUpdated += async (arg1, m2) => await MessageUpdated(arg1, m2); client.MessageUpdated += (arg1, m2) => { MessageUpdated(arg1, m2); return Task.CompletedTask; };
client.MessageDeleted += async (arg1, arg2) => await MessageDeleted(arg1, arg2); client.MessageDeleted += (arg1, arg2) => { MessageDeleted(arg1, arg2); return Task.CompletedTask; };
client.UserBanned += async (arg1, arg2) => await UserBanned(arg1, arg2); client.UserBanned += (arg1, arg2) => { UserBanned(arg1, arg2); return Task.CompletedTask; };
client.UserPresenceUpdated += async (arg1, arg2, arg3) => await UserPresenceUpdated(arg1, arg2, arg3); client.UserUnbanned += (arg1, arg2) => { UserUnbanned(arg1, arg2); return Task.CompletedTask; };
client.UserVoiceStateUpdated += async (arg1, arg2, arg3) => await UserVoiceStateUpdated(arg1, arg2, arg3); client.UserPresenceUpdated += (arg1, arg2, arg3) => { UserPresenceUpdated(arg1, arg2, arg3); return Task.CompletedTask; };
client.ChannelCreated += async arg => await ChannelCreated(arg); client.UserVoiceStateUpdated += (arg1, arg2, arg3) => { UserVoiceStateUpdated(arg1, arg2, arg3); return Task.CompletedTask; };
client.ChannelDestroyed += async arg => await ChannelDestroyed(arg); client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; };
client.ChannelUpdated += async (arg1, arg2) => await ChannelUpdated(arg1, arg2); client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; };
client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; };
_log.Info($"Shard #{i} initialized."); _log.Info($"Shard #{i} initialized.");
} }