Only a few things with permissions left, and prefixes

This commit is contained in:
Master Kwoth 2017-05-29 06:13:22 +02:00
parent dfb4c778d2
commit 6d27271d4a
120 changed files with 3033 additions and 13687 deletions

View File

@ -0,0 +1,14 @@
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
{
/// <summary>
/// Implemented by modules which block execution before anything is executed
/// </summary>
public interface IEarlyBlocker
{
Task<bool> TryBlockEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg);
}
}

View File

@ -7,12 +7,12 @@ namespace NadekoBot.DataStructures.ModuleBehaviors
/// <summary> /// <summary>
/// Implemented by modules which can execute something and prevent further commands from being executed. /// Implemented by modules which can execute something and prevent further commands from being executed.
/// </summary> /// </summary>
public interface IBlockingExecutor public interface IEarlyBlockingExecutor
{ {
/// <summary> /// <summary>
/// Try to execute some logic within some module's service. /// Try to execute some logic within some module's service.
/// </summary> /// </summary>
/// <returns>Whether it should block other command executions after it.</returns> /// <returns>Whether it should block other command executions after it.</returns>
Task<bool> TryExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg); Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg);
} }
} }

View File

@ -0,0 +1,7 @@
namespace NadekoBot.DataStructures.ModuleBehaviors
{
public interface IEarlyExecutor
{
}
}

View File

@ -0,0 +1,12 @@
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
{
public interface ILateBlocker
{
Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild,
IMessageChannel channel, IUser user, string moduleName, string commandName);
}
}

View File

@ -0,0 +1,7 @@
namespace NadekoBot.DataStructures.ModuleBehaviors
{
public interface ILateBlockingExecutor
{
}
}

View File

@ -0,0 +1,14 @@
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures.ModuleBehaviors
{
/// <summary>
/// Last thing to be executed, won't stop further executions
/// </summary>
public interface ILateExecutor
{
Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg);
}
}

View File

@ -0,0 +1,18 @@
using Discord.Commands;
using System;
using System.Threading.Tasks;
namespace NadekoBot.DataStructures
{
public class NoPublicBot : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissions(ICommandContext context, CommandInfo command, IServiceProvider services)
{
#if GLOBAL_NADEKo
return Task.FromResult(PreconditionResult.FromError("Not available on the public bot"));
#else
return Task.FromResult(PreconditionResult.FromSuccess());
#endif
}
}
}

View File

@ -702,7 +702,7 @@ namespace NadekoBot.Migrations
column: "BotConfigId"); column: "BotConfigId");
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Repeaters_ChannelId", name: "IX_Repeaters_channelId",
table: "Repeaters", table: "Repeaters",
column: "ChannelId", column: "ChannelId",
unique: true); unique: true);

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Migrations
}); });
migrationBuilder.CreateIndex( migrationBuilder.CreateIndex(
name: "IX_Repeaters_ChannelId", name: "IX_Repeaters_channelId",
table: "Repeaters", table: "Repeaters",
column: "ChannelId", column: "ChannelId",
unique: true); unique: true);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class permissionsversion : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "PermissionVersion",
table: "BotConfig",
nullable: false,
defaultValue: 1);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PermissionVersion",
table: "BotConfig");
}
}
}

View File

@ -12,7 +12,7 @@ namespace NadekoBot.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); .HasAnnotation("ProductVersion", "1.1.1");
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
{ {
@ -165,6 +165,8 @@ namespace NadekoBot.Migrations
b.Property<string>("OkColor"); b.Property<string>("OkColor");
b.Property<int>("PermissionVersion");
b.Property<string>("RemindMessageFormat"); b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses"); b.Property<bool>("RotatingStatuses");

View File

@ -15,10 +15,10 @@ namespace NadekoBot.Modules.Administration
public partial class Administration : NadekoTopLevelModule public partial class Administration : NadekoTopLevelModule
{ {
private IGuild _nadekoSupportServer; private IGuild _nadekoSupportServer;
private readonly DbHandler _db; private readonly DbService _db;
private readonly AdministrationService _admin; private readonly AdministrationService _admin;
public Administration(DbHandler db, AdministrationService admin) public Administration(DbService db, AdministrationService admin)
{ {
_db = db; _db = db;
_admin = admin; _admin = admin;

View File

@ -14,10 +14,10 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class AutoAssignRoleCommands : NadekoSubmodule public class AutoAssignRoleCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly AutoAssignRoleService _service; private readonly AutoAssignRoleService _service;
public AutoAssignRoleCommands(AutoAssignRoleService service, DbHandler db) public AutoAssignRoleCommands(AutoAssignRoleService service, DbService db)
{ {
_db = db; _db = db;
_service = service; _service = service;

View File

@ -12,10 +12,10 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class GameChannelCommands : NadekoSubmodule public class GameChannelCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly GameVoiceChannelService _service; private readonly GameVoiceChannelService _service;
public GameChannelCommands(GameVoiceChannelService service, DbHandler db) public GameChannelCommands(GameVoiceChannelService service, DbService db)
{ {
_db = db; _db = db;
_service = service; _service = service;

View File

@ -22,9 +22,9 @@ namespace NadekoBot.Modules.Administration
public class Migration : NadekoSubmodule public class Migration : NadekoSubmodule
{ {
private const int CURRENT_VERSION = 1; private const int CURRENT_VERSION = 1;
private readonly DbHandler _db; private readonly DbService _db;
public Migration(DbHandler db) public Migration(DbService db)
{ {
_db = db; _db = db;
} }

View File

@ -14,9 +14,9 @@ namespace NadekoBot.Modules.Administration
public class MuteCommands : NadekoSubmodule public class MuteCommands : NadekoSubmodule
{ {
private readonly MuteService _service; private readonly MuteService _service;
private readonly DbHandler _db; private readonly DbService _db;
public MuteCommands(MuteService service, DbHandler db) public MuteCommands(MuteService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -14,10 +14,10 @@ namespace NadekoBot.Modules.Administration
public class PlayingRotateCommands : NadekoSubmodule public class PlayingRotateCommands : NadekoSubmodule
{ {
private static readonly object _locker = new object(); private static readonly object _locker = new object();
private readonly DbHandler _db; private readonly DbService _db;
private readonly PlayingRotateService _service; private readonly PlayingRotateService _service;
public PlayingRotateCommands(PlayingRotateService service, DbHandler db) public PlayingRotateCommands(PlayingRotateService service, DbService db)
{ {
_db = db; _db = db;
_service = service; _service = service;

View File

@ -19,9 +19,9 @@ namespace NadekoBot.Modules.Administration
{ {
private readonly ProtectionService _service; private readonly ProtectionService _service;
private readonly MuteService _mute; private readonly MuteService _mute;
private readonly DbHandler _db; private readonly DbService _db;
public ProtectionCommands(ProtectionService service, MuteService mute, DbHandler db) public ProtectionCommands(ProtectionService service, MuteService mute, DbService db)
{ {
_service = service; _service = service;
_mute = mute; _mute = mute;

View File

@ -15,12 +15,12 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class RatelimitCommands : NadekoSubmodule public class SlowModeCommands : NadekoSubmodule
{ {
private readonly RatelimitService _service; private readonly SlowmodeService _service;
private readonly DbHandler _db; private readonly DbService _db;
public RatelimitCommands(RatelimitService service, DbHandler db) public SlowModeCommands(SlowmodeService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class SelfAssignedRolesCommands : NadekoSubmodule public class SelfAssignedRolesCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
public SelfAssignedRolesCommands(DbHandler db) public SelfAssignedRolesCommands(DbService db)
{ {
_db = db; _db = db;
} }

View File

@ -21,14 +21,14 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class SelfCommands : NadekoSubmodule public class SelfCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private static readonly object _locker = new object(); private static readonly object _locker = new object();
private readonly SelfService _service; private readonly SelfService _service;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly IImagesService _images; private readonly IImagesService _images;
public SelfCommands(DbHandler db, SelfService service, DiscordShardedClient client, public SelfCommands(DbService db, SelfService service, DiscordShardedClient client,
IImagesService images) IImagesService images)
{ {
_db = db; _db = db;
@ -203,64 +203,6 @@ namespace NadekoBot.Modules.Administration
await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false);
} }
//todo dm forwarding
//public async Task HandleDmForwarding(IUserMessage msg, ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels)
//{
// if (_service.ForwardDMs && ownerChannels.Length > 0)
// {
// var title = _strings.GetText("dm_from",
// NadekoBot.Localization.DefaultCultureInfo,
// typeof(Administration).Name.ToLowerInvariant()) +
// $" [{msg.Author}]({msg.Author.Id})";
// var attachamentsTxt = GetTextStatic("attachments",
// NadekoBot.Localization.DefaultCultureInfo,
// typeof(Administration).Name.ToLowerInvariant());
// var toSend = msg.Content;
// if (msg.Attachments.Count > 0)
// {
// toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
// string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
// }
// if (_service.ForwardDMsToAllOwners)
// {
// var allOwnerChannels = await Task.WhenAll(ownerChannels
// .Select(x => x.Value))
// .ConfigureAwait(false);
// foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
// {
// try
// {
// await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false);
// }
// catch
// {
// _log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id);
// }
// }
// }
// else
// {
// var firstOwnerChannel = await ownerChannels[0];
// if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
// {
// try
// {
// await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false);
// }
// catch
// {
// // ignored
// }
// }
// }
// }
// }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[OwnerOnly] [OwnerOnly]

View File

@ -14,9 +14,9 @@ namespace NadekoBot.Modules.Administration
public class ServerGreetCommands : NadekoSubmodule public class ServerGreetCommands : NadekoSubmodule
{ {
private readonly GreetSettingsService _greetService; private readonly GreetSettingsService _greetService;
private readonly DbHandler _db; private readonly DbService _db;
public ServerGreetCommands(GreetSettingsService greetService, DbHandler db) public ServerGreetCommands(GreetSettingsService greetService, DbService db)
{ {
_greetService = greetService; _greetService = greetService;
_db = db; _db = db;

View File

@ -19,10 +19,10 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class UserPunishCommands : NadekoSubmodule public class UserPunishCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly MuteService _muteService; private readonly MuteService _muteService;
public UserPunishCommands(DbHandler db, MuteService muteService) public UserPunishCommands(DbService db, MuteService muteService)
{ {
_db = db; _db = db;
_muteService = muteService; _muteService = muteService;

View File

@ -19,9 +19,9 @@ namespace NadekoBot.Modules.Administration
public class VcRoleCommands : NadekoSubmodule public class VcRoleCommands : NadekoSubmodule
{ {
private readonly VcRoleService _service; private readonly VcRoleService _service;
private readonly DbHandler _db; private readonly DbService _db;
public VcRoleCommands(VcRoleService service, DbHandler db) public VcRoleCommands(VcRoleService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;
@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageRoles)] [RequireBotPermission(GuildPermission.ManageRoles)]
//todo discord.net [RequireBotPermission(GuildPermission.ManageChannels)] //todo 999 discord.net [RequireBotPermission(GuildPermission.ManageChannels)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task VcRole([Remainder]IRole role = null) public async Task VcRole([Remainder]IRole role = null)
{ {

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Administration
public class VoicePlusTextCommands : NadekoSubmodule public class VoicePlusTextCommands : NadekoSubmodule
{ {
private readonly VplusTService _service; private readonly VplusTService _service;
private readonly DbHandler _db; private readonly DbService _db;
public VoicePlusTextCommands(VplusTService service, DbHandler db) public VoicePlusTextCommands(VplusTService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -15,11 +15,11 @@ namespace NadekoBot.Modules.CustomReactions
public class CustomReactions : NadekoTopLevelModule public class CustomReactions : NadekoTopLevelModule
{ {
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly DbHandler _db; private readonly DbService _db;
private readonly CustomReactionsService _crs; private readonly CustomReactionsService _crs;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public CustomReactions(IBotCredentials creds, DbHandler db, CustomReactionsService crs, public CustomReactions(IBotCredentials creds, DbService db, CustomReactionsService crs,
DiscordShardedClient client) DiscordShardedClient client)
{ {
_creds = creds; _creds = creds;
@ -239,7 +239,7 @@ namespace NadekoBot.Modules.CustomReactions
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
//todo i can dramatically improve performance of this, if Ids are ordered. //todo 91 i can dramatically improve performance of this, if Ids are ordered.
_crs.GlobalReactions = _crs.GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); _crs.GlobalReactions = _crs.GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true; success = true;
} }

View File

@ -21,16 +21,16 @@ namespace NadekoBot.Modules.Gambling
public class AnimalRacing : NadekoSubmodule public class AnimalRacing : NadekoSubmodule
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>(); public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
public AnimalRacing(BotConfig bc, CurrencyHandler ch, DiscordShardedClient client) public AnimalRacing(BotConfig bc, CurrencyService cs, DiscordShardedClient client)
{ {
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
_client = client; _client = client;
} }
@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Gambling
public async Task Race() public async Task Race()
{ {
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix, var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix,
_bc, _ch, _client,_localization, _strings); _bc, _cs, _client,_localization, _strings);
if (ar.Fail) if (ar.Fail)
await ReplyErrorLocalized("race_failed_starting").ConfigureAwait(false); await ReplyErrorLocalized("race_failed_starting").ConfigureAwait(false);
@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Gambling
await ar.JoinRace(Context.User as IGuildUser, amount); await ar.JoinRace(Context.User as IGuildUser, amount);
} }
//todo needs to be completely isolated, shouldn't use any services in the constructor, //todo 85 needs to be completely isolated, shouldn't use any services in the constructor,
//then move the rest either to the module itself, or the service //then move the rest either to the module itself, or the service
public class AnimalRace public class AnimalRace
{ {
@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Gambling
private readonly ITextChannel _raceChannel; private readonly ITextChannel _raceChannel;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly ILocalization _localization; private readonly ILocalization _localization;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
@ -89,12 +89,12 @@ namespace NadekoBot.Modules.Gambling
public bool Started { get; private set; } public bool Started { get; private set; }
public AnimalRace(ulong serverId, ITextChannel channel, string prefix, BotConfig bc, public AnimalRace(ulong serverId, ITextChannel channel, string prefix, BotConfig bc,
CurrencyHandler ch, DiscordShardedClient client, ILocalization localization, CurrencyService cs, DiscordShardedClient client, ILocalization localization,
NadekoStrings strings) NadekoStrings strings)
{ {
_prefix = prefix; _prefix = prefix;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_serverId = serverId; _serverId = serverId;
_raceChannel = channel; _raceChannel = channel;
@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Gambling
var p = _participants.FirstOrDefault(); var p = _participants.FirstOrDefault();
if (p != null && p.AmountBet > 0) if (p != null && p.AmountBet > 0)
await _ch.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false); await _cs.AddAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false);
End(); End();
return; return;
} }
@ -232,7 +232,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var wonAmount = winner.AmountBet * (_participants.Count - 1); var wonAmount = winner.AmountBet * (_participants.Count - 1);
await _ch.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true) await _cs.AddAsync(winner.User, "Won a Race", wonAmount, true)
.ConfigureAwait(false); .ConfigureAwait(false);
await _raceChannel.SendConfirmAsync(GetText("animal_race"), await _raceChannel.SendConfirmAsync(GetText("animal_race"),
Format.Bold(GetText("animal_race_won_money", winner.User.Mention, Format.Bold(GetText("animal_race_won_money", winner.User.Mention,
@ -287,7 +287,7 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
if (amount > 0) if (amount > 0)
if (!await _ch.RemoveCurrencyAsync(u, "BetRace", amount, false).ConfigureAwait(false)) if (!await _cs.RemoveAsync(u, "BetRace", amount, false).ConfigureAwait(false))
{ {
await _raceChannel.SendErrorAsync(GetText("not_enough", _bc.CurrencySign)).ConfigureAwait(false); await _raceChannel.SendErrorAsync(GetText("not_enough", _bc.CurrencySign)).ConfigureAwait(false);
return; return;

View File

@ -36,13 +36,13 @@ namespace NadekoBot.Modules.Gambling
private string _secretCode = string.Empty; private string _secretCode = string.Empty;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
public CurrencyEvents(DiscordShardedClient client, BotConfig bc, CurrencyHandler ch) public CurrencyEvents(DiscordShardedClient client, BotConfig bc, CurrencyService cs)
{ {
_client = client; _client = client;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var _ = Task.Run(async () => var _ = Task.Run(async () =>
{ {
await _ch.AddCurrencyAsync(arg.Author, "Sneaky Game Event", 100, false) await _cs.AddAsync(arg.Author, "Sneaky Game Event", 100, false)
.ConfigureAwait(false); .ConfigureAwait(false);
try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
@ -137,7 +137,7 @@ namespace NadekoBot.Modules.Gambling
desc, footer: footer) desc, footer: footer)
.ConfigureAwait(false); .ConfigureAwait(false);
await new FlowerReactionEvent(_client, _ch).Start(msg, context, amount); await new FlowerReactionEvent(_client, _cs).Start(msg, context, amount);
} }
} }
} }
@ -152,18 +152,18 @@ namespace NadekoBot.Modules.Gambling
private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
private readonly Logger _log; private readonly Logger _log;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private IUserMessage StartingMessage { get; set; } private IUserMessage StartingMessage { get; set; }
private CancellationTokenSource Source { get; } private CancellationTokenSource Source { get; }
private CancellationToken CancelToken { get; } private CancellationToken CancelToken { get; }
public FlowerReactionEvent(DiscordShardedClient client, CurrencyHandler ch) public FlowerReactionEvent(DiscordShardedClient client, CurrencyService cs)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_client = client; _client = client;
_ch = ch; _cs = cs;
Source = new CancellationTokenSource(); Source = new CancellationTokenSource();
CancelToken = Source.Token; CancelToken = Source.Token;
} }
@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Gambling
{ {
if (r.Emote.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) if (r.Emote.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
{ {
await _ch.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", amount, false) await _cs.AddAsync(r.User.Value, "Flower Reaction Event", amount, false)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -220,8 +220,8 @@ namespace NadekoBot.Modules.Gambling
if (num == 10) if (num == 10)
{ {
var images = _images.Dice; var images = _images.Dice;
using (var imgOneStream = images[1].Value.ToStream()) using (var imgOneStream = images[1].ToStream())
using (var imgZeroStream = images[0].Value.ToStream()) using (var imgZeroStream = images[0].ToStream())
{ {
Image imgOne = new Image(imgOneStream); Image imgOne = new Image(imgOneStream);
Image imgZero = new Image(imgZeroStream); Image imgZero = new Image(imgZeroStream);
@ -229,7 +229,7 @@ namespace NadekoBot.Modules.Gambling
return new[] { imgOne, imgZero }.Merge(); return new[] { imgOne, imgZero }.Merge();
} }
} }
using (var die = _images.Dice[num].Value.ToStream()) using (var die = _images.Dice[num].ToStream())
{ {
return new Image(die); return new Image(die);
} }

View File

@ -18,15 +18,15 @@ namespace NadekoBot.Modules.Gambling
{ {
private readonly IImagesService _images; private readonly IImagesService _images;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly NadekoRandom rng = new NadekoRandom(); private readonly NadekoRandom rng = new NadekoRandom();
public FlipCoinCommands(IImagesService images, CurrencyHandler ch, BotConfig bc) public FlipCoinCommands(IImagesService images, CurrencyService cs, BotConfig bc)
{ {
_images = images; _images = images;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -74,47 +74,49 @@ namespace NadekoBot.Modules.Gambling
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] public enum BetFlipGuess
public async Task Betflip(int amount, string guess)
{ {
var guessStr = guess.Trim().ToUpperInvariant(); H = 1,
if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") Head = 1,
return; Heads = 1,
T = 2,
Tail = 2,
Tails = 2
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Betflip(int amount, BetFlipGuess guess)
{
if (amount < _bc.MinimumBetAmount) if (amount < _bc.MinimumBetAmount)
{ {
await ReplyErrorLocalized("min_bet_limit", _bc.MinimumBetAmount + _bc.CurrencySign).ConfigureAwait(false); await ReplyErrorLocalized("min_bet_limit", _bc.MinimumBetAmount + _bc.CurrencySign).ConfigureAwait(false);
return; return;
} }
var removed = await _ch.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false); var removed = await _cs.RemoveAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
await ReplyErrorLocalized("not_enough", _bc.CurrencyPluralName).ConfigureAwait(false); await ReplyErrorLocalized("not_enough", _bc.CurrencyPluralName).ConfigureAwait(false);
return; return;
} }
//heads = true BetFlipGuess result;
//tails = false
//todo this seems stinky, no time to look at it right now
var isHeads = guessStr == "HEADS" || guessStr == "H";
var result = false;
IEnumerable<byte> imageToSend; IEnumerable<byte> imageToSend;
if (rng.Next(0, 2) == 1) if (rng.Next(0, 2) == 1)
{ {
imageToSend = _images.Heads; imageToSend = _images.Heads;
result = true; result = BetFlipGuess.Heads;
} }
else else
{ {
imageToSend = _images.Tails; imageToSend = _images.Tails;
result = BetFlipGuess.Tails;
} }
string str; string str;
if (isHeads == result) if (guess == result)
{ {
var toWin = (int)Math.Round(amount * _bc.BetflipMultiplier); var toWin = (int)Math.Round(amount * _bc.BetflipMultiplier);
str = Context.User.Mention + " " + GetText("flip_guess", toWin + _bc.CurrencySign); str = Context.User.Mention + " " + GetText("flip_guess", toWin + _bc.CurrencySign);
await _ch.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false); await _cs.AddAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false);
} }
else else
{ {

View File

@ -20,8 +20,8 @@ namespace NadekoBot.Modules.Gambling
public class FlowerShop : NadekoSubmodule public class FlowerShop : NadekoSubmodule
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly DbHandler _db; private readonly DbService _db;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public enum Role public enum Role
@ -34,11 +34,11 @@ namespace NadekoBot.Modules.Gambling
List List
} }
public FlowerShop(BotConfig bc, DbHandler db, CurrencyHandler ch, DiscordShardedClient client) public FlowerShop(BotConfig bc, DbService db, CurrencyService cs, DiscordShardedClient client)
{ {
_db = db; _db = db;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
_client = client; _client = client;
} }
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
if (await _ch.RemoveCurrencyAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) if (await _cs.RemoveAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price))
{ {
try try
{ {
@ -120,11 +120,11 @@ namespace NadekoBot.Modules.Gambling
catch (Exception ex) catch (Exception ex)
{ {
_log.Warn(ex); _log.Warn(ex);
await _ch.AddCurrencyAsync(Context.User.Id, $"Shop error refund", entry.Price); await _cs.AddAsync(Context.User.Id, $"Shop error refund", entry.Price);
await ReplyErrorLocalized("shop_role_purchase_error").ConfigureAwait(false); await ReplyErrorLocalized("shop_role_purchase_error").ConfigureAwait(false);
return; return;
} }
await _ch.AddCurrencyAsync(entry.AuthorId, $"Shop sell item - {entry.Type}", GetProfitAmount(entry.Price)); await _cs.AddAsync(entry.AuthorId, $"Shop sell item - {entry.Type}", GetProfitAmount(entry.Price));
await ReplyConfirmLocalized("shop_role_purchase", Format.Bold(role.Name)).ConfigureAwait(false); await ReplyConfirmLocalized("shop_role_purchase", Format.Bold(role.Name)).ConfigureAwait(false);
return; return;
} }
@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Gambling
var item = entry.Items.ToArray()[new NadekoRandom().Next(0, entry.Items.Count)]; var item = entry.Items.ToArray()[new NadekoRandom().Next(0, entry.Items.Count)];
if (await _ch.RemoveCurrencyAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price)) if (await _cs.RemoveAsync(Context.User.Id, $"Shop purchase - {entry.Type}", entry.Price))
{ {
int removed; int removed;
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Gambling
.AddField(efb => efb.WithName(GetText("name")).WithValue(entry.Name).WithIsInline(true))) .AddField(efb => efb.WithName(GetText("name")).WithValue(entry.Name).WithIsInline(true)))
.ConfigureAwait(false); .ConfigureAwait(false);
await _ch.AddCurrencyAsync(entry.AuthorId, await _cs.AddAsync(entry.AuthorId,
$"Shop sell item - {entry.Name}", $"Shop sell item - {entry.Name}",
GetProfitAmount(entry.Price)).ConfigureAwait(false); GetProfitAmount(entry.Price)).ConfigureAwait(false);
} }
@ -174,7 +174,7 @@ namespace NadekoBot.Modules.Gambling
uow._context.Set<ShopEntryItem>().Add(item); uow._context.Set<ShopEntryItem>().Add(item);
uow.Complete(); uow.Complete();
await _ch.AddCurrencyAsync(Context.User.Id, await _cs.AddAsync(Context.User.Id,
$"Shop error refund - {entry.Name}", $"Shop error refund - {entry.Name}",
entry.Price, entry.Price,
uow).ConfigureAwait(false); uow).ConfigureAwait(false);

View File

@ -32,13 +32,13 @@ namespace NadekoBot.Modules.Gambling
//thanks to judge for helping me with this //thanks to judge for helping me with this
private readonly IImagesService _images; private readonly IImagesService _images;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
public Slots(IImagesService images, BotConfig bc, CurrencyHandler ch) public Slots(IImagesService images, BotConfig bc, CurrencyService cs)
{ {
_images = images; _images = images;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
} }
public class SlotMachine public class SlotMachine
@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
if (!await _ch.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false)) if (!await _cs.RemoveAsync(Context.User, "Slot Machine", amount, false))
{ {
await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false); await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false);
return; return;
@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Gambling
var msg = GetText("better_luck"); var msg = GetText("better_luck");
if (result.Multiplier != 0) if (result.Multiplier != 0)
{ {
await _ch.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false); await _cs.AddAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier); Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier);
if (result.Multiplier == 1) if (result.Multiplier == 1)
msg = GetText("slot_single", _bc.CurrencySign, 1); msg = GetText("slot_single", _bc.CurrencySign, 1);

View File

@ -57,10 +57,10 @@ namespace NadekoBot.Modules.Gambling
InsufficientAmount InsufficientAmount
} }
public WaifuClaimCommands(BotConfig bc, CurrencyHandler ch, DbHandler db) public WaifuClaimCommands(BotConfig bc, CurrencyService cs, DbService db)
{ {
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
_db = db; _db = db;
} }
@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var claimer = uow.DiscordUsers.GetOrCreate(Context.User); var claimer = uow.DiscordUsers.GetOrCreate(Context.User);
var waifu = uow.DiscordUsers.GetOrCreate(target); var waifu = uow.DiscordUsers.GetOrCreate(target);
if (!await _ch.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{ {
result = WaifuClaimResult.NotEnoughFunds; result = WaifuClaimResult.NotEnoughFunds;
} }
@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Gambling
} }
else if (isAffinity && amount > w.Price * 0.88f) else if (isAffinity && amount > w.Price * 0.88f)
{ {
if (!await _ch.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{ {
result = WaifuClaimResult.NotEnoughFunds; result = WaifuClaimResult.NotEnoughFunds;
} }
@ -138,7 +138,7 @@ namespace NadekoBot.Modules.Gambling
} }
else if (amount >= w.Price * 1.1f) // if no affinity else if (amount >= w.Price * 1.1f) // if no affinity
{ {
if (!await _ch.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) if (!await _cs.RemoveAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{ {
result = WaifuClaimResult.NotEnoughFunds; result = WaifuClaimResult.NotEnoughFunds;
} }
@ -230,13 +230,13 @@ namespace NadekoBot.Modules.Gambling
if (w.Affinity?.UserId == Context.User.Id) if (w.Affinity?.UserId == Context.User.Id)
{ {
await _ch.AddCurrencyAsync(w.Waifu.UserId, "Waifu Compensation", amount, uow).ConfigureAwait(false); await _cs.AddAsync(w.Waifu.UserId, "Waifu Compensation", amount, uow).ConfigureAwait(false);
w.Price = (int)Math.Floor(w.Price * 0.75f); w.Price = (int)Math.Floor(w.Price * 0.75f);
result = DivorceResult.SucessWithPenalty; result = DivorceResult.SucessWithPenalty;
} }
else else
{ {
await _ch.AddCurrencyAsync(Context.User.Id, "Waifu Refund", amount, uow).ConfigureAwait(false); await _cs.AddAsync(Context.User.Id, "Waifu Refund", amount, uow).ConfigureAwait(false);
result = DivorceResult.Success; result = DivorceResult.Success;
} }
@ -278,8 +278,8 @@ namespace NadekoBot.Modules.Gambling
private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30); private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30);
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly DbHandler _db; private readonly DbService _db;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]

View File

@ -13,14 +13,14 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling : NadekoTopLevelModule public partial class Gambling : NadekoTopLevelModule
{ {
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly DbHandler _db; private readonly DbService _db;
private readonly CurrencyHandler _currency; private readonly CurrencyService _currency;
private string CurrencyName => _bc.CurrencyName; private string CurrencyName => _bc.CurrencyName;
private string CurrencyPluralName => _bc.CurrencyPluralName; private string CurrencyPluralName => _bc.CurrencyPluralName;
private string CurrencySign => _bc.CurrencySign; private string CurrencySign => _bc.CurrencySign;
public Gambling(BotConfig bc, DbHandler db, CurrencyHandler currency) public Gambling(BotConfig bc, DbService db, CurrencyService currency)
{ {
_bc = bc; _bc = bc;
_db = db; _db = db;
@ -71,13 +71,13 @@ namespace NadekoBot.Modules.Gambling
{ {
if (amount <= 0 || Context.User.Id == receiver.Id) if (amount <= 0 || Context.User.Id == receiver.Id)
return; return;
var success = await _currency.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false); var success = await _currency.RemoveAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
if (!success) if (!success)
{ {
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
return; return;
} }
await _currency.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false); await _currency.AddAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString())) await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString()))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0) if (amount <= 0)
return; return;
await _currency.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); await _currency.AddAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
} }
@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Gambling
var users = (await Context.Guild.GetUsersAsync()) var users = (await Context.Guild.GetUsersAsync())
.Where(u => u.GetRoles().Contains(role)) .Where(u => u.GetRoles().Contains(role))
.ToList(); .ToList();
await Task.WhenAll(users.Select(u => _currency.AddCurrencyAsync(u.Id, await Task.WhenAll(users.Select(u => _currency.AddAsync(u.Id,
$"Awarded by bot owner to **{role.Name}** role. ({Context.User.Username}/{Context.User.Id})", $"Awarded by bot owner to **{role.Name}** role. ({Context.User.Username}/{Context.User.Id})",
amount))) amount)))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0) if (amount <= 0)
return; return;
if (await _currency.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false)) if (await _currency.RemoveAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false); await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false);
else else
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false); await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false);
@ -143,7 +143,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0) if (amount <= 0)
return; return;
if (await _currency.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false)) if (await _currency.RemoveAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
else else
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false); await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false);
@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Gambling
if (amount < 1) if (amount < 1)
return; return;
if (!await _currency.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false)) if (!await _currency.RemoveAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false))
{ {
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
return; return;
@ -227,19 +227,19 @@ namespace NadekoBot.Modules.Gambling
if (rnd < 91) if (rnd < 91)
{ {
str += GetText("br_win", (amount * _bc.Betroll67Multiplier) + CurrencySign, 66); str += GetText("br_win", (amount * _bc.Betroll67Multiplier) + CurrencySign, 66);
await _currency.AddCurrencyAsync(Context.User, "Betroll Gamble", await _currency.AddAsync(Context.User, "Betroll Gamble",
(int) (amount * _bc.Betroll67Multiplier), false).ConfigureAwait(false); (int) (amount * _bc.Betroll67Multiplier), false).ConfigureAwait(false);
} }
else if (rnd < 100) else if (rnd < 100)
{ {
str += GetText("br_win", (amount * _bc.Betroll91Multiplier) + CurrencySign, 90); str += GetText("br_win", (amount * _bc.Betroll91Multiplier) + CurrencySign, 90);
await _currency.AddCurrencyAsync(Context.User, "Betroll Gamble", await _currency.AddAsync(Context.User, "Betroll Gamble",
(int) (amount * _bc.Betroll91Multiplier), false).ConfigureAwait(false); (int) (amount * _bc.Betroll91Multiplier), false).ConfigureAwait(false);
} }
else else
{ {
str += GetText("br_win", (amount * _bc.Betroll100Multiplier) + CurrencySign, 100) + " 👑"; str += GetText("br_win", (amount * _bc.Betroll100Multiplier) + CurrencySign, 100) + " 👑";
await _currency.AddCurrencyAsync(Context.User, "Betroll Gamble", await _currency.AddAsync(Context.User, "Betroll Gamble",
(int) (amount * _bc.Betroll100Multiplier), false).ConfigureAwait(false); (int) (amount * _bc.Betroll100Multiplier), false).ConfigureAwait(false);
} }
} }

View File

@ -65,7 +65,7 @@ namespace NadekoBot.Modules.Games
Voting Voting
} }
//todo Isolate, this shouldn't print or anything like that. //todo 85 Isolate, this shouldn't print or anything like that.
public class AcrophobiaGame public class AcrophobiaGame
{ {
private readonly ITextChannel _channel; private readonly ITextChannel _channel;

View File

@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class CleverBotCommands : NadekoSubmodule public class CleverBotCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly GamesService _games; private readonly ChatterBotService _games;
public CleverBotCommands(DbHandler db, GamesService games) public CleverBotCommands(DbService db, ChatterBotService games)
{ {
_db = db; _db = db;
_games = games; _games = games;
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
if (_games.CleverbotGuilds.TryRemove(channel.Guild.Id, out Lazy<ChatterBotSession> throwaway)) if (_games.ChatterBotGuilds.TryRemove(channel.Guild.Id, out Lazy<ChatterBotSession> throwaway))
{ {
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Games
return; return;
} }
_games.CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true)); _games.ChatterBotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true));
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {

View File

@ -24,16 +24,16 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class PlantPickCommands : NadekoSubmodule public class PlantPickCommands : NadekoSubmodule
{ {
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly GamesService _games; private readonly GamesService _games;
private readonly DbHandler _db; private readonly DbService _db;
public PlantPickCommands(BotConfig bc, CurrencyHandler ch, GamesService games, public PlantPickCommands(BotConfig bc, CurrencyService cs, GamesService games,
DbHandler db) DbService db)
{ {
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
_games = games; _games = games;
_db = db; _db = db;
} }
@ -54,7 +54,7 @@ namespace NadekoBot.Modules.Games
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await _ch.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {_bc.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); await _cs.AddAsync((IGuildUser)Context.User, $"Picked {_bc.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
var msg = await ReplyConfirmLocalized("picked", msgs.Count + _bc.CurrencySign) var msg = await ReplyConfirmLocalized("picked", msgs.Count + _bc.CurrencySign)
.ConfigureAwait(false); .ConfigureAwait(false);
msg.DeleteAfter(10); msg.DeleteAfter(10);
@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Games
if (amount < 1) if (amount < 1)
return; return;
var removed = await _ch.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {_bc.CurrencyName}", amount, false).ConfigureAwait(false); var removed = await _cs.RemoveAsync((IGuildUser)Context.User, $"Planted a {_bc.CurrencyName}", amount, false).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false); await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false);
@ -88,9 +88,9 @@ namespace NadekoBot.Modules.Games
msgToSend += " " + GetText("pick_sn", Prefix); msgToSend += " " + GetText("pick_sn", Prefix);
IUserMessage msg; IUserMessage msg;
using (var toSend = imgData.Value.ToStream()) using (var toSend = imgData.Data.ToStream())
{ {
msg = await Context.Channel.SendFileAsync(toSend, imgData.Key, msgToSend).ConfigureAwait(false); msg = await Context.Channel.SendFileAsync(toSend, imgData.Name, msgToSend).ConfigureAwait(false);
} }
var msgs = new IUserMessage[amount]; var msgs = new IUserMessage[amount];

View File

@ -3,12 +3,8 @@ using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Services; using NadekoBot.Services.Games;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
{ {
@ -17,12 +13,13 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class PollCommands : NadekoSubmodule public class PollCommands : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly PollService _polls;
public PollCommands(DiscordShardedClient client) public PollCommands(DiscordShardedClient client, PollService polls)
{ {
_client = client; _client = client;
_polls = polls;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -42,8 +39,7 @@ namespace NadekoBot.Modules.Games
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task PollStats() public async Task PollStats()
{ {
Poll poll; if (!_polls.ActivePolls.TryGetValue(Context.Guild.Id, out var poll))
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
return; return;
await Context.Channel.EmbedAsync(poll.GetStats(GetText("current_poll_results"))); await Context.Channel.EmbedAsync(poll.GetStats(GetText("current_poll_results")));
@ -51,20 +47,7 @@ namespace NadekoBot.Modules.Games
private async Task InternalStartPoll(string arg, bool isPublic = false) private async Task InternalStartPoll(string arg, bool isPublic = false)
{ {
var channel = (ITextChannel)Context.Channel; if(await _polls.StartPoll((ITextChannel)Context.Channel, Context.Message, arg, isPublic) == false)
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return;
var data = arg.Split(';');
if (data.Length < 3)
return;
var poll = new Poll(_client, _strings, Context.Message, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
{
await poll.StartPoll().ConfigureAwait(false);
}
else
await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false); await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
} }
@ -75,151 +58,11 @@ namespace NadekoBot.Modules.Games
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
Poll poll; _polls.ActivePolls.TryRemove(channel.Guild.Id, out var poll);
ActivePolls.TryRemove(channel.Guild.Id, out poll);
await poll.StopPoll().ConfigureAwait(false); await poll.StopPoll().ConfigureAwait(false);
} }
} }
public class Poll
{
private readonly IUserMessage _originalMessage;
private readonly IGuild _guild;
private string[] answers { get; }
private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
private readonly string _question;
private readonly DiscordShardedClient _client;
private readonly NadekoStrings _strings;
public bool IsPublic { get; }
public Poll(DiscordShardedClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
_client = client;
_strings = strings;
_originalMessage = umsg;
_guild = ((ITextChannel)umsg.Channel).Guild;
_question = question;
answers = enumerable as string[] ?? enumerable.ToArray();
IsPublic = isPublic;
}
public EmbedBuilder GetStats(string title)
{
var results = _participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value)
.ToArray();
var eb = new EmbedBuilder().WithTitle(title);
var sb = new StringBuilder()
.AppendLine(Format.Bold(_question))
.AppendLine();
var totalVotesCast = 0;
if (results.Length == 0)
{
sb.AppendLine(GetText("no_votes_cast"));
}
else
{
for (int i = 0; i < results.Length; i++)
{
var result = results[i];
sb.AppendLine(GetText("poll_result",
result.Key,
Format.Bold(answers[result.Key - 1]),
Format.Bold(result.Value.ToString())));
totalVotesCast += result.Value;
}
}
eb.WithDescription(sb.ToString())
.WithFooter(efb => efb.WithText(GetText("x_votes_cast", totalVotesCast)));
return eb;
}
public async Task StartPoll()
{
_client.MessageReceived += Vote;
var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n";
var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!IsPublic)
msgToSend += "\n" + Format.Bold(GetText("poll_vote_private"));
else
msgToSend += "\n" + Format.Bold(GetText("poll_vote_public"));
await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
}
public async Task StopPoll()
{
_client.MessageReceived -= Vote;
await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
}
private async Task Vote(SocketMessage imsg)
{
try
{
// has to be a user message
var msg = imsg as SocketUserMessage;
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)
{
//if public, channel must be the same the poll started in
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.All(u => u.Id != imsg.Author.Id))
return;
}
//user can vote only once
if (_participants.TryAdd(msg.Author.Id, vote))
{
if (!IsPublic)
{
await ch.SendConfirmAsync(GetText("thanks_for_voting", Format.Bold(msg.Author.Username))).ConfigureAwait(false);
}
else
{
var toDelete = await ch.SendConfirmAsync(GetText("poll_voted", Format.Bold(msg.Author.ToString()))).ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
}
}
catch { }
}
private string GetText(string key, params object[] replacements)
=> _strings.GetText(key,
_guild.Id,
typeof(Games).Name.ToLowerInvariant(),
replacements);
}
} }
} }

View File

@ -6,7 +6,6 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Models; using NadekoBot.Modules.Games.Models;
using NadekoBot.Services.Games; using NadekoBot.Services.Games;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -71,15 +70,10 @@ namespace NadekoBot.Modules.Games
public async Task Typeadd([Remainder] string text) public async Task Typeadd([Remainder] string text)
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
if (string.IsNullOrWhiteSpace(text))
return;
_games.TypingArticles.Add(new TypingArticle _games.AddTypingArticle(Context.User, text);
{
Title = $"Text added on {DateTime.UtcNow} by {Context.User}",
Text = text.SanitizeMentions(),
});
//todo move this to service
File.WriteAllText(_games.TypingArticlesPath, JsonConvert.SerializeObject(_games.TypingArticles));
await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false); await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false);
} }

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
public IGuild Guild { get; } public IGuild Guild { get; }
public ITextChannel Channel { get; } public ITextChannel Channel { get; }
@ -44,14 +44,14 @@ namespace NadekoBot.Modules.Games.Trivia
public int WinRequirement { get; } public int WinRequirement { get; }
public TriviaGame(NadekoStrings strings, DiscordShardedClient client, BotConfig bc, public TriviaGame(NadekoStrings strings, DiscordShardedClient client, BotConfig bc,
CurrencyHandler ch, IGuild guild, ITextChannel channel, CurrencyService cs, IGuild guild, ITextChannel channel,
bool showHints, int winReq, bool isPokemon) bool showHints, int winReq, bool isPokemon)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_strings = strings; _strings = strings;
_client = client; _client = client;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
ShowHints = showHints; ShowHints = showHints;
Guild = guild; Guild = guild;
@ -227,7 +227,7 @@ namespace NadekoBot.Modules.Games.Trivia
} }
var reward = _bc.TriviaCurrencyReward; var reward = _bc.TriviaCurrencyReward;
if (reward > 0) if (reward > 0)
await _ch.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false); await _cs.AddAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
return; return;
} }

View File

@ -17,15 +17,15 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class TriviaCommands : NadekoSubmodule public class TriviaCommands : NadekoSubmodule
{ {
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly BotConfig _bc; private readonly BotConfig _bc;
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>(); public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
public TriviaCommands(DiscordShardedClient client, BotConfig bc, CurrencyHandler ch) public TriviaCommands(DiscordShardedClient client, BotConfig bc, CurrencyService cs)
{ {
_ch = ch; _cs = cs;
_client = client; _client = client;
_bc = bc; _bc = bc;
} }
@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Games
var showHints = !additionalArgs.Contains("nohint"); var showHints = !additionalArgs.Contains("nohint");
var isPokemon = additionalArgs.Contains("pokemon"); var isPokemon = additionalArgs.Contains("pokemon");
var trivia = new TriviaGame(_strings, _client, _bc, _ch, channel.Guild, channel, showHints, winReq, isPokemon); var trivia = new TriviaGame(_strings, _client, _bc, _cs, channel.Guild, channel, showHints, winReq, isPokemon);
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia)) if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{ {
try try

View File

@ -14,19 +14,21 @@ using System.Collections.Generic;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Threading; using System.Threading;
using NadekoBot.Services.Music; using NadekoBot.Services.Music;
using NadekoBot.DataStructures;
namespace NadekoBot.Modules.Music namespace NadekoBot.Modules.Music
{ {
[NoPublicBot]
public class Music : NadekoTopLevelModule public class Music : NadekoTopLevelModule
{ {
private static MusicService _music; private static MusicService _music;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
private readonly DbHandler _db; private readonly DbService _db;
public Music(DiscordShardedClient client, IBotCredentials creds, IGoogleApiService google, public Music(DiscordShardedClient client, IBotCredentials creds, IGoogleApiService google,
DbHandler db, MusicService music) DbService db, MusicService music)
{ {
_client = client; _client = client;
_creds = creds; _creds = creds;

View File

@ -23,14 +23,14 @@ namespace NadekoBot.Modules.Permissions
public class BlacklistCommands : NadekoSubmodule public class BlacklistCommands : NadekoSubmodule
{ {
private readonly BlacklistService _bs; private readonly BlacklistService _bs;
private readonly DbHandler _db; private readonly DbService _db;
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private ConcurrentHashSet<ulong> BlacklistedUsers => _bs.BlacklistedUsers; private ConcurrentHashSet<ulong> BlacklistedUsers => _bs.BlacklistedUsers;
private ConcurrentHashSet<ulong> BlacklistedGuilds => _bs.BlacklistedGuilds; private ConcurrentHashSet<ulong> BlacklistedGuilds => _bs.BlacklistedGuilds;
private ConcurrentHashSet<ulong> BlacklistedChannels => _bs.BlacklistedChannels; private ConcurrentHashSet<ulong> BlacklistedChannels => _bs.BlacklistedChannels;
public BlacklistCommands(BlacklistService bs, DbHandler db, IBotCredentials creds) public BlacklistCommands(BlacklistService bs, DbService db, IBotCredentials creds)
{ {
_bs = bs; _bs = bs;
_db = db; _db = db;

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions
[Group] [Group]
public class CmdCdsCommands : NadekoSubmodule public class CmdCdsCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly CmdCdService _service; private readonly CmdCdService _service;
private ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns private ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns
@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Permissions
private ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns private ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns
=> _service.ActiveCooldowns; => _service.ActiveCooldowns;
public CmdCdsCommands(CmdCdService service, DbHandler db) public CmdCdsCommands(CmdCdService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;
@ -88,40 +88,6 @@ namespace NadekoBot.Modules.Permissions
else else
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText("sec")), s => $"{s,-30}", 2).ConfigureAwait(false); await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText("sec")), s => $"{s,-30}", 2).ConfigureAwait(false);
} }
public bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user)
{
if (guild == null)
return false;
var cmdcds = CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null)
{
var activeCdsForGuild = ActiveCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Aliases.First().ToLowerInvariant()) != null)
{
return true;
}
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = cmd.Aliases.First().ToLowerInvariant(),
});
var _ = Task.Run(async () =>
{
try
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}
catch
{
// ignored
}
});
}
return false;
}
} }
} }
} }

View File

@ -15,10 +15,10 @@ namespace NadekoBot.Modules.Permissions
[Group] [Group]
public class FilterCommands : NadekoSubmodule public class FilterCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly FilterService _service; private readonly FilterService _service;
public FilterCommands(FilterService service, DbHandler db) public FilterCommands(FilterService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -16,9 +16,9 @@ namespace NadekoBot.Modules.Permissions
public class GlobalPermissionCommands : NadekoSubmodule public class GlobalPermissionCommands : NadekoSubmodule
{ {
private GlobalPermissionService _service; private GlobalPermissionService _service;
private readonly DbHandler _db; private readonly DbService _db;
public GlobalPermissionCommands(GlobalPermissionService service, DbHandler db) public GlobalPermissionCommands(GlobalPermissionService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -14,10 +14,10 @@ namespace NadekoBot.Modules.Permissions.Commands
public class ResetPermissionsCommands : NadekoSubmodule public class ResetPermissionsCommands : NadekoSubmodule
{ {
private readonly PermissionsService _service; private readonly PermissionsService _service;
private readonly DbHandler _db; private readonly DbService _db;
private readonly GlobalPermissionService _globalPerms; private readonly GlobalPermissionService _globalPerms;
public ResetPermissionsCommands(PermissionsService service, GlobalPermissionService globalPerms, DbHandler db) public ResetPermissionsCommands(PermissionsService service, GlobalPermissionService globalPerms, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Permissions.Commands
[RequireUserPermission(GuildPermission.Administrator)] [RequireUserPermission(GuildPermission.Administrator)]
public async Task ResetPermissions() public async Task ResetPermissions()
{ {
//todo 80 move to service //todo 50 move to service
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id); var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id);
@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Permissions.Commands
[OwnerOnly] [OwnerOnly]
public async Task ResetGlobalPermissions() public async Task ResetGlobalPermissions()
{ {
//todo 80 move to service //todo 50 move to service
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
var gc = uow.BotConfig.GetOrCreate(); var gc = uow.BotConfig.GetOrCreate();

View File

@ -15,10 +15,10 @@ namespace NadekoBot.Modules.Permissions
{ {
public partial class Permissions : NadekoTopLevelModule public partial class Permissions : NadekoTopLevelModule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly PermissionsService _service; private readonly PermissionsService _service;
public Permissions(PermissionsService service, DbHandler db) public Permissions(PermissionsService service, DbService db)
{ {
_db = db; _db = db;
_service = service; _service = service;

View File

@ -15,16 +15,16 @@ namespace NadekoBot.Modules.Pokemon
public class Pokemon : NadekoTopLevelModule public class Pokemon : NadekoTopLevelModule
{ {
private readonly PokemonService _service; private readonly PokemonService _service;
private readonly DbHandler _db; private readonly DbService _db;
private readonly BotConfig _bc; private readonly BotConfig _bc;
private readonly CurrencyHandler _ch; private readonly CurrencyService _cs;
public Pokemon(PokemonService pokemonService, DbHandler db, BotConfig bc, CurrencyHandler ch) public Pokemon(PokemonService pokemonService, DbService db, BotConfig bc, CurrencyService cs)
{ {
_service = pokemonService; _service = pokemonService;
_db = db; _db = db;
_bc = bc; _bc = bc;
_ch = ch; _cs = cs;
} }
private int GetDamage(PokemonType usertype, PokemonType targetType) private int GetDamage(PokemonType usertype, PokemonType targetType)
@ -229,7 +229,7 @@ namespace NadekoBot.Modules.Pokemon
var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention; var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
if (amount > 0) if (amount > 0)
{ {
if (!await _ch.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false)) if (!await _cs.RemoveAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
{ {
await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false); await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
return; return;
@ -295,7 +295,7 @@ namespace NadekoBot.Modules.Pokemon
var amount = 1; var amount = 1;
if (amount > 0) if (amount > 0)
{ {
if (!await _ch.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false)) if (!await _cs.RemoveAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
{ {
await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false); await ReplyErrorLocalized("no_currency", _bc.CurrencySign).ConfigureAwait(false);
return; return;

View File

@ -9,7 +9,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
//todo drawing //todo 50 drawing
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
public partial class Searches public partial class Searches

View File

@ -17,10 +17,10 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class StreamNotificationCommands : NadekoSubmodule public class StreamNotificationCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly StreamNotificationService _service; private readonly StreamNotificationService _service;
public StreamNotificationCommands(DbHandler db, StreamNotificationService service) public StreamNotificationCommands(DbService db, StreamNotificationService service)
{ {
_db = db; _db = db;
_service = service; _service = service;

View File

@ -59,7 +59,7 @@ namespace NadekoBot.Modules.Searches
return; return;
} }
if (_searches.TranslatedChannels.TryRemove(channel.Id, out var throwaway)) if (_searches.TranslatedChannels.TryRemove(channel.Id, out _))
{ {
await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false); await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false);
return; return;

View File

@ -20,10 +20,10 @@ namespace NadekoBot.Modules.Utility
public class CommandMapCommands : NadekoSubmodule public class CommandMapCommands : NadekoSubmodule
{ {
private readonly UtilityService _service; private readonly UtilityService _service;
private readonly DbHandler _db; private readonly DbService _db;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public CommandMapCommands(UtilityService service, DbHandler db, DiscordShardedClient client) public CommandMapCommands(UtilityService service, DbService db, DiscordShardedClient client)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -18,13 +18,13 @@ namespace NadekoBot.Modules.Utility
{ {
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly IStatsService _stats; private readonly IStatsService _stats;
private readonly CommandHandler _ch; private readonly CommandHandler _cmdHandler;
public InfoCommands(DiscordShardedClient client, IStatsService stats, CommandHandler ch) public InfoCommands(DiscordShardedClient client, IStatsService stats, CommandHandler ch)
{ {
_client = client; _client = client;
_stats = stats; _stats = stats;
_ch = ch; _cmdHandler = ch;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Utility
int startCount = page * activityPerPage; int startCount = page * activityPerPage;
StringBuilder str = new StringBuilder(); StringBuilder str = new StringBuilder();
foreach (var kvp in _ch.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page * activityPerPage).Take(activityPerPage)) foreach (var kvp in _cmdHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page * activityPerPage).Take(activityPerPage))
{ {
str.AppendLine(GetText("activity_line", str.AppendLine(GetText("activity_line",
++startCount, ++startCount,
@ -141,7 +141,7 @@ namespace NadekoBot.Modules.Utility
.WithTitle(GetText("activity_page", page + 1)) .WithTitle(GetText("activity_page", page + 1))
.WithOkColor() .WithOkColor()
.WithFooter(efb => efb.WithText(GetText("activity_users_total", .WithFooter(efb => efb.WithText(GetText("activity_users_total",
_ch.UserMessagesSent.Count))) _cmdHandler.UserMessagesSent.Count)))
.WithDescription(str.ToString())); .WithDescription(str.ToString()));
} }
} }

View File

@ -22,9 +22,9 @@ namespace NadekoBot.Modules.Utility
{ {
private readonly MessageRepeaterService _service; private readonly MessageRepeaterService _service;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly DbHandler _db; private readonly DbService _db;
public RepeatCommands(MessageRepeaterService service, DiscordShardedClient client, DbHandler db) public RepeatCommands(MessageRepeaterService service, DiscordShardedClient client, DbService db)
{ {
_service = service; _service = service;
_client = client; _client = client;

View File

@ -1,19 +1,12 @@
using System.Collections.Generic; using System.Threading.Tasks;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Modules.Utility.Models;
using Newtonsoft.Json;
using System.Threading;
using System; using System;
using System.Collections.Immutable;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Discord; using Discord;
using NLog; using NadekoBot.Services.Utility;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
@ -22,27 +15,26 @@ namespace NadekoBot.Modules.Utility
[Group] [Group]
public class PatreonCommands : NadekoSubmodule public class PatreonCommands : NadekoSubmodule
{ {
//todo rename patreon thingy and move it to be a service, or a part of utility service private readonly PatreonRewardsService _patreon;
private readonly PatreonThingy patreon;
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly BotConfig _config; private readonly BotConfig _config;
private readonly DbHandler _db; private readonly DbService _db;
private readonly CurrencyHandler _currency; private readonly CurrencyService _currency;
public PatreonCommands(IBotCredentials creds, BotConfig config, DbHandler db, CurrencyHandler currency) public PatreonCommands(PatreonRewardsService p, IBotCredentials creds, BotConfig config, DbService db, CurrencyService currency)
{ {
_creds = creds; _creds = creds;
_config = config; _config = config;
_db = db; _db = db;
_currency = currency; _currency = currency;
patreon = PatreonThingy.GetInstance(creds, db, currency); _patreon = p;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[OwnerOnly] [OwnerOnly]
public async Task PatreonRewardsReload() public async Task PatreonRewardsReload()
{ {
await patreon.LoadPledges().ConfigureAwait(false); await _patreon.LoadPledges().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false); await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
} }
@ -60,7 +52,7 @@ namespace NadekoBot.Modules.Utility
int amount = 0; int amount = 0;
try try
{ {
amount = await patreon.ClaimReward(Context.User.Id).ConfigureAwait(false); amount = await _patreon.ClaimReward(Context.User.Id).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -72,7 +64,7 @@ namespace NadekoBot.Modules.Utility
await ReplyConfirmLocalized("clpa_success", amount + _config.CurrencySign).ConfigureAwait(false); await ReplyConfirmLocalized("clpa_success", amount + _config.CurrencySign).ConfigureAwait(false);
return; return;
} }
var rem = (patreon.Interval - (DateTime.UtcNow - patreon.LastUpdate)); var rem = (_patreon.Interval - (DateTime.UtcNow - _patreon.LastUpdate));
var helpcmd = Format.Code(NadekoBot.Prefix + "donate"); var helpcmd = Format.Code(NadekoBot.Prefix + "donate");
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(GetText("clpa_fail")) .WithDescription(GetText("clpa_fail"))
@ -85,158 +77,5 @@ namespace NadekoBot.Modules.Utility
} }
} }
public class PatreonThingy
{
//todo quickly hacked while rewriting, fix this
private static PatreonThingy _instance = null;
public static PatreonThingy GetInstance(IBotCredentials creds, DbHandler db, CurrencyHandler cur)
=> _instance ?? (_instance = new PatreonThingy(creds, db, cur));
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);
public ImmutableArray<PatreonUserAndReward> Pledges { get; private set; }
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
public readonly Timer Updater;
private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
private readonly Logger _log;
public readonly TimeSpan Interval = TimeSpan.FromHours(1);
private IBotCredentials _creds;
private readonly DbHandler _db;
private readonly CurrencyHandler _currency;
static PatreonThingy() { }
private PatreonThingy(IBotCredentials creds, DbHandler db, CurrencyHandler currency)
{
_creds = creds;
_db = db;
_currency = currency;
if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
return;
_log = LogManager.GetCurrentClassLogger();
Updater = new Timer(async (_) => await LoadPledges(), null, TimeSpan.Zero, Interval);
}
public async Task LoadPledges()
{
LastUpdate = DateTime.UtcNow;
await getPledgesLocker.WaitAsync(1000).ConfigureAwait(false);
try
{
var rewards = new List<PatreonPledge>();
var users = new List<PatreonUser>();
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
var data = new PatreonData()
{
Links = new PatreonDataLinks()
{
next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges"
}
};
do
{
var res = await http.GetStringAsync(data.Links.next)
.ConfigureAwait(false);
data = JsonConvert.DeserializeObject<PatreonData>(res);
var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
.Where(x => x.attributes.declined_since == null));
users.AddRange(data.Included
.Where(x => x["type"].ToString() == "user")
.Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
} while (!string.IsNullOrWhiteSpace(data.Links.next));
}
Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
{
User = y,
Reward = x,
}).ToImmutableArray();
}
catch (Exception ex)
{
_log.Warn(ex);
}
finally
{
var _ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
getPledgesLocker.Release();
});
}
}
public async Task<int> ClaimReward(ulong userId)
{
await claimLockJustInCase.WaitAsync();
var now = DateTime.UtcNow;
try
{
var data = Pledges.FirstOrDefault(x => x.User.attributes?.social_connections?.discord?.user_id == userId.ToString());
if (data == null)
return 0;
var amount = data.Reward.attributes.amount_cents;
using (var uow = _db.UnitOfWork)
{
var users = uow._context.Set<RewardedUser>();
var usr = users.FirstOrDefault(x => x.PatreonUserId == data.User.id);
if (usr == null)
{
users.Add(new RewardedUser()
{
UserId = userId,
PatreonUserId = data.User.id,
LastReward = now,
AmountRewardedThisMonth = amount,
});
await _currency.AddCurrencyAsync(userId, "Patreon reward - new", amount, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return amount;
}
if (usr.LastReward.Month != now.Month)
{
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
usr.PatreonUserId = data.User.id;
await _currency.AddCurrencyAsync(userId, "Patreon reward - recurring", amount, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return amount;
}
if ( usr.AmountRewardedThisMonth < amount)
{
var toAward = amount - usr.AmountRewardedThisMonth;
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
usr.PatreonUserId = data.User.id;
await _currency.AddCurrencyAsync(usr.UserId, "Patreon reward - update", toAward, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return toAward;
}
}
return 0;
}
finally
{
claimLockJustInCase.Release();
}
}
}
} }
} }

View File

@ -17,9 +17,9 @@ namespace NadekoBot.Modules.Utility
[Group] [Group]
public class QuoteCommands : NadekoSubmodule public class QuoteCommands : NadekoSubmodule
{ {
private readonly DbHandler _db; private readonly DbService _db;
public QuoteCommands(DbHandler db) public QuoteCommands(DbService db)
{ {
_db = db; _db = db;
} }

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Utility
public class RemindCommands : NadekoSubmodule public class RemindCommands : NadekoSubmodule
{ {
private readonly RemindService _service; private readonly RemindService _service;
private readonly DbHandler _db; private readonly DbService _db;
public RemindCommands(RemindService service, DbHandler db) public RemindCommands(RemindService service, DbService db)
{ {
_service = service; _service = service;
_db = db; _db = db;

View File

@ -494,7 +494,6 @@ namespace NadekoBot.Services.Utility
} }
else if (s.Embeds.Any()) else if (s.Embeds.Any())
{ {
//todo probably just go through all properties and check if they are set, if they are, add them
msg += "EMBEDS: " + string.Join("\n--------\n", s.Embeds.Select(x => $"Description: {x.Description}")); msg += "EMBEDS: " + string.Join("\n--------\n", s.Embeds.Select(x => $"Description: {x.Description}"));
} }
} }

View File

@ -24,6 +24,7 @@ using NadekoBot.Services.Games;
using NadekoBot.Services.Administration; using NadekoBot.Services.Administration;
using NadekoBot.Services.Permissions; using NadekoBot.Services.Permissions;
using NadekoBot.Services.Utility; using NadekoBot.Services.Utility;
using NadekoBot.Services.Help;
namespace NadekoBot namespace NadekoBot
{ {
@ -45,7 +46,7 @@ namespace NadekoBot
public ImmutableArray<GuildConfig> AllGuildConfigs { get; } public ImmutableArray<GuildConfig> AllGuildConfigs { get; }
public BotConfig BotConfig { get; } public BotConfig BotConfig { get; }
public DbHandler Db { get; } public DbService Db { get; }
public CommandService CommandService { get; } public CommandService CommandService { get; }
public DiscordShardedClient Client { get; } public DiscordShardedClient Client { get; }
@ -60,7 +61,7 @@ namespace NadekoBot
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
Credentials = new BotCredentials(); Credentials = new BotCredentials();
Db = new DbHandler(Credentials); Db = new DbService(Credentials);
using (var uow = Db.UnitOfWork) using (var uow = Db.UnitOfWork)
{ {
@ -99,7 +100,7 @@ namespace NadekoBot
var commandHandler = new CommandHandler(Client, CommandService, Credentials, this); var commandHandler = new CommandHandler(Client, CommandService, Credentials, this);
var stats = new StatsService(Client, commandHandler, Credentials); var stats = new StatsService(Client, commandHandler, Credentials);
var images = new ImagesService(); var images = new ImagesService();
var currencyHandler = new CurrencyHandler(BotConfig, Db); var currencyHandler = new CurrencyService(BotConfig, Db);
//module services //module services
//todo 90 - autodiscover, DI, and add instead of manual like this //todo 90 - autodiscover, DI, and add instead of manual like this
@ -109,27 +110,36 @@ namespace NadekoBot
var repeaterService = new MessageRepeaterService(Client, AllGuildConfigs); var repeaterService = new MessageRepeaterService(Client, AllGuildConfigs);
var converterService = new ConverterService(Db); var converterService = new ConverterService(Db);
#endregion #endregion
#region Searches #region Searches
var searchesService = new SearchesService(Client, googleApiService, Db); var searchesService = new SearchesService(Client, googleApiService, Db);
var streamNotificationService = new StreamNotificationService(Db, Client, strings); var streamNotificationService = new StreamNotificationService(Db, Client, strings);
#endregion #endregion
var clashService = new ClashOfClansService(Client, Db, localization, strings); var clashService = new ClashOfClansService(Client, Db, localization, strings);
var musicService = new MusicService(googleApiService, strings, localization, Db, soundcloudApiService, Credentials); var musicService = new MusicService(googleApiService, strings, localization, Db, soundcloudApiService, Credentials, AllGuildConfigs);
var crService = new CustomReactionsService(Db, Client); var crService = new CustomReactionsService(Db, Client);
var helpService = new HelpService(BotConfig);
#region Games
var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, strings, images); var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, strings, images);
var chatterBotService = new ChatterBotService(Client, AllGuildConfigs);
var pollService = new PollService(Client, strings);
#endregion
#region administration #region administration
var administrationService = new AdministrationService(AllGuildConfigs, commandHandler); var administrationService = new AdministrationService(AllGuildConfigs, commandHandler);
var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db); var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, Db);
var selfService = new SelfService(this, commandHandler, Db, BotConfig); var selfService = new SelfService(Client, this, commandHandler, Db, BotConfig, localization, strings, Credentials);
var vcRoleService = new VcRoleService(Client, AllGuildConfigs); var vcRoleService = new VcRoleService(Client, AllGuildConfigs, Db);
var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, Db); var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, Db);
var muteService = new MuteService(Client, AllGuildConfigs, Db); var muteService = new MuteService(Client, AllGuildConfigs, Db);
var ratelimitService = new RatelimitService(Client, AllGuildConfigs); var ratelimitService = new SlowmodeService(AllGuildConfigs);
var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService); var protectionService = new ProtectionService(Client, AllGuildConfigs, muteService);
var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService); var playingRotateService = new PlayingRotateService(Client, BotConfig, musicService);
var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs); var gameVcService = new GameVoiceChannelService(Client, Db, AllGuildConfigs);
var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs); var autoAssignRoleService = new AutoAssignRoleService(Client, AllGuildConfigs);
var permissionsService = new PermissionsService(Db); var permissionsService = new PermissionsService(Db, BotConfig);
var blacklistService = new BlacklistService(BotConfig); var blacklistService = new BlacklistService(BotConfig);
var cmdcdsService = new CmdCdService(AllGuildConfigs); var cmdcdsService = new CmdCdService(AllGuildConfigs);
var filterService = new FilterService(AllGuildConfigs); var filterService = new FilterService(AllGuildConfigs);
@ -148,9 +158,9 @@ namespace NadekoBot
.Add<NadekoStrings>(strings) .Add<NadekoStrings>(strings)
.Add<DiscordShardedClient>(Client) .Add<DiscordShardedClient>(Client)
.Add<BotConfig>(BotConfig) .Add<BotConfig>(BotConfig)
.Add<CurrencyHandler>(currencyHandler) .Add<CurrencyService>(currencyHandler)
.Add<CommandHandler>(commandHandler) .Add<CommandHandler>(commandHandler)
.Add<DbHandler>(Db) .Add<DbService>(Db)
//modules //modules
.Add<UtilityService>(utilityService) .Add<UtilityService>(utilityService)
.Add(remindService) .Add(remindService)
@ -162,7 +172,10 @@ namespace NadekoBot
.Add<MusicService>(musicService) .Add<MusicService>(musicService)
.Add<GreetSettingsService>(greetSettingsService) .Add<GreetSettingsService>(greetSettingsService)
.Add<CustomReactionsService>(crService) .Add<CustomReactionsService>(crService)
.Add<HelpService>(helpService)
.Add<GamesService>(gamesService) .Add<GamesService>(gamesService)
.Add(chatterBotService)
.Add(pollService)
.Add<AdministrationService>(administrationService) .Add<AdministrationService>(administrationService)
.Add(selfService) .Add(selfService)
.Add(vcRoleService) .Add(vcRoleService)
@ -228,9 +241,13 @@ namespace NadekoBot
await commandHandler.StartHandling().ConfigureAwait(false); await commandHandler.StartHandling().ConfigureAwait(false);
var _ = await CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly); var _ = await CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly);
#if !GLOBAL_NADEKO #if GLOBAL_NADEKO
//todo uncomment this //unload modules which are not available on the public bot
//await CommandService.AddModuleAsync<Music>().ConfigureAwait(false); CommandService
.Modules
.ToArray()
.Where(x => x.Preconditions.Any(y => y.GetType() == typeof(NoPublicBot)))
.ForEach(x => CommandService.RemoveModuleAsync(x));
#endif #endif
Ready = true; Ready = true;
_log.Info(await stats.Print().ConfigureAwait(false)); _log.Info(await stats.Print().ConfigureAwait(false));

View File

@ -29,11 +29,8 @@
<ItemGroup> <ItemGroup>
<Compile Remove="data\**\*;credentials.json;credentials_example.json" /> <Compile Remove="data\**\*;credentials.json;credentials_example.json" />
<Compile Remove="Modules\Administration\**" /> <Compile Remove="Modules\Administration\**" />
<Compile Remove="Modules\NSFW\**" />
<EmbeddedResource Remove="Modules\Administration\**" /> <EmbeddedResource Remove="Modules\Administration\**" />
<EmbeddedResource Remove="Modules\NSFW\**" />
<None Remove="Modules\Administration\**" /> <None Remove="Modules\Administration\**" />
<None Remove="Modules\NSFW\**" />
<Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" /> <Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
<Compile Include="Modules\Administration\Administration.cs" /> <Compile Include="Modules\Administration\Administration.cs" />
<Compile Include="Modules\Administration\Commands\AutoAssignRoleCommands.cs" /> <Compile Include="Modules\Administration\Commands\AutoAssignRoleCommands.cs" />
@ -87,6 +84,7 @@
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" /> <PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="NLog" Version="5.0.0-beta03" /> <PackageReference Include="NLog" Version="5.0.0-beta03" />
<PackageReference Include="System.ValueTuple" Version="4.4.0-preview1-25305-02" />
<PackageReference Include="System.Xml.XPath" Version="4.3.0" /> <PackageReference Include="System.Xml.XPath" Version="4.3.0" />
</ItemGroup> </ItemGroup>
@ -102,5 +100,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="Modules\Music\Classes\" /> <Folder Include="Modules\Music\Classes\" />
<Folder Include="Modules\Utility\Models\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -15,10 +15,10 @@ namespace NadekoBot.Services.Administration
public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>(); public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>();
private readonly Logger _log; private readonly Logger _log;
private readonly DbHandler _db; private readonly DbService _db;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public GameVoiceChannelService(DiscordShardedClient client, DbHandler db, IEnumerable<GuildConfig> gcs) public GameVoiceChannelService(DiscordShardedClient client, DbService db, IEnumerable<GuildConfig> gcs)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;

View File

@ -26,9 +26,9 @@ namespace NadekoBot.Services.Administration
private readonly Logger _log = LogManager.GetCurrentClassLogger(); private readonly Logger _log = LogManager.GetCurrentClassLogger();
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly DbHandler _db; private readonly DbService _db;
public MuteService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbHandler db) public MuteService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbService db)
{ {
_client = client; _client = client;
_db = db; _db = db;

View File

@ -1,4 +1,5 @@
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
@ -6,22 +7,22 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Discord;
using System.Threading.Tasks;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class RatelimitService public class SlowmodeService : IEarlyBlocker
{ {
public ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>(); public ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(); public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>();
public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(); public ConcurrentDictionary<ulong, HashSet<ulong>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
private readonly Logger _log; private readonly Logger _log;
private readonly DiscordShardedClient _client;
public RatelimitService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs) public SlowmodeService(IEnumerable<GuildConfig> gcs)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_client = client;
IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>( IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
gcs.ToDictionary(x => x.GuildId, gcs.ToDictionary(x => x.GuildId,
@ -30,24 +31,33 @@ namespace NadekoBot.Services.Administration
IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>( IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
gcs.ToDictionary(x => x.GuildId, gcs.ToDictionary(x => x.GuildId,
x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId)))); x => new HashSet<ulong>(x.SlowmodeIgnoredUsers.Select(y => y.UserId))));
}
_client.MessageReceived += async (umsg) => public async Task<bool> TryBlockEarly(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
{
if (guild == null)
return false;
try
{ {
try var channel = usrMsg?.Channel as SocketTextChannel;
if (channel == null || usrMsg == null || usrMsg.IsAuthor(client))
return false;
if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
return false;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
{ {
var usrMsg = umsg as SocketUserMessage; await usrMsg.DeleteAsync();
var channel = usrMsg?.Channel as SocketTextChannel; return true;
if (channel == null || usrMsg == null || usrMsg.IsAuthor(client))
return;
if (!RatelimitingChannels.TryGetValue(channel.Id, out Ratelimiter limiter))
return;
if (limiter.CheckUserRatelimit(usrMsg.Author.Id, channel.Guild.Id, usrMsg.Author as SocketGuildUser))
await usrMsg.DeleteAsync();
} }
catch (Exception ex) { _log.Warn(ex); } }
}; catch (Exception ex)
{
_log.Warn(ex);
}
return false;
} }
} }
} }

View File

@ -10,7 +10,7 @@ namespace NadekoBot.Services.Administration
{ {
public class Ratelimiter public class Ratelimiter
{ {
private readonly RatelimitService _svc; private readonly SlowmodeService _svc;
public class RatelimitedUser public class RatelimitedUser
{ {
@ -22,7 +22,7 @@ namespace NadekoBot.Services.Administration
public int MaxMessages { get; set; } public int MaxMessages { get; set; }
public int PerSeconds { get; set; } public int PerSeconds { get; set; }
public Ratelimiter(RatelimitService svc) public Ratelimiter(SlowmodeService svc)
{ {
_svc = svc; _svc = svc;
} }

View File

@ -1,23 +1,43 @@
using NadekoBot.Services.Database.Models; using Discord;
using NadekoBot.DataStructures;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models;
using NLog;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.WebSocket;
using System.Collections.Generic;
namespace NadekoBot.Services.Administration namespace NadekoBot.Services.Administration
{ {
public class SelfService public class SelfService : ILateExecutor
{ {
public volatile bool ForwardDMs; public volatile bool ForwardDMs;
public volatile bool ForwardDMsToAllOwners; public volatile bool ForwardDMsToAllOwners;
private readonly NadekoBot _bot; private readonly NadekoBot _bot;
private readonly CommandHandler _cmdHandler; private readonly CommandHandler _cmdHandler;
private readonly DbHandler _db; private readonly DbService _db;
private readonly Logger _log;
private readonly ILocalization _localization;
private readonly NadekoStrings _strings;
private readonly DiscordShardedClient _client;
private readonly IBotCredentials _creds;
private ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels = new ImmutableArray<AsyncLazy<IDMChannel>>();
public SelfService(NadekoBot bot, CommandHandler cmdHandler, DbHandler db, public SelfService(DiscordShardedClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
BotConfig bc) BotConfig bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds)
{ {
_bot = bot; _bot = bot;
_cmdHandler = cmdHandler; _cmdHandler = cmdHandler;
_db = db; _db = db;
_log = LogManager.GetCurrentClassLogger();
_localization = localization;
_strings = strings;
_client = client;
_creds = creds;
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
@ -37,6 +57,112 @@ namespace NadekoBot.Services.Administration
await Task.Delay(400).ConfigureAwait(false); await Task.Delay(400).ConfigureAwait(false);
} }
}); });
var ___ = Task.Run(async () =>
{
while (!bot.Ready)
await Task.Delay(1000);
await Task.Delay(5000);
_client.Guilds.SelectMany(g => g.Users);
LoadOwnerChannels();
if (!ownerChannels.Any())
_log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
else
_log.Info($"Created {ownerChannels.Length} out of {_creds.OwnerIds.Length} owner message channels.");
});
}
private void LoadOwnerChannels()
{
var hs = new HashSet<ulong>(_creds.OwnerIds);
var channels = new Dictionary<ulong, AsyncLazy<IDMChannel>>();
foreach (var s in _client.Shards)
{
if (hs.Count == 0)
break;
foreach (var g in s.Guilds)
{
if (hs.Count == 0)
break;
foreach (var u in g.Users)
{
if (hs.Remove(u.Id))
{
channels.Add(u.Id, new AsyncLazy<IDMChannel>(async () => await u.CreateDMChannelAsync()));
if (hs.Count == 0)
break;
}
}
}
}
ownerChannels = channels.OrderBy(x => _creds.OwnerIds.IndexOf(x.Key))
.Select(x => x.Value)
.ToImmutableArray();
}
// forwards dms
public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg)
{
if (msg.Channel is IDMChannel && ForwardDMs && ownerChannels.Length > 0)
{
var title = _strings.GetText("dm_from",
_localization.DefaultCultureInfo,
"Administration".ToLowerInvariant()) +
$" [{msg.Author}]({msg.Author.Id})";
var attachamentsTxt = _strings.GetText("attachments",
_localization.DefaultCultureInfo,
"Administration".ToLowerInvariant());
var toSend = msg.Content;
if (msg.Attachments.Count > 0)
{
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" +
string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
}
if (ForwardDMsToAllOwners)
{
var allOwnerChannels = await Task.WhenAll(ownerChannels
.Select(x => x.Value))
.ConfigureAwait(false);
foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id))
{
try
{
await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false);
}
catch
{
_log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id);
}
}
}
else
{
var firstOwnerChannel = await ownerChannels[0];
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
{
try
{
await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false);
}
catch
{
// ignored
}
}
}
}
} }
} }
} }

View File

@ -13,20 +13,25 @@ namespace NadekoBot.Services.Administration
public class VcRoleService public class VcRoleService
{ {
private readonly Logger _log; private readonly Logger _log;
private readonly DbService _db;
private readonly DiscordShardedClient _client;
public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; } public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; }
public VcRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs) public VcRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbService db)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db;
_client = client;
client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated; _client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>(); VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
var missingRoles = new List<VcRoleInfo>();
foreach (var gconf in gcs) foreach (var gconf in gcs)
{ {
var g = client.GetGuild(gconf.GuildId); var g = _client.GetGuild(gconf.GuildId);
if (g == null) if (g == null)
continue; //todo delete everything from db if guild doesn't exist? continue;
var infos = new ConcurrentDictionary<ulong, IRole>(); var infos = new ConcurrentDictionary<ulong, IRole>();
VcRoles.TryAdd(gconf.GuildId, infos); VcRoles.TryAdd(gconf.GuildId, infos);
@ -34,11 +39,21 @@ namespace NadekoBot.Services.Administration
{ {
var role = g.GetRole(ri.RoleId); var role = g.GetRole(ri.RoleId);
if (role == null) if (role == null)
continue; //todo remove this entry from db {
missingRoles.Add(ri);
continue;
}
infos.TryAdd(ri.VoiceChannelId, role); infos.TryAdd(ri.VoiceChannelId, role);
} }
} }
if(missingRoles.Any())
using (var uow = _db.UnitOfWork)
{
_log.Warn($"Removing {missingRoles.Count} missing roles from {nameof(VcRoleService)}");
uow._context.RemoveRange(missingRoles);
uow.Complete();
}
} }
private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState,

View File

@ -22,11 +22,11 @@ namespace NadekoBot.Services.Administration
private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>(); private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly DbHandler _db; private readonly DbService _db;
private readonly Logger _log; private readonly Logger _log;
public VplusTService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings, public VplusTService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings,
DbHandler db) DbService db)
{ {
_client = client; _client = client;
_strings = strings; _strings = strings;

View File

@ -18,14 +18,14 @@ namespace NadekoBot.Services.ClashOfClans
public class ClashOfClansService public class ClashOfClansService
{ {
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly DbHandler _db; private readonly DbService _db;
private readonly ILocalization _localization; private readonly ILocalization _localization;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly Timer checkWarTimer; private readonly Timer checkWarTimer;
public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
public ClashOfClansService(DiscordShardedClient client, DbHandler db, ILocalization localization, NadekoStrings strings) public ClashOfClansService(DiscordShardedClient client, DbService db, ILocalization localization, NadekoStrings strings)
{ {
_client = client; _client = client;
_db = db; _db = db;

View File

@ -87,20 +87,6 @@ namespace NadekoBot.Services
public Task StartHandling() public Task StartHandling()
{ {
var _ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
_client.Guilds.SelectMany(g => g.Users);
LoadOwnerChannels();
if (!ownerChannels.Any())
_log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
else
_log.Info($"Created {ownerChannels.Length} out of {_creds.OwnerIds.Length} owner message channels.");
});
_client.MessageReceived += MessageReceivedHandler; _client.MessageReceived += MessageReceivedHandler;
_client.MessageUpdated += (oldmsg, newMsg, channel) => _client.MessageUpdated += (oldmsg, newMsg, channel) =>
{ {
@ -126,80 +112,6 @@ namespace NadekoBot.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
private void LoadOwnerChannels()
{
var hs = new HashSet<ulong>(_creds.OwnerIds);
var channels = new Dictionary<ulong, AsyncLazy<IDMChannel>>();
foreach (var s in _client.Shards)
{
if(hs.Count == 0)
break;
foreach (var g in s.Guilds)
{
if(hs.Count == 0)
break;
foreach (var u in g.Users)
{
if(hs.Remove(u.Id))
{
channels.Add(u.Id, new AsyncLazy<IDMChannel>(async () => await u.CreateDMChannelAsync()));
if(hs.Count == 0)
break;
}
}
}
}
ownerChannels = channels.OrderBy(x => _creds.OwnerIds.IndexOf(x.Key))
.Select(x => x.Value)
.ToImmutableArray();
}
////todo cleverbot
//private async Task<bool> TryRunCleverbot(IUserMessage usrMsg, SocketGuild guild)
//{
// if (guild == null)
// return false;
// try
// {
// var message = Games.CleverBotCommands.PrepareMessage(usrMsg, out Games.ChatterBotSession cbs);
// if (message == null || cbs == null)
// return false;
// PermissionCache pc = Permissions.GetCache(guild.Id);
// if (!pc.Permissions.CheckPermissions(usrMsg,
// NadekoBot.Prefix + "cleverbot",
// typeof(Games).Name,
// out int index))
// {
// //todo print in guild actually
// var returnMsg =
// $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action.";
// _log.Info(returnMsg);
// return true;
// }
// var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(cbs, (ITextChannel)usrMsg.Channel, message).ConfigureAwait(false);
// if (cleverbotExecuted)
// {
// _log.Info($@"CleverBot Executed
//Server: {guild.Name} [{guild.Id}]
//Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}]
//UserId: {usrMsg.Author} [{usrMsg.Author.Id}]
//Message: {usrMsg.Content}");
// return true;
// }
// }
// catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
// return false;
//}
////todo blacklisting
//private bool IsBlacklisted(IGuild guild, IUserMessage usrMsg) =>
// (guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) ||
// BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
// BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id);
private const float _oneThousandth = 1.0f / 1000; private const float _oneThousandth = 1.0f / 1000;
private Task LogSuccessfulExecution(IUserMessage usrMsg, bool exec, ITextChannel channel, params int[] execPoints) private Task LogSuccessfulExecution(IUserMessage usrMsg, bool exec, ITextChannel channel, params int[] execPoints)
@ -233,166 +145,66 @@ namespace NadekoBot.Services
//exec.Result.ErrorReason // {4} //exec.Result.ErrorReason // {4}
); );
} }
////todo invite filtering
//private async Task<bool> InviteFiltered(IGuild guild, IUserMessage usrMsg)
//{
// if ((Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) ||
// Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id)) &&
// usrMsg.Content.IsDiscordInvite())
// {
// try
// {
// await usrMsg.DeleteAsync().ConfigureAwait(false);
// return true;
// }
// catch (HttpException ex)
// {
// _log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
// return true;
// }
// }
// return false;
//}
////todo word filtering private async Task MessageReceivedHandler(SocketMessage msg)
//private async Task<bool> WordFiltered(IGuild guild, IUserMessage usrMsg)
//{
// var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
// var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
// var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
// if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
// {
// foreach (var word in wordsInMessage)
// {
// if (filteredChannelWords.Contains(word) ||
// filteredServerWords.Contains(word))
// {
// try
// {
// await usrMsg.DeleteAsync().ConfigureAwait(false);
// }
// catch (HttpException ex)
// {
// _log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex);
// }
// return true;
// }
// }
// }
// return false;
//}
private Task MessageReceivedHandler(SocketMessage msg)
{ {
var _ = Task.Run(async () => await Task.Yield();
try
{ {
try if (msg.Author.IsBot || !_bot.Ready) //no bots, wait until bot connected and initialized
{ return;
if (msg.Author.IsBot || !_bot.Ready) //no bots, wait until bot connected and initialized
return;
var usrMsg = msg as SocketUserMessage; if (!(msg is SocketUserMessage usrMsg))
if (usrMsg == null) //has to be an user message, not system/other messages. return;
return;
if (usrMsg.Author.Id == 193022505026453504)
return;
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
// track how many messagges each user is sending // track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
#endif #endif
var channel = msg.Channel as SocketTextChannel; var channel = msg.Channel as ISocketMessageChannel;
var guild = channel?.Guild; var guild = (msg.Channel as SocketTextChannel)?.Guild;
await TryRunCommand(guild, channel, usrMsg); await TryRunCommand(guild, channel, usrMsg);
} }
catch (Exception ex) catch (Exception ex)
{
_log.Warn("Error in CommandHandler");
_log.Warn(ex);
if (ex.InnerException != null)
{ {
_log.Warn("Error in CommandHandler"); _log.Warn("Inner Exception of the error in CommandHandler");
_log.Warn(ex); _log.Warn(ex.InnerException);
if (ex.InnerException != null)
{
_log.Warn("Inner Exception of the error in CommandHandler");
_log.Warn(ex.InnerException);
}
} }
}); }
return Task.CompletedTask;
} }
public async Task TryRunCommand(SocketGuild guild, ITextChannel channel, IUserMessage usrMsg) public async Task TryRunCommand(SocketGuild guild, ISocketMessageChannel channel, IUserMessage usrMsg)
{ {
var execTime = Environment.TickCount; var execTime = Environment.TickCount;
foreach (var svc in _services) foreach (var svc in _services)
{ {
if (svc is IBlockingExecutor _executor && if (svc is IEarlyBlocker blocker &&
await _executor.TryExecute(_client, guild, usrMsg)) await blocker.TryBlockEarly(_client, guild, usrMsg).ConfigureAwait(false))
{
_log.Info("Blocked User: [{0}] Message: [{1}] Service: [{2}]", usrMsg.Author, usrMsg.Content, svc.GetType().Name);
return;
}
}
var exec2 = Environment.TickCount - execTime;
foreach (var svc in _services)
{
if (svc is IEarlyBlockingExecutor exec &&
await exec.TryExecuteEarly(_client, guild, usrMsg).ConfigureAwait(false))
{ {
_log.Info("User [{0}] executed [{1}] in [{2}]", usrMsg.Author, usrMsg.Content, svc.GetType().Name); _log.Info("User [{0}] executed [{1}] in [{2}]", usrMsg.Author, usrMsg.Content, svc.GetType().Name);
return; return;
} }
} }
////todo word and invite filtering
//if (guild != null && guild.OwnerId != usrMsg.Author.Id)
//{
// if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false))
// return;
// if (await WordFiltered(guild, usrMsg).ConfigureAwait(false))
// return;
//}
////todo blacklisting
//if (IsBlacklisted(guild, usrMsg))
// return;
//var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false);
//if (cleverBotRan)
// return;
var exec2 = Environment.TickCount - execTime;
////todo custom reactions
//// maybe this message is a custom reaction
//// todo log custom reaction executions. return struct with info
//var cr = await Task.Run(() => CustomReactions.TryGetCustomReaction(usrMsg)).ConfigureAwait(false);
//if (cr != null) //if it was, don't execute the command
//{
// try
// {
// if (guild != null)
// {
// PermissionCache pc = Permissions.GetCache(guild.Id);
// if (!pc.Permissions.CheckPermissions(usrMsg, cr.Trigger, "ActualCustomReactions",
// out int index))
// {
// //todo print in guild actually
// var returnMsg =
// $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action.";
// _log.Info(returnMsg);
// return;
// }
// }
// await cr.Send(usrMsg).ConfigureAwait(false);
// if (cr.AutoDeleteTrigger)
// {
// try { await usrMsg.DeleteAsync().ConfigureAwait(false); } catch { }
// }
// }
// catch (Exception ex)
// {
// _log.Warn("Sending CREmbed failed");
// _log.Warn(ex);
// }
// return;
//}
var exec3 = Environment.TickCount - execTime; var exec3 = Environment.TickCount - execTime;
string messageContent = usrMsg.Content; string messageContent = usrMsg.Content;
@ -436,13 +248,13 @@ namespace NadekoBot.Services
var exec = await Task.Run(() => ExecuteCommandAsync(new CommandContext(_client, usrMsg), NadekoBot.Prefix.Length, _services, MultiMatchHandling.Best)).ConfigureAwait(false); var exec = await Task.Run(() => ExecuteCommandAsync(new CommandContext(_client, usrMsg), NadekoBot.Prefix.Length, _services, MultiMatchHandling.Best)).ConfigureAwait(false);
execTime = Environment.TickCount - execTime; execTime = Environment.TickCount - execTime;
////todo permissions ////todo commandHandler
//if (exec.Result.IsSuccess) if (exec)
//{ {
// await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); // await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false);
// await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false); await LogSuccessfulExecution(usrMsg, exec, channel as ITextChannel, exec2, exec3, execTime).ConfigureAwait(false);
// return; return;
//} }
//else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) //else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand)
//{ //{
// LogErroredExecution(usrMsg, exec, channel, exec2, exec3, execTime); // LogErroredExecution(usrMsg, exec, channel, exec2, exec3, execTime);
@ -453,20 +265,20 @@ namespace NadekoBot.Services
// } // }
// return; // return;
//} //}
if (exec)
return;
} }
if (usrMsg.Channel is IPrivateChannel) foreach (var svc in _services)
{ {
// rofl, gotta do this to prevent dm help message being sent to if (svc is ILateExecutor exec)
// users who are voting on private polls (sending a number in a DM) {
if (int.TryParse(usrMsg.Content, out int vote)) return; await exec.LateExecute(_client, guild, usrMsg).ConfigureAwait(false);
}
////todo help
//await usrMsg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
////todo selfcommands
//await SelfCommands.HandleDmForwarding(usrMsg, ownerChannels).ConfigureAwait(false);
} }
} }
public Task<bool> ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public Task<bool> ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
@ -540,16 +352,16 @@ namespace NadekoBot.Services
// } // }
//} //}
////////future //////future
//////int price; ////int price;
//////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0) ////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0)
//////{ ////{
////// var success = await _ch.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false); //// var success = await _cs.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false);
////// if (!success) //// if (!success)
////// { //// {
////// return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command.")); //// return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command."));
////// } //// }
//////} ////}
} }
////todo perms ////todo perms
@ -564,12 +376,19 @@ namespace NadekoBot.Services
// GlobalCommandsCooldown constant (miliseconds) // GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) if (!UsersOnShortCooldown.Add(context.Message.Author.Id))
return false; return false;
//return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown."); //return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.");
var commandName = cmd.Aliases.First();
foreach (var svc in _services)
{
if (svc is ILateBlocker exec &&
await exec.TryBlockLate(_client, context.Message, context.Guild, context.Channel, context.User, cmd.Module.GetTopLevelModule().Name, commandName).ConfigureAwait(false))
{
_log.Info("Late blocking User [{0}] Command: [{1}] in [{2}]", context.User, commandName, svc.GetType().Name);
return false;
}
}
////todo cmdcds
//if (CmdCdsCommands.HasCooldown(cmd, context.Guild, context.User))
// return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, "That command is on a cooldown for you."));
await commands[i].ExecuteAsync(context, parseResult, serviceProvider); await commands[i].ExecuteAsync(context, parseResult, serviceProvider);
return true; return true;
} }

View File

@ -7,20 +7,20 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class CurrencyHandler public class CurrencyService
{ {
private readonly BotConfig _config; private readonly BotConfig _config;
private readonly DbHandler _db; private readonly DbService _db;
public CurrencyHandler(BotConfig config, DbHandler db) public CurrencyService(BotConfig config, DbService db)
{ {
_config = config; _config = config;
_db = db; _db = db;
} }
public async Task<bool> RemoveCurrencyAsync(IUser author, string reason, long amount, bool sendMessage) public async Task<bool> RemoveAsync(IUser author, string reason, long amount, bool sendMessage)
{ {
var success = await RemoveCurrencyAsync(author.Id, reason, amount); var success = await RemoveAsync(author.Id, reason, amount);
if (success && sendMessage) if (success && sendMessage)
try { await author.SendErrorAsync($"`You lost:` {amount} {_config.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } try { await author.SendErrorAsync($"`You lost:` {amount} {_config.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { }
@ -28,12 +28,11 @@ namespace NadekoBot.Services
return success; return success;
} }
public async Task<bool> RemoveCurrencyAsync(ulong authorId, string reason, long amount, IUnitOfWork uow = null) public async Task<bool> RemoveAsync(ulong authorId, string reason, long amount, IUnitOfWork uow = null)
{ {
if (amount < 0) if (amount < 0)
throw new ArgumentNullException(nameof(amount)); throw new ArgumentNullException(nameof(amount));
if (uow == null) if (uow == null)
{ {
using (uow = _db.UnitOfWork) using (uow = _db.UnitOfWork)
@ -61,15 +60,15 @@ namespace NadekoBot.Services
return true; return true;
} }
public async Task AddCurrencyAsync(IUser author, string reason, long amount, bool sendMessage) public async Task AddAsync(IUser author, string reason, long amount, bool sendMessage)
{ {
await AddCurrencyAsync(author.Id, reason, amount); await AddAsync(author.Id, reason, amount);
if (sendMessage) if (sendMessage)
try { await author.SendConfirmAsync($"`You received:` {amount} {_config.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } try { await author.SendConfirmAsync($"`You received:` {amount} {_config.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { }
} }
public async Task AddCurrencyAsync(ulong receiverId, string reason, long amount, IUnitOfWork uow = null) public async Task AddAsync(ulong receiverId, string reason, long amount, IUnitOfWork uow = null)
{ {
if (amount < 0) if (amount < 0)
throw new ArgumentNullException(nameof(amount)); throw new ArgumentNullException(nameof(amount));

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.CustomReactions namespace NadekoBot.Services.CustomReactions
{ {
public class CustomReactionsService : IBlockingExecutor public class CustomReactionsService : IEarlyBlockingExecutor
{ {
public CustomReaction[] GlobalReactions = new CustomReaction[] { }; public CustomReaction[] GlobalReactions = new CustomReaction[] { };
public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>(); public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
@ -19,10 +19,10 @@ namespace NadekoBot.Services.CustomReactions
public ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>(); public ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
private readonly Logger _log; private readonly Logger _log;
private readonly DbHandler _db; private readonly DbService _db;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
public CustomReactionsService(DbHandler db, DiscordShardedClient client) public CustomReactionsService(DbService db, DiscordShardedClient client)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;
@ -90,16 +90,15 @@ namespace NadekoBot.Services.CustomReactions
return greaction; return greaction;
} }
public async Task<bool> TryExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg) public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg)
{ {
//todo custom reactions
// maybe this message is a custom reaction // maybe this message is a custom reaction
// todo log custom reaction executions. return struct with info
var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false); var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false);
if (cr != null) //if it was, don't execute the command if (cr != null)
{ {
try try
{ {
//todo permissions
//if (guild != null) //if (guild != null)
//{ //{
// PermissionCache pc = Permissions.GetCache(guild.Id); // PermissionCache pc = Permissions.GetCache(guild.Id);
@ -107,7 +106,6 @@ namespace NadekoBot.Services.CustomReactions
// if (!pc.Permissions.CheckPermissions(usrMsg, cr.Trigger, "ActualCustomReactions", // if (!pc.Permissions.CheckPermissions(usrMsg, cr.Trigger, "ActualCustomReactions",
// out int index)) // out int index))
// { // {
// //todo print in guild actually
// var returnMsg = // var returnMsg =
// $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action."; // $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action.";
// _log.Info(returnMsg); // _log.Info(returnMsg);

View File

@ -64,6 +64,7 @@ Nadeko Support Server: https://discord.gg/nadekobot";
public List<StartupCommand> StartupCommands { get; set; } public List<StartupCommand> StartupCommands { get; set; }
public HashSet<BlockedCmdOrMdl> BlockedCommands { get; set; } public HashSet<BlockedCmdOrMdl> BlockedCommands { get; set; }
public HashSet<BlockedCmdOrMdl> BlockedModules { get; set; } public HashSet<BlockedCmdOrMdl> BlockedModules { get; set; }
public int PermissionVersion { get; set; } = 1;
} }
public class BlockedCmdOrMdl : DbEntity public class BlockedCmdOrMdl : DbEntity

View File

@ -126,10 +126,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.Where(gc => gc.RootPermission != null) .Where(gc => gc.RootPermission != null)
.Include(gc => gc.RootPermission); .Include(gc => gc.RootPermission);
//todo this is possibly a disaster for performance
//What i could do instead is count the number of permissions in the permission table for this guild
// and make a for loop with those.
// or just select permissions for this guild and manually chain them
for (int i = 0; i < 60; i++) for (int i = 0; i < 60; i++)
{ {
query = query.ThenInclude(gc => gc.Next); query = query.ThenInclude(gc => gc.Next);

View File

@ -3,17 +3,15 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class DbHandler public class DbService
{ {
private readonly DbContextOptions options; private readonly DbContextOptions options;
private string connectionString { get; } private readonly string _connectionString;
static DbHandler() { } public DbService(IBotCredentials creds)
public DbHandler(IBotCredentials creds)
{ {
connectionString = creds.Db.ConnectionString; _connectionString = creds.Db.ConnectionString;
var optionsBuilder = new DbContextOptionsBuilder(); var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite(creds.Db.ConnectionString); optionsBuilder.UseSqlite(creds.Db.ConnectionString);
options = optionsBuilder.Options; options = optionsBuilder.Options;

View File

@ -0,0 +1,120 @@
using Discord;
using Discord.WebSocket;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Services.Games
{
public class ChatterBotService : IEarlyBlockingExecutor
{
private readonly DiscordShardedClient _client;
private readonly Logger _log;
public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> ChatterBotGuilds { get; }
public ChatterBotService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
{
_client = client;
_log = LogManager.GetCurrentClassLogger();
ChatterBotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
gcs.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true)));
}
public string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot)
{
var channel = msg.Channel as ITextChannel;
cleverbot = null;
if (channel == null)
return null;
if (!ChatterBotGuilds.TryGetValue(channel.Guild.Id, out Lazy<ChatterBotSession> lazyCleverbot))
return null;
cleverbot = lazyCleverbot.Value;
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
if (msg.Content.StartsWith(normalMention))
{
message = msg.Content.Substring(normalMention.Length).Trim();
}
else if (msg.Content.StartsWith(nickMention))
{
message = msg.Content.Substring(nickMention.Length).Trim();
}
else
{
return null;
}
return message;
}
public async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message)
{
await channel.TriggerTypingAsync().ConfigureAwait(false);
var response = await cleverbot.Think(message).ConfigureAwait(false);
try
{
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
}
catch
{
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
}
return true;
}
public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
{
if (guild == null)
return false;
try
{
var message = PrepareMessage(usrMsg, out ChatterBotSession cbs);
if (message == null || cbs == null)
return false;
//todo permissions
//PermissionCache pc = Permissions.GetCache(guild.Id);
//if (!pc.Permissions.CheckPermissions(usrMsg,
// NadekoBot.Prefix + "cleverbot",
// "Games".ToLowerInvariant(),
// out int index))
//{
// //todo print in guild actually
// var returnMsg =
// $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand(guild)}** is preventing this action.";
// _log.Info(returnMsg);
// return true;
//}
var cleverbotExecuted = await TryAsk(cbs, (ITextChannel)usrMsg.Channel, message).ConfigureAwait(false);
if (cleverbotExecuted)
{
_log.Info($@"CleverBot Executed
Server: {guild.Name} [{guild.Id}]
Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}]
UserId: {usrMsg.Author} [{usrMsg.Author.Id}]
Message: {usrMsg.Content}");
return true;
}
}
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
return false;
}
}
}

View File

@ -28,8 +28,6 @@ namespace NadekoBot.Services.Games
private readonly IImagesService _images; private readonly IImagesService _images;
private readonly Logger _log; private readonly Logger _log;
public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
public readonly string TypingArticlesPath = "data/typing_articles2.json"; public readonly string TypingArticlesPath = "data/typing_articles2.json";
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>(); public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
@ -52,11 +50,6 @@ namespace NadekoBot.Services.Games
}, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1)); }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
//cleverbot
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
gcs.Where(gc => gc.CleverbotEnabled)
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true)));
//plantpick //plantpick
client.MessageReceived += PotentialFlowerGeneration; client.MessageReceived += PotentialFlowerGeneration;
GenerationChannels = new ConcurrentHashSet<ulong>(gcs GenerationChannels = new ConcurrentHashSet<ulong>(gcs
@ -73,55 +66,15 @@ namespace NadekoBot.Services.Games
} }
} }
public void AddTypingArticle(IUser user, string text)
public string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot)
{ {
var channel = msg.Channel as ITextChannel; TypingArticles.Add(new TypingArticle
cleverbot = null;
if (channel == null)
return null;
Lazy<ChatterBotSession> lazyCleverbot;
if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out lazyCleverbot))
return null;
cleverbot = lazyCleverbot.Value;
var nadekoId = _client.CurrentUser.Id;
var normalMention = $"<@{nadekoId}> ";
var nickMention = $"<@!{nadekoId}> ";
string message;
if (msg.Content.StartsWith(normalMention))
{ {
message = msg.Content.Substring(normalMention.Length).Trim(); Title = $"Text added on {DateTime.UtcNow} by {user}",
} Text = text.SanitizeMentions(),
else if (msg.Content.StartsWith(nickMention)) });
{
message = msg.Content.Substring(nickMention.Length).Trim();
}
else
{
return null;
}
return message; File.WriteAllText(TypingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
}
public async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message)
{
await channel.TriggerTypingAsync().ConfigureAwait(false);
var response = await cleverbot.Think(message).ConfigureAwait(false);
try
{
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false);
}
catch
{
await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\
}
return true;
} }
public ConcurrentHashSet<ulong> GenerationChannels { get; } public ConcurrentHashSet<ulong> GenerationChannels { get; }
@ -129,8 +82,10 @@ namespace NadekoBot.Services.Games
public ConcurrentDictionary<ulong, List<IUserMessage>> PlantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>(); public ConcurrentDictionary<ulong, List<IUserMessage>> PlantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
//channelId/last generation //channelId/last generation
public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>(); public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
private ConcurrentDictionary<ulong, object> _locks { get; } = new ConcurrentDictionary<ulong, object>();
public KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage() public (string Name, ImmutableArray<byte> Data) GetRandomCurrencyImage()
{ {
var rng = new NadekoRandom(); var rng = new NadekoRandom();
return _images.Currency[rng.Next(0, _images.Currency.Length)]; return _images.Currency[rng.Next(0, _images.Currency.Length)];
@ -139,68 +94,61 @@ namespace NadekoBot.Services.Games
private string GetText(ITextChannel ch, string key, params object[] rep) private string GetText(ITextChannel ch, string key, params object[] rep)
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep); => _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
private Task PotentialFlowerGeneration(SocketMessage imsg) private async Task PotentialFlowerGeneration(SocketMessage imsg)
{ {
var msg = imsg as SocketUserMessage; var msg = imsg as SocketUserMessage;
if (msg == null || msg.Author.IsBot) if (msg == null || msg.Author.IsBot)
return Task.CompletedTask; return;
var channel = imsg.Channel as ITextChannel; var channel = imsg.Channel as ITextChannel;
if (channel == null) if (channel == null)
return Task.CompletedTask; return;
if (!GenerationChannels.Contains(channel.Id)) if (!GenerationChannels.Contains(channel.Id))
return Task.CompletedTask; return;
var _ = Task.Run(async () => try
{ {
try var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue);
var rng = new NadekoRandom();
if (DateTime.Now - TimeSpan.FromSeconds(_bc.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
return;
var num = rng.Next(1, 101) + _bc.CurrencyGenerationChance * 100;
if (num > 100 && LastGenerations.TryUpdate(channel.Id, DateTime.Now, lastGeneration))
{ {
var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); var dropAmount = _bc.CurrencyDropAmount;
var rng = new NadekoRandom();
//todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup: if (dropAmount > 0)
if (DateTime.Now - TimeSpan.FromSeconds(_bc.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again
return;
var num = rng.Next(1, 101) + _bc.CurrencyGenerationChance * 100;
if (num > 100)
{ {
LastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); var msgs = new IUserMessage[dropAmount];
var prefix = NadekoBot.Prefix;
var dropAmount = _bc.CurrencyDropAmount; var toSend = dropAmount == 1
? GetText(channel, "curgen_sn", _bc.CurrencySign)
if (dropAmount > 0) + " " + GetText(channel, "pick_sn", prefix)
: GetText(channel, "curgen_pl", dropAmount, _bc.CurrencySign)
+ " " + GetText(channel, "pick_pl", prefix);
var file = GetRandomCurrencyImage();
using (var fileStream = file.Data.ToStream())
{ {
var msgs = new IUserMessage[dropAmount]; var sent = await channel.SendFileAsync(
var prefix = NadekoBot.Prefix; fileStream,
var toSend = dropAmount == 1 file.Name,
? GetText(channel, "curgen_sn", _bc.CurrencySign) toSend).ConfigureAwait(false);
+ " " + GetText(channel, "pick_sn", prefix)
: GetText(channel, "curgen_pl", dropAmount, _bc.CurrencySign)
+ " " + GetText(channel, "pick_pl", prefix);
var file = GetRandomCurrencyImage();
using (var fileStream = file.Value.ToStream())
{
var sent = await channel.SendFileAsync(
fileStream,
file.Key,
toSend).ConfigureAwait(false);
msgs[0] = sent; msgs[0] = sent;
}
PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
} }
PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
} }
} }
catch (Exception ex) }
{ catch (Exception ex)
LogManager.GetCurrentClassLogger().Warn(ex); {
} LogManager.GetCurrentClassLogger().Warn(ex);
}); }
return Task.CompletedTask; return;
} }
} }
} }

View File

@ -0,0 +1,158 @@
using Discord;
using Discord.WebSocket;
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Games
{
//todo 75 rewrite
public class Poll
{
private readonly IUserMessage _originalMessage;
private readonly IGuild _guild;
private string[] answers { get; }
private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
private readonly string _question;
private readonly DiscordShardedClient _client;
private readonly NadekoStrings _strings;
private bool running = false;
private HashSet<ulong> _guildUsers;
public event Action<ulong> OnEnded = delegate { };
public bool IsPublic { get; }
public Poll(DiscordShardedClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
_client = client;
_strings = strings;
_originalMessage = umsg;
_guild = ((ITextChannel)umsg.Channel).Guild;
_question = question;
answers = enumerable as string[] ?? enumerable.ToArray();
IsPublic = isPublic;
}
public EmbedBuilder GetStats(string title)
{
var results = _participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value)
.ToArray();
var eb = new EmbedBuilder().WithTitle(title);
var sb = new StringBuilder()
.AppendLine(Format.Bold(_question))
.AppendLine();
var totalVotesCast = 0;
if (results.Length == 0)
{
sb.AppendLine(GetText("no_votes_cast"));
}
else
{
for (int i = 0; i < results.Length; i++)
{
var result = results[i];
sb.AppendLine(GetText("poll_result",
result.Key,
Format.Bold(answers[result.Key - 1]),
Format.Bold(result.Value.ToString())));
totalVotesCast += result.Value;
}
}
eb.WithDescription(sb.ToString())
.WithFooter(efb => efb.WithText(GetText("x_votes_cast", totalVotesCast)));
return eb;
}
public async Task StartPoll()
{
var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n";
var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!IsPublic)
msgToSend += "\n" + Format.Bold(GetText("poll_vote_private"));
else
msgToSend += "\n" + Format.Bold(GetText("poll_vote_public"));
if (!IsPublic)
_guildUsers = new HashSet<ulong>((await _guild.GetUsersAsync().ConfigureAwait(false)).Select(x => x.Id));
await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false);
running = true;
}
public async Task StopPoll()
{
running = false;
OnEnded(_guild.Id);
await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
}
public async Task<bool> TryVote(IUserMessage msg)
{
// has to be a user message
if (msg == null || msg.Author.IsBot || !running)
return false;
// has to be an integer
if (!int.TryParse(msg.Content, out int vote))
return false;
if (vote < 1 || vote > answers.Length)
return false;
IMessageChannel ch;
if (IsPublic)
{
//if public, channel must be the same the poll started in
if (_originalMessage.Channel.Id != msg.Channel.Id)
return false;
ch = msg.Channel;
}
else
{
//if private, channel must be dm channel
if ((ch = msg.Channel as IDMChannel) == null)
return false;
// user must be a member of the guild this poll is in
if (!_guildUsers.Contains(msg.Author.Id))
return false;
}
//user can vote only once
if (_participants.TryAdd(msg.Author.Id, vote))
{
if (!IsPublic)
{
await ch.SendConfirmAsync(GetText("thanks_for_voting", Format.Bold(msg.Author.Username))).ConfigureAwait(false);
}
else
{
var toDelete = await ch.SendConfirmAsync(GetText("poll_voted", Format.Bold(msg.Author.ToString()))).ConfigureAwait(false);
toDelete.DeleteAfter(5);
}
return true;
}
return false;
}
private string GetText(string key, params object[] replacements)
=> _strings.GetText(key,
_guild.Id,
"Games".ToLowerInvariant(),
replacements);
}
}

View File

@ -0,0 +1,78 @@
using NadekoBot.DataStructures.ModuleBehaviors;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using NLog;
namespace NadekoBot.Services.Games
{
public class PollService : IEarlyBlockingExecutor
{
public ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
private readonly Logger _log;
private readonly DiscordShardedClient _client;
private readonly NadekoStrings _strings;
public PollService(DiscordShardedClient client, NadekoStrings strings)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
_strings = strings;
}
public async Task<bool?> StartPoll(ITextChannel channel, IUserMessage msg, string arg, bool isPublic = false)
{
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return null;
var data = arg.Split(';');
if (data.Length < 3)
return null;
var poll = new Poll(_client, _strings, msg, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
{
poll.OnEnded += (gid) =>
{
ActivePolls.TryRemove(gid, out _);
};
await poll.StartPoll().ConfigureAwait(false);
return true;
}
return false;
}
public async Task<bool> TryExecuteEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg)
{
if (guild == null)
{
foreach (var kvp in ActivePolls)
{
if (!kvp.Value.IsPublic)
{
if (await kvp.Value.TryVote(msg).ConfigureAwait(false))
return true;
}
}
return false;
}
if (!ActivePolls.TryGetValue(guild.Id, out var poll))
return false;
try
{
return await poll.TryVote(msg).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
return false;
}
}
}

View File

@ -14,13 +14,13 @@ namespace NadekoBot.Services
{ {
public class GreetSettingsService public class GreetSettingsService
{ {
private readonly DbHandler _db; private readonly DbService _db;
public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache; public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly Logger _log; private readonly Logger _log;
public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbHandler db) public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbService db)
{ {
_db = db; _db = db;
_client = client; _client = client;

View File

@ -0,0 +1,32 @@
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using System;
namespace NadekoBot.Services.Help
{
public class HelpService : ILateExecutor
{
private readonly BotConfig _bc;
public HelpService(BotConfig bc)
{
_bc = bc;
}
public async Task LateExecute(DiscordShardedClient client, IGuild guild, IUserMessage msg)
{
try
{
if(guild == null)
await msg.Channel.SendMessageAsync(_bc.DMHelpString).ConfigureAwait(false);
}
catch (Exception)
{
//ignore
}
}
}
}

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
namespace NadekoBot.Services namespace NadekoBot.Services
@ -9,8 +8,8 @@ namespace NadekoBot.Services
ImmutableArray<byte> Heads { get; } ImmutableArray<byte> Heads { get; }
ImmutableArray<byte> Tails { get; } ImmutableArray<byte> Tails { get; }
ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Currency { get; } ImmutableArray<(string, ImmutableArray<byte>)> Currency { get; }
ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Dice { get; } ImmutableArray<ImmutableArray<byte>> Dice { get; }
ImmutableArray<byte> SlotBackground { get; } ImmutableArray<byte> SlotBackground { get; }
ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; } ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; }

View File

@ -162,7 +162,7 @@ namespace NadekoBot.Services.Impl
return toReturn; return toReturn;
} }
//todo AsyncEnumerable
public async Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds) public async Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds)
{ {
var videoIdsList = videoIds as List<string> ?? videoIds.ToList(); var videoIdsList = videoIds as List<string> ?? videoIds.ToList();

View File

@ -1,6 +1,5 @@
using NLog; using NLog;
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -30,11 +29,10 @@ namespace NadekoBot.Services.Impl
public ImmutableArray<byte> Heads { get; private set; } public ImmutableArray<byte> Heads { get; private set; }
public ImmutableArray<byte> Tails { get; private set; } public ImmutableArray<byte> Tails { get; private set; }
public ImmutableArray<(string, ImmutableArray<byte>)> Currency { get; private set; }
//todo C#7 tuples public ImmutableArray<ImmutableArray<byte>> Dice { get; private set; }
public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Currency { get; private set; }
public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Dice { get; private set; }
public ImmutableArray<byte> SlotBackground { get; private set; } public ImmutableArray<byte> SlotBackground { get; private set; }
public ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; private set; } public ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; private set; }
@ -59,15 +57,12 @@ namespace NadekoBot.Services.Impl
Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray(); Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray();
Currency = Directory.GetFiles(_currencyImagesPath) Currency = Directory.GetFiles(_currencyImagesPath)
.Select(x => new KeyValuePair<string, ImmutableArray<byte>>( .Select(x => (Path.GetFileName(x), File.ReadAllBytes(x).ToImmutableArray()))
Path.GetFileName(x),
File.ReadAllBytes(x).ToImmutableArray()))
.ToImmutableArray(); .ToImmutableArray();
Dice = Directory.GetFiles(_diceImagesPath) Dice = Directory.GetFiles(_diceImagesPath)
.OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x))) .OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x)))
.Select(x => new KeyValuePair<string, ImmutableArray<byte>>(x, .Select(x => File.ReadAllBytes(x).ToImmutableArray())
File.ReadAllBytes(x).ToImmutableArray()))
.ToImmutableArray(); .ToImmutableArray();
SlotBackground = File.ReadAllBytes(_slotBackgroundPath).ToImmutableArray(); SlotBackground = File.ReadAllBytes(_slotBackgroundPath).ToImmutableArray();

View File

@ -10,13 +10,13 @@ namespace NadekoBot.Services
public class Localization : ILocalization public class Localization : ILocalization
{ {
private readonly Logger _log; private readonly Logger _log;
private readonly DbHandler _db; private readonly DbService _db;
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; } public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture; public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
private Localization() { } private Localization() { }
public Localization(string defaultCulture, IDictionary<ulong, string> cultureInfoNames, DbHandler db) public Localization(string defaultCulture, IDictionary<ulong, string> cultureInfoNames, DbService db)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;

View File

@ -61,7 +61,7 @@ namespace NadekoBot.Services
} }
public string GetText(string key, ulong guildId, string lowerModuleTypeName, params object[] replacements) => public string GetText(string key, ulong guildId, string lowerModuleTypeName, params object[] replacements) =>
GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName); GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName, replacements);
public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName) public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
{ {

View File

@ -10,6 +10,7 @@ using NLog;
using System.IO; using System.IO;
using VideoLibrary; using VideoLibrary;
using System.Net.Http; using System.Net.Http;
using System.Collections.Generic;
namespace NadekoBot.Services.Music namespace NadekoBot.Services.Music
{ {
@ -20,16 +21,17 @@ namespace NadekoBot.Services.Music
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly ILocalization _localization; private readonly ILocalization _localization;
private readonly DbHandler _db; private readonly DbService _db;
private readonly Logger _log; private readonly Logger _log;
private readonly SoundCloudApiService _sc; private readonly SoundCloudApiService _sc;
private readonly IBotCredentials _creds; private readonly IBotCredentials _creds;
private readonly ConcurrentDictionary<ulong, float> _defaultVolumes;
public ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>(); public ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
public MusicService(IGoogleApiService google, public MusicService(IGoogleApiService google,
NadekoStrings strings, ILocalization localization, DbHandler db, NadekoStrings strings, ILocalization localization, DbService db,
SoundCloudApiService sc, IBotCredentials creds) SoundCloudApiService sc, IBotCredentials creds, IEnumerable<GuildConfig> gcs)
{ {
_google = google; _google = google;
_strings = strings; _strings = strings;
@ -41,6 +43,8 @@ namespace NadekoBot.Services.Music
try { Directory.Delete(MusicDataPath, true); } catch { } try { Directory.Delete(MusicDataPath, true); } catch { }
_defaultVolumes = new ConcurrentDictionary<ulong, float>(gcs.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));
Directory.CreateDirectory(MusicDataPath); Directory.CreateDirectory(MusicDataPath);
} }
@ -57,12 +61,14 @@ namespace NadekoBot.Services.Music
return MusicPlayers.GetOrAdd(guildId, server => return MusicPlayers.GetOrAdd(guildId, server =>
{ {
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; var vol = _defaultVolumes.GetOrAdd(guildId, (id) =>
using (var uow = _db.UnitOfWork)
{ {
//todo move to cached variable using (var uow = _db.UnitOfWork)
vol = uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume; {
} return uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume;
}
});
var mp = new MusicPlayer(voiceCh, textCh, vol, _google); var mp = new MusicPlayer(voiceCh, textCh, vol, _google);
IUserMessage playingMessage = null; IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null; IUserMessage lastFinishedMessage = null;

View File

@ -1,14 +1,18 @@
using NadekoBot.Services.Database.Models; using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class BlacklistService public class BlacklistService : IEarlyBlocker
{ {
public ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } public ConcurrentHashSet<ulong> BlacklistedUsers { get; }
public ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } public ConcurrentHashSet<ulong> BlacklistedGuilds { get; }
public ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } public ConcurrentHashSet<ulong> BlacklistedChannels { get; }
public BlacklistService(BotConfig bc) public BlacklistService(BotConfig bc)
{ {
@ -17,5 +21,10 @@ namespace NadekoBot.Services.Permissions
BlacklistedGuilds = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId)); BlacklistedGuilds = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId));
BlacklistedChannels = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId)); BlacklistedChannels = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId));
} }
public Task<bool> TryBlockEarly(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
=> Task.FromResult((guild != null && BlacklistedGuilds.Contains(guild.Id)) ||
BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
BlacklistedUsers.Contains(usrMsg.Author.Id));
} }
} }

View File

@ -1,11 +1,15 @@
using NadekoBot.Services.Database.Models; using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class CmdCdService public class CmdCdService : ILateBlocker
{ {
public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; } public ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>(); public ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
@ -16,6 +20,41 @@ namespace NadekoBot.Services.Permissions
gcs.ToDictionary(k => k.GuildId, gcs.ToDictionary(k => k.GuildId,
v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns))); v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
} }
public Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild,
IMessageChannel channel, IUser user, string moduleName, string commandName)
{
if (guild == null)
return Task.FromResult(false);
var cmdcds = CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == commandName.ToLowerInvariant())) != null)
{
var activeCdsForGuild = ActiveCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == commandName.ToLowerInvariant()) != null)
{
return Task.FromResult(true);
}
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = commandName.ToLowerInvariant(),
});
var _ = Task.Run(async () =>
{
try
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == commandName.ToLowerInvariant() && ac.UserId == user.Id);
}
catch
{
// ignored
}
});
}
return Task.FromResult(false);
}
} }
public class ActiveCooldown public class ActiveCooldown

View File

@ -1,12 +1,21 @@
using NadekoBot.Services.Database.Models; using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
using NadekoBot.Extensions;
using Discord.Net;
using NLog;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class FilterService public class FilterService : IEarlyBlocker
{ {
private readonly Logger _log;
public ConcurrentHashSet<ulong> InviteFilteringChannels { get; } public ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public ConcurrentHashSet<ulong> InviteFilteringServers { get; } public ConcurrentHashSet<ulong> InviteFilteringServers { get; }
@ -34,6 +43,8 @@ namespace NadekoBot.Services.Permissions
public FilterService(IEnumerable<GuildConfig> gcs) public FilterService(IEnumerable<GuildConfig> gcs)
{ {
_log = LogManager.GetCurrentClassLogger();
InviteFilteringServers = new ConcurrentHashSet<ulong>(gcs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId)); InviteFilteringServers = new ConcurrentHashSet<ulong>(gcs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId))); InviteFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
@ -46,5 +57,61 @@ namespace NadekoBot.Services.Permissions
WordFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId))); WordFilteringChannels = new ConcurrentHashSet<ulong>(gcs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
} }
//todo ignore guild admin
public async Task<bool> TryBlockEarly(DiscordShardedClient client, IGuild guild, IUserMessage msg)
=> await FilterInvites(client, guild, msg) || await FilterWords(client, guild, msg);
public async Task<bool> FilterWords(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
{
if (guild is null)
return false;
var filteredChannelWords = FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
{
foreach (var word in wordsInMessage)
{
if (filteredChannelWords.Contains(word) ||
filteredServerWords.Contains(word))
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex);
}
return true;
}
}
}
return false;
}
public async Task<bool> FilterInvites(DiscordShardedClient client, IGuild guild, IUserMessage usrMsg)
{
if (guild is null)
return false;
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) ||
InviteFilteringServers.Contains(guild.Id)) &&
usrMsg.Content.IsDiscordInvite())
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return true;
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
return true;
}
}
return false;
}
} }
} }

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
@ -6,25 +7,27 @@ using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Services.Permissions namespace NadekoBot.Services.Permissions
{ {
public class PermissionsService public class PermissionsService : ILateBlocker
{ {
private readonly DbHandler _db; private readonly DbService _db;
private readonly Logger _log; private readonly Logger _log;
//guildid, root permission //guildid, root permission
public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } = public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
new ConcurrentDictionary<ulong, PermissionCache>(); new ConcurrentDictionary<ulong, PermissionCache>();
public PermissionsService(DbHandler db) public PermissionsService(DbService db, BotConfig bc)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
TryMigratePermissions(); TryMigratePermissions(bc);
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
foreach (var x in uow.GuildConfigs.Permissionsv2ForAll()) foreach (var x in uow.GuildConfigs.Permissionsv2ForAll())
@ -59,61 +62,67 @@ namespace NadekoBot.Services.Permissions
return pc; return pc;
} }
private void TryMigratePermissions() private void TryMigratePermissions(BotConfig bc)
{ {
var log = LogManager.GetCurrentClassLogger(); var log = LogManager.GetCurrentClassLogger();
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs if (bc.PermissionVersion <= 1)
.OldPermissionsForAll()
.Where(x => x.RootPermission != null) // there is a check inside already, but just in case
.ToDictionary(k => k.GuildId,
v => new OldPermissionCache()
{
RootPermission = v.RootPermission,
Verbose = v.VerbosePermissions,
PermRole = v.PermissionRole
}));
if (oldCache.Any())
{ {
log.Info("Old permissions found. Performing one-time migration to v2."); log.Info("Permission version is 1, upgrading to 2.");
var i = 0; var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
foreach (var oc in oldCache) .OldPermissionsForAll()
{ .Where(x => x.RootPermission != null) // there is a check inside already, but just in case
if (i % 3 == 0) .ToDictionary(k => k.GuildId,
log.Info("Migrating Permissions #" + i + " - GuildId: " + oc.Key); v => new OldPermissionCache()
i++;
var gc = uow.GuildConfigs.GcWithPermissionsv2For(oc.Key);
var oldPerms = oc.Value.RootPermission.AsEnumerable().Reverse().ToList();
uow._context.Set<Permission>().RemoveRange(oldPerms);
gc.RootPermission = null;
if (oldPerms.Count > 2)
{
var newPerms = oldPerms.Take(oldPerms.Count - 1)
.Select(x => x.Tov2())
.ToList();
var allowPerm = Permissionv2.AllowAllPerm;
var firstPerm = newPerms[0];
if (allowPerm.State != firstPerm.State ||
allowPerm.PrimaryTarget != firstPerm.PrimaryTarget ||
allowPerm.SecondaryTarget != firstPerm.SecondaryTarget ||
allowPerm.PrimaryTargetId != firstPerm.PrimaryTargetId ||
allowPerm.SecondaryTargetName != firstPerm.SecondaryTargetName)
newPerms.Insert(0, Permissionv2.AllowAllPerm);
Cache.TryAdd(oc.Key, new PermissionCache
{ {
Permissions = new PermissionsCollection<Permissionv2>(newPerms), RootPermission = v.RootPermission,
Verbose = gc.VerbosePermissions, Verbose = v.VerbosePermissions,
PermRole = gc.PermissionRole, PermRole = v.PermissionRole
}); }));
gc.Permissions = newPerms;
if (oldCache.Any())
{
log.Info("Old permissions found. Performing one-time migration to v2.");
var i = 0;
foreach (var oc in oldCache)
{
if (i % 3 == 0)
log.Info("Migrating Permissions #" + i + " - GuildId: " + oc.Key);
i++;
var gc = uow.GuildConfigs.GcWithPermissionsv2For(oc.Key);
var oldPerms = oc.Value.RootPermission.AsEnumerable().Reverse().ToList();
uow._context.Set<Permission>().RemoveRange(oldPerms);
gc.RootPermission = null;
if (oldPerms.Count > 2)
{
var newPerms = oldPerms.Take(oldPerms.Count - 1)
.Select(x => x.Tov2())
.ToList();
var allowPerm = Permissionv2.AllowAllPerm;
var firstPerm = newPerms[0];
if (allowPerm.State != firstPerm.State ||
allowPerm.PrimaryTarget != firstPerm.PrimaryTarget ||
allowPerm.SecondaryTarget != firstPerm.SecondaryTarget ||
allowPerm.PrimaryTargetId != firstPerm.PrimaryTargetId ||
allowPerm.SecondaryTargetName != firstPerm.SecondaryTargetName)
newPerms.Insert(0, Permissionv2.AllowAllPerm);
Cache.TryAdd(oc.Key, new PermissionCache
{
Permissions = new PermissionsCollection<Permissionv2>(newPerms),
Verbose = gc.VerbosePermissions,
PermRole = gc.PermissionRole,
});
gc.Permissions = newPerms;
}
} }
log.Info("Permission migration to v2 is done.");
} }
log.Info("Permission migration to v2 is done.");
uow.BotConfig.GetOrCreate().PermissionVersion = 2;
uow.Complete(); uow.Complete();
} }
} }
@ -151,5 +160,39 @@ namespace NadekoBot.Services.Permissions
return old; return old;
}); });
} }
public async Task<bool> TryBlockLate(DiscordShardedClient client, IUserMessage msg, IGuild guild, IMessageChannel channel, IUser user, string moduleName, string commandName)
{
await Task.Yield();
if (guild == null)
{
return false;
}
var resetCommand = commandName == "resetperms";
//todo perms
PermissionCache pc = GetCache(guild.Id);
if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out int index))
{
var returnMsg = $"Permission number #{index + 1} **{pc.Permissions[index].GetCommand((SocketGuild)guild)}** is preventing this action.";
return true;
//return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, returnMsg));
}
if (moduleName == "Permissions")
{
var roles = (user as SocketGuildUser)?.Roles ?? ((IGuildUser)user).RoleIds.Select(x => guild.GetRole(x)).Where(x => x != null);
if (!roles.Any(r => r.Name.Trim().ToLowerInvariant() == pc.PermRole.Trim().ToLowerInvariant()) && user.Id != ((IGuildUser)user).Guild.OwnerId)
{
return true;
//return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"You need the **{pc.PermRole}** role in order to use permission commands."));
}
}
return false;
}
} }
} }

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Services.Searches
{ {
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
private readonly DbHandler _db; private readonly DbService _db;
private readonly Logger _log; private readonly Logger _log;
public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>(); public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
@ -31,7 +31,7 @@ namespace NadekoBot.Services.Searches
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>(); public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
public List<MagicItem> MagicItems { get; } = new List<MagicItem>(); public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbHandler db) public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbService db)
{ {
_client = client; _client = client;
_google = google; _google = google;

View File

@ -19,11 +19,11 @@ namespace NadekoBot.Services.Searches
private bool firstStreamNotifPass { get; set; } = true; private bool firstStreamNotifPass { get; set; } = true;
private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private readonly DbHandler _db; private readonly DbService _db;
private readonly DiscordShardedClient _client; private readonly DiscordShardedClient _client;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
public StreamNotificationService(DbHandler db, DiscordShardedClient client, NadekoStrings strings) public StreamNotificationService(DbService db, DiscordShardedClient client, NadekoStrings strings)
{ {
_db = db; _db = db;
_client = client; _client = client;

View File

@ -17,9 +17,9 @@ namespace NadekoBot.Services.Utility
private readonly Logger _log; private readonly Logger _log;
private Timer _timer; private Timer _timer;
private readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0); private readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0);
private readonly DbHandler _db; private readonly DbService _db;
public ConverterService(DbHandler db) public ConverterService(DbService db)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.Utility namespace NadekoBot.Services.Utility
{ {
//todo 50 rewrite
public class MessageRepeaterService public class MessageRepeaterService
{ {
//messagerepeater //messagerepeater
@ -23,7 +24,6 @@ namespace NadekoBot.Services.Utility
#else #else
await Task.Delay(30000).ConfigureAwait(false); await Task.Delay(30000).ConfigureAwait(false);
#endif #endif
//todo this is pretty terrible :kms: no time
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(gcs
.ToDictionary(gc => gc.GuildId, .ToDictionary(gc => gc.GuildId,
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters

View File

@ -1,6 +1,6 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace NadekoBot.Modules.Utility.Models namespace NadekoBot.Services.Utility.Patreon
{ {
public class PatreonData public class PatreonData
{ {

View File

@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Models namespace NadekoBot.Services.Utility.Patreon
{ {
public class Attributes public class Attributes
{ {

View File

@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Models namespace NadekoBot.Services.Utility.Patreon
{ {
public class DiscordConnection public class DiscordConnection
{ {

View File

@ -0,0 +1,162 @@
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Utility.Patreon;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Services.Utility
{
public class PatreonRewardsService
{
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);
public ImmutableArray<PatreonUserAndReward> Pledges { get; private set; }
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
public readonly Timer Updater;
private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
private readonly Logger _log;
public readonly TimeSpan Interval = TimeSpan.FromHours(1);
private IBotCredentials _creds;
private readonly DbService _db;
private readonly CurrencyService _currency;
private PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency)
{
_creds = creds;
_db = db;
_currency = currency;
if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken))
return;
_log = LogManager.GetCurrentClassLogger();
Updater = new Timer(async (_) => await LoadPledges(), null, TimeSpan.Zero, Interval);
}
public async Task LoadPledges()
{
LastUpdate = DateTime.UtcNow;
await getPledgesLocker.WaitAsync(1000).ConfigureAwait(false);
try
{
var rewards = new List<PatreonPledge>();
var users = new List<PatreonUser>();
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
var data = new PatreonData()
{
Links = new PatreonDataLinks()
{
next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges"
}
};
do
{
var res = await http.GetStringAsync(data.Links.next)
.ConfigureAwait(false);
data = JsonConvert.DeserializeObject<PatreonData>(res);
var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
.Where(x => x.attributes.declined_since == null));
users.AddRange(data.Included
.Where(x => x["type"].ToString() == "user")
.Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
} while (!string.IsNullOrWhiteSpace(data.Links.next));
}
Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
{
User = y,
Reward = x,
}).ToImmutableArray();
}
catch (Exception ex)
{
_log.Warn(ex);
}
finally
{
var _ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
getPledgesLocker.Release();
});
}
}
public async Task<int> ClaimReward(ulong userId)
{
await claimLockJustInCase.WaitAsync();
var now = DateTime.UtcNow;
try
{
var data = Pledges.FirstOrDefault(x => x.User.attributes?.social_connections?.discord?.user_id == userId.ToString());
if (data == null)
return 0;
var amount = data.Reward.attributes.amount_cents;
using (var uow = _db.UnitOfWork)
{
var users = uow._context.Set<RewardedUser>();
var usr = users.FirstOrDefault(x => x.PatreonUserId == data.User.id);
if (usr == null)
{
users.Add(new RewardedUser()
{
UserId = userId,
PatreonUserId = data.User.id,
LastReward = now,
AmountRewardedThisMonth = amount,
});
await _currency.AddAsync(userId, "Patreon reward - new", amount, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return amount;
}
if (usr.LastReward.Month != now.Month)
{
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
usr.PatreonUserId = data.User.id;
await _currency.AddAsync(userId, "Patreon reward - recurring", amount, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return amount;
}
if (usr.AmountRewardedThisMonth < amount)
{
var toAward = amount - usr.AmountRewardedThisMonth;
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
usr.PatreonUserId = data.User.id;
await _currency.AddAsync(usr.UserId, "Patreon reward - update", toAward, uow).ConfigureAwait(false);
await uow.CompleteAsync().ConfigureAwait(false);
return toAward;
}
}
return 0;
}
finally
{
claimLockJustInCase.Release();
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More