Timed Mute added (.mute X @User)

This commit is contained in:
Kwoth 2017-03-10 23:06:22 +01:00
parent 5c300b2cf5
commit 93c28918d0
10 changed files with 1474 additions and 10 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class unmutetimers : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UnmuteTimer",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
DateAdded = table.Column<DateTime>(nullable: true),
GuildConfigId = table.Column<int>(nullable: true),
UnmuteAt = table.Column<DateTime>(nullable: false),
UserId = table.Column<ulong>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UnmuteTimer", x => x.Id);
table.ForeignKey(
name: "FK_UnmuteTimer_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UnmuteTimer_GuildConfigId",
table: "UnmuteTimer",
column: "GuildConfigId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UnmuteTimer");
}
}
}

View File

@ -924,6 +924,26 @@ namespace NadekoBot.Migrations
b.ToTable("SelfAssignableRoles"); b.ToTable("SelfAssignableRoles");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId");
b.Property<DateTime>("UnmuteAt");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("UnmuteTimer");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -1169,6 +1189,13 @@ namespace NadekoBot.Migrations
.HasForeignKey("BotConfigId"); .HasForeignKey("BotConfigId");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.UnmuteTimer", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("UnmuteTimers")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
{ {
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity") b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity")

View File

@ -2,13 +2,13 @@
using Discord.Commands; using Discord.Commands;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
@ -20,6 +20,8 @@ namespace NadekoBot.Modules.Administration
{ {
private static ConcurrentDictionary<ulong, string> guildMuteRoles { get; } private static ConcurrentDictionary<ulong, string> guildMuteRoles { get; }
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> mutedUsers { get; } private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> mutedUsers { get; }
private static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>> unmuteTimers { get; }
= new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>>();
public static event Action<IGuildUser, MuteType> UserMuted = delegate { }; public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { }; public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
@ -43,6 +45,23 @@ namespace NadekoBot.Modules.Administration
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId)) v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
)); ));
foreach (var conf in configs)
{
foreach (var x in conf.UnmuteTimers)
{
TimeSpan after;
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
{
after = TimeSpan.FromMinutes(2);
}
else
{
after = x.UnmuteAt - DateTime.UtcNow;
}
StartUnmuteTimer(conf.GuildId, x.UserId, after);
}
}
NadekoBot.Client.UserJoined += Client_UserJoined; NadekoBot.Client.UserJoined += Client_UserJoined;
} }
@ -67,10 +86,15 @@ namespace NadekoBot.Modules.Administration
public static async Task MuteUser(IGuildUser usr) public static async Task MuteUser(IGuildUser usr)
{ {
await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false); await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false);
await usr.AddRolesAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); var muteRole = await GetMuteRole(usr.Guild);
if (!usr.RoleIds.Contains(muteRole.Id))
await usr.AddRolesAsync(muteRole).ConfigureAwait(false);
StopUnmuteTimer(usr.GuildId, usr.Id);
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers)); var config = uow.GuildConfigs.For(usr.Guild.Id,
set => set.Include(gc => gc.MutedUsers)
.Include(gc => gc.UnmuteTimers));
config.MutedUsers.Add(new MutedUserId() config.MutedUsers.Add(new MutedUserId()
{ {
UserId = usr.Id UserId = usr.Id
@ -78,7 +102,9 @@ namespace NadekoBot.Modules.Administration
ConcurrentHashSet<ulong> muted; ConcurrentHashSet<ulong> muted;
if (mutedUsers.TryGetValue(usr.Guild.Id, out muted)) if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
muted.Add(usr.Id); muted.Add(usr.Id);
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
UserMuted(usr, MuteType.All); UserMuted(usr, MuteType.All);
@ -86,11 +112,13 @@ namespace NadekoBot.Modules.Administration
public static async Task UnmuteUser(IGuildUser usr) public static async Task UnmuteUser(IGuildUser usr)
{ {
StopUnmuteTimer(usr.GuildId, usr.Id);
await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false);
await usr.RemoveRolesAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); try { await usr.RemoveRolesAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); } catch { /*ignore*/ }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers)); var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers)
.Include(gc => gc.UnmuteTimers));
config.MutedUsers.Remove(new MutedUserId() config.MutedUsers.Remove(new MutedUserId()
{ {
UserId = usr.Id UserId = usr.Id
@ -98,6 +126,9 @@ namespace NadekoBot.Modules.Administration
ConcurrentHashSet<ulong> muted; ConcurrentHashSet<ulong> muted;
if (mutedUsers.TryGetValue(usr.Guild.Id, out muted)) if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
muted.TryRemove(usr.Id); muted.TryRemove(usr.Id);
config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
UserUnmuted(usr, MuteType.All); UserUnmuted(usr, MuteType.All);
@ -139,6 +170,79 @@ namespace NadekoBot.Modules.Administration
return muteRole; return muteRole;
} }
public static async Task TimedMute(IGuildUser user, TimeSpan after)
{
await MuteUser(user).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(user.GuildId, set => set.Include(x => x.UnmuteTimers));
config.UnmuteTimers.Add(new UnmuteTimer()
{
UserId = user.Id,
UnmuteAt = DateTime.UtcNow + after,
}); // add teh unmute timer to the database
uow.Complete();
}
StartUnmuteTimer(user.GuildId, user.Id, after); // start the timer
}
public static void StartUnmuteTimer(ulong guildId, ulong userId, TimeSpan after)
{
//load the unmute timers for this guild
var userUnmuteTimers = unmuteTimers.GetOrAdd(guildId, new ConcurrentDictionary<ulong, Timer>());
//unmute timer to be added
var toAdd = new Timer(async _ =>
{
try
{
var guild = NadekoBot.Client.GetGuild(guildId); // load the guild
if (guild == null)
{
RemoveUnmuteTimerFromDb(guildId, userId);
return; // if guild can't be found, just remove the timer from db
}
// unmute the user, this will also remove the timer from the db
await UnmuteUser(guild.GetUser(userId)).ConfigureAwait(false);
}
catch (Exception ex)
{
RemoveUnmuteTimerFromDb(guildId, userId); // if unmute errored, just remove unmute from db
Administration._log.Warn("Couldn't unmute user {0} in guild {1}", userId, guildId);
Administration._log.Warn(ex);
}
}, null, after, Timeout.InfiniteTimeSpan);
//add it, or stop the old one and add this one
userUnmuteTimers.AddOrUpdate(userId, (key) => toAdd, (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return toAdd;
});
}
public static void StopUnmuteTimer(ulong guildId, ulong userId)
{
ConcurrentDictionary<ulong, Timer> userUnmuteTimers;
if (!unmuteTimers.TryGetValue(guildId, out userUnmuteTimers)) return;
Timer removed;
if(userUnmuteTimers.TryRemove(userId, out removed))
{
removed.Change(Timeout.Infinite, Timeout.Infinite);
}
}
private static void RemoveUnmuteTimerFromDb(ulong guildId, ulong userId)
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.UnmuteTimers));
config.UnmuteTimers.RemoveWhere(x => x.UserId == userId);
uow.Complete();
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
@ -170,6 +274,7 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)] [RequireUserPermission(GuildPermission.MuteMembers)]
[Priority(1)]
public async Task Mute(IGuildUser user) public async Task Mute(IGuildUser user)
{ {
try try
@ -183,6 +288,27 @@ namespace NadekoBot.Modules.Administration
} }
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.MuteMembers)]
[Priority(0)]
public async Task Mute(int minutes, IGuildUser user)
{
if (minutes < 1)
return;
try
{
await TimedMute(user, TimeSpan.FromMinutes(minutes)).ConfigureAwait(false);
await ReplyConfirmLocalized("user_muted_time", Format.Bold(user.ToString()), minutes).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]

View File

@ -720,7 +720,7 @@ namespace NadekoBot.Modules.Music
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon()) .WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon())
.WithDescription(string.Join("\n", playlists.Select(r => .WithDescription(string.Join("\n", playlists.Select(r =>
GetText("playlists", "#" + r.Id, r.Name, r.Author, r.Songs.Count)))) GetText("playlists", r.Id, r.Name, r.Author, r.Songs.Count))))
.WithOkColor(); .WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);

View File

@ -1531,6 +1531,15 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to {0} has been **muted** from text and voice chat for {1} minutes..
/// </summary>
public static string administration_user_muted_time {
get {
return ResourceManager.GetString("administration_user_muted_time", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to User&apos;s role added. /// Looks up a localized string similar to User&apos;s role added.
/// </summary> /// </summary>
@ -5958,7 +5967,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Deleted a random quote.. /// Looks up a localized string similar to Quote #{0} deleted..
/// </summary> /// </summary>
public static string utility_quote_deleted { public static string utility_quote_deleted {
get { get {

View File

@ -2230,4 +2230,7 @@ Owner ID: {2}</value>
<data name="gambling_page" xml:space="preserve"> <data name="gambling_page" xml:space="preserve">
<value>page {0}</value> <value>page {0}</value>
</data> </data>
<data name="administration_user_muted_time" xml:space="preserve">
<value>{0} has been **muted** from text and voice chat for {1} minutes.</value>
</data>
</root> </root>

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using static NadekoBot.Modules.Administration.Administration; using static NadekoBot.Modules.Administration.Administration;
namespace NadekoBot.Services.Database.Models namespace NadekoBot.Services.Database.Models
@ -68,9 +69,28 @@ namespace NadekoBot.Services.Database.Models
public string Locale { get; set; } = null; public string Locale { get; set; } = null;
public string TimeZoneId { get; set; } = null; public string TimeZoneId { get; set; } = null;
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new HashSet<UnmuteTimer>();
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>(); //public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
} }
public class UnmuteTimer : DbEntity
{
public ulong UserId { get; set; }
public DateTime UnmuteAt { get; set; }
public override int GetHashCode() =>
UserId.GetHashCode();
public override bool Equals(object obj)
{
var ut = obj as UnmuteTimer;
if (ut == null)
return false;
return ut.UserId == UserId;
}
}
public class FilterChannelId : DbEntity public class FilterChannelId : DbEntity
{ {
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }

View File

@ -64,7 +64,7 @@ namespace NadekoBot.Services.Database.Models
public class Permissionv2 : DbEntity, IIndexed public class Permissionv2 : DbEntity, IIndexed
{ {
public int GuildConfigId { get; set; } public int? GuildConfigId { get; set; }
public int Index { get; set; } public int Index { get; set; }
public PrimaryPermissionType PrimaryTarget { get; set; } public PrimaryPermissionType PrimaryTarget { get; set; }

View File

@ -16,6 +16,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
_set.Include(gc => gc.LogSetting) _set.Include(gc => gc.LogSetting)
.ThenInclude(ls => ls.IgnoredChannels) .ThenInclude(ls => ls.IgnoredChannels)
.Include(gc => gc.MutedUsers) .Include(gc => gc.MutedUsers)
.Include(gc => gc.UnmuteTimers)
.Include(gc => gc.GenerateCurrencyChannelIds) .Include(gc => gc.GenerateCurrencyChannelIds)
.Include(gc => gc.FilterInvitesChannelIds) .Include(gc => gc.FilterInvitesChannelIds)
.Include(gc => gc.FilterWordsChannelIds) .Include(gc => gc.FilterWordsChannelIds)