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>
/// Implemented by modules which can execute something and prevent further commands from being executed.
/// </summary>
public interface IBlockingExecutor
public interface IEarlyBlockingExecutor
{
/// <summary>
/// Try to execute some logic within some module's service.
/// </summary>
/// <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");
migrationBuilder.CreateIndex(
name: "IX_Repeaters_ChannelId",
name: "IX_Repeaters_channelId",
table: "Repeaters",
column: "ChannelId",
unique: true);

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Migrations
});
migrationBuilder.CreateIndex(
name: "IX_Repeaters_ChannelId",
name: "IX_Repeaters_channelId",
table: "Repeaters",
column: "ChannelId",
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)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752");
.HasAnnotation("ProductVersion", "1.1.1");
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
{
@ -165,6 +165,8 @@ namespace NadekoBot.Migrations
b.Property<string>("OkColor");
b.Property<int>("PermissionVersion");
b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses");

View File

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

View File

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

View File

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

View File

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

View File

@ -14,9 +14,9 @@ namespace NadekoBot.Modules.Administration
public class MuteCommands : NadekoSubmodule
{
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;
_db = db;

View File

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

View File

@ -19,9 +19,9 @@ namespace NadekoBot.Modules.Administration
{
private readonly ProtectionService _service;
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;
_mute = mute;

View File

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

View File

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

View File

@ -21,14 +21,14 @@ namespace NadekoBot.Modules.Administration
[Group]
public class SelfCommands : NadekoSubmodule
{
private readonly DbHandler _db;
private readonly DbService _db;
private static readonly object _locker = new object();
private readonly SelfService _service;
private readonly DiscordShardedClient _client;
private readonly IImagesService _images;
public SelfCommands(DbHandler db, SelfService service, DiscordShardedClient client,
public SelfCommands(DbService db, SelfService service, DiscordShardedClient client,
IImagesService images)
{
_db = db;
@ -204,64 +204,6 @@ namespace NadekoBot.Modules.Administration
}
//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]
[OwnerOnly]
public async Task ConnectShard(int shardid)

View File

@ -14,9 +14,9 @@ namespace NadekoBot.Modules.Administration
public class ServerGreetCommands : NadekoSubmodule
{
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;
_db = db;

View File

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

View File

@ -19,9 +19,9 @@ namespace NadekoBot.Modules.Administration
public class VcRoleCommands : NadekoSubmodule
{
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;
_db = db;
@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageRoles)]
[RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageRoles)]
//todo discord.net [RequireBotPermission(GuildPermission.ManageChannels)]
//todo 999 discord.net [RequireBotPermission(GuildPermission.ManageChannels)]
[RequireContext(ContextType.Guild)]
public async Task VcRole([Remainder]IRole role = null)
{

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Administration
public class VoicePlusTextCommands : NadekoSubmodule
{
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;
_db = db;

View File

@ -15,11 +15,11 @@ namespace NadekoBot.Modules.CustomReactions
public class CustomReactions : NadekoTopLevelModule
{
private readonly IBotCredentials _creds;
private readonly DbHandler _db;
private readonly DbService _db;
private readonly CustomReactionsService _crs;
private readonly DiscordShardedClient _client;
public CustomReactions(IBotCredentials creds, DbHandler db, CustomReactionsService crs,
public CustomReactions(IBotCredentials creds, DbService db, CustomReactionsService crs,
DiscordShardedClient client)
{
_creds = creds;
@ -239,7 +239,7 @@ namespace NadekoBot.Modules.CustomReactions
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{
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();
success = true;
}

View File

@ -21,16 +21,16 @@ namespace NadekoBot.Modules.Gambling
public class AnimalRacing : NadekoSubmodule
{
private readonly BotConfig _bc;
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client;
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;
_ch = ch;
_cs = cs;
_client = client;
}
@ -39,7 +39,7 @@ namespace NadekoBot.Modules.Gambling
public async Task Race()
{
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix,
_bc, _ch, _client,_localization, _strings);
_bc, _cs, _client,_localization, _strings);
if (ar.Fail)
await ReplyErrorLocalized("race_failed_starting").ConfigureAwait(false);
@ -63,7 +63,7 @@ namespace NadekoBot.Modules.Gambling
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
public class AnimalRace
{
@ -81,7 +81,7 @@ namespace NadekoBot.Modules.Gambling
private readonly ITextChannel _raceChannel;
private readonly BotConfig _bc;
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client;
private readonly ILocalization _localization;
private readonly NadekoStrings _strings;
@ -89,12 +89,12 @@ namespace NadekoBot.Modules.Gambling
public bool Started { get; private set; }
public AnimalRace(ulong serverId, ITextChannel channel, string prefix, BotConfig bc,
CurrencyHandler ch, DiscordShardedClient client, ILocalization localization,
CurrencyService cs, DiscordShardedClient client, ILocalization localization,
NadekoStrings strings)
{
_prefix = prefix;
_bc = bc;
_ch = ch;
_cs = cs;
_log = LogManager.GetCurrentClassLogger();
_serverId = serverId;
_raceChannel = channel;
@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Gambling
var p = _participants.FirstOrDefault();
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();
return;
}
@ -232,7 +232,7 @@ namespace NadekoBot.Modules.Gambling
{
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);
await _raceChannel.SendConfirmAsync(GetText("animal_race"),
Format.Bold(GetText("animal_race_won_money", winner.User.Mention,
@ -287,7 +287,7 @@ namespace NadekoBot.Modules.Gambling
return;
}
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);
return;

View File

@ -36,13 +36,13 @@ namespace NadekoBot.Modules.Gambling
private string _secretCode = string.Empty;
private readonly DiscordShardedClient _client;
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;
_bc = bc;
_ch = ch;
_cs = cs;
}
[NadekoCommand, Usage, Description, Aliases]
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Gambling
{
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);
try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
@ -137,7 +137,7 @@ namespace NadekoBot.Modules.Gambling
desc, footer: footer)
.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 Logger _log;
private readonly DiscordShardedClient _client;
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
private IUserMessage StartingMessage { get; set; }
private CancellationTokenSource Source { get; }
private CancellationToken CancelToken { get; }
public FlowerReactionEvent(DiscordShardedClient client, CurrencyHandler ch)
public FlowerReactionEvent(DiscordShardedClient client, CurrencyService cs)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
_ch = ch;
_cs = cs;
Source = new CancellationTokenSource();
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))
{
await _ch.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", amount, false)
await _cs.AddAsync(r.User.Value, "Flower Reaction Event", amount, false)
.ConfigureAwait(false);
}
}

View File

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

View File

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

View File

@ -20,8 +20,8 @@ namespace NadekoBot.Modules.Gambling
public class FlowerShop : NadekoSubmodule
{
private readonly BotConfig _bc;
private readonly DbHandler _db;
private readonly CurrencyHandler _ch;
private readonly DbService _db;
private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client;
public enum Role
@ -34,11 +34,11 @@ namespace NadekoBot.Modules.Gambling
List
}
public FlowerShop(BotConfig bc, DbHandler db, CurrencyHandler ch, DiscordShardedClient client)
public FlowerShop(BotConfig bc, DbService db, CurrencyService cs, DiscordShardedClient client)
{
_db = db;
_bc = bc;
_ch = ch;
_cs = cs;
_client = client;
}
@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Gambling
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
{
@ -120,11 +120,11 @@ namespace NadekoBot.Modules.Gambling
catch (Exception 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);
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);
return;
}
@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Gambling
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;
using (var uow = _db.UnitOfWork)
@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Gambling
.AddField(efb => efb.WithName(GetText("name")).WithValue(entry.Name).WithIsInline(true)))
.ConfigureAwait(false);
await _ch.AddCurrencyAsync(entry.AuthorId,
await _cs.AddAsync(entry.AuthorId,
$"Shop sell item - {entry.Name}",
GetProfitAmount(entry.Price)).ConfigureAwait(false);
}
@ -174,7 +174,7 @@ namespace NadekoBot.Modules.Gambling
uow._context.Set<ShopEntryItem>().Add(item);
uow.Complete();
await _ch.AddCurrencyAsync(Context.User.Id,
await _cs.AddAsync(Context.User.Id,
$"Shop error refund - {entry.Name}",
entry.Price,
uow).ConfigureAwait(false);

View File

@ -32,13 +32,13 @@ namespace NadekoBot.Modules.Gambling
//thanks to judge for helping me with this
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;
_bc = bc;
_ch = ch;
_cs = cs;
}
public class SlotMachine
@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Gambling
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);
return;
@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Gambling
var msg = GetText("better_luck");
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);
if (result.Multiplier == 1)
msg = GetText("slot_single", _bc.CurrencySign, 1);

View File

@ -57,10 +57,10 @@ namespace NadekoBot.Modules.Gambling
InsufficientAmount
}
public WaifuClaimCommands(BotConfig bc, CurrencyHandler ch, DbHandler db)
public WaifuClaimCommands(BotConfig bc, CurrencyService cs, DbService db)
{
_bc = bc;
_ch = ch;
_cs = cs;
_db = db;
}
@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Gambling
{
var claimer = uow.DiscordUsers.GetOrCreate(Context.User);
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;
}
@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Gambling
}
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;
}
@ -138,7 +138,7 @@ namespace NadekoBot.Modules.Gambling
}
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;
}
@ -230,13 +230,13 @@ namespace NadekoBot.Modules.Gambling
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);
result = DivorceResult.SucessWithPenalty;
}
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;
}
@ -278,8 +278,8 @@ namespace NadekoBot.Modules.Gambling
private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30);
private readonly BotConfig _bc;
private readonly CurrencyHandler _ch;
private readonly DbHandler _db;
private readonly CurrencyService _cs;
private readonly DbService _db;
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]

View File

@ -13,14 +13,14 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling : NadekoTopLevelModule
{
private readonly BotConfig _bc;
private readonly DbHandler _db;
private readonly CurrencyHandler _currency;
private readonly DbService _db;
private readonly CurrencyService _currency;
private string CurrencyName => _bc.CurrencyName;
private string CurrencyPluralName => _bc.CurrencyPluralName;
private string CurrencySign => _bc.CurrencySign;
public Gambling(BotConfig bc, DbHandler db, CurrencyHandler currency)
public Gambling(BotConfig bc, DbService db, CurrencyService currency)
{
_bc = bc;
_db = db;
@ -71,13 +71,13 @@ namespace NadekoBot.Modules.Gambling
{
if (amount <= 0 || Context.User.Id == receiver.Id)
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)
{
await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
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()))
.ConfigureAwait(false);
}
@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0)
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);
}
@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Gambling
var users = (await Context.Guild.GetUsersAsync())
.Where(u => u.GetRoles().Contains(role))
.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})",
amount)))
.ConfigureAwait(false);
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0)
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);
else
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false);
@ -143,7 +143,7 @@ namespace NadekoBot.Modules.Gambling
if (amount <= 0)
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);
else
await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false);
@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Gambling
if (amount < 1)
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);
return;
@ -227,19 +227,19 @@ namespace NadekoBot.Modules.Gambling
if (rnd < 91)
{
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);
}
else if (rnd < 100)
{
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);
}
else
{
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);
}
}

View File

@ -65,7 +65,7 @@ namespace NadekoBot.Modules.Games
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
{
private readonly ITextChannel _channel;

View File

@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Games
[Group]
public class CleverBotCommands : NadekoSubmodule
{
private readonly DbHandler _db;
private readonly GamesService _games;
private readonly DbService _db;
private readonly ChatterBotService _games;
public CleverBotCommands(DbHandler db, GamesService games)
public CleverBotCommands(DbService db, ChatterBotService games)
{
_db = db;
_games = games;
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games
{
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)
{
@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Games
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)
{

View File

@ -24,16 +24,16 @@ namespace NadekoBot.Modules.Games
[Group]
public class PlantPickCommands : NadekoSubmodule
{
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
private readonly BotConfig _bc;
private readonly GamesService _games;
private readonly DbHandler _db;
private readonly DbService _db;
public PlantPickCommands(BotConfig bc, CurrencyHandler ch, GamesService games,
DbHandler db)
public PlantPickCommands(BotConfig bc, CurrencyService cs, GamesService games,
DbService db)
{
_bc = bc;
_ch = ch;
_cs = cs;
_games = games;
_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 _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)
.ConfigureAwait(false);
msg.DeleteAfter(10);
@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Games
if (amount < 1)
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)
{
await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false);
@ -88,9 +88,9 @@ namespace NadekoBot.Modules.Games
msgToSend += " " + GetText("pick_sn", Prefix);
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];

View File

@ -3,12 +3,8 @@ using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NadekoBot.Services;
using NadekoBot.Services.Games;
namespace NadekoBot.Modules.Games
{
@ -17,12 +13,13 @@ namespace NadekoBot.Modules.Games
[Group]
public class PollCommands : NadekoSubmodule
{
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
private readonly DiscordShardedClient _client;
private readonly PollService _polls;
public PollCommands(DiscordShardedClient client)
public PollCommands(DiscordShardedClient client, PollService polls)
{
_client = client;
_polls = polls;
}
[NadekoCommand, Usage, Description, Aliases]
@ -42,8 +39,7 @@ namespace NadekoBot.Modules.Games
[RequireContext(ContextType.Guild)]
public async Task PollStats()
{
Poll poll;
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
if (!_polls.ActivePolls.TryGetValue(Context.Guild.Id, out var poll))
return;
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)
{
var channel = (ITextChannel)Context.Channel;
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
if(await _polls.StartPoll((ITextChannel)Context.Channel, Context.Message, arg, isPublic) == false)
await ReplyErrorLocalized("poll_already_running").ConfigureAwait(false);
}
@ -75,151 +58,11 @@ namespace NadekoBot.Modules.Games
{
var channel = (ITextChannel)Context.Channel;
Poll poll;
ActivePolls.TryRemove(channel.Guild.Id, out poll);
_polls.ActivePolls.TryRemove(channel.Guild.Id, out var poll);
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.Services.Games;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
@ -71,15 +70,10 @@ namespace NadekoBot.Modules.Games
public async Task Typeadd([Remainder] string text)
{
var channel = (ITextChannel)Context.Channel;
if (string.IsNullOrWhiteSpace(text))
return;
_games.TypingArticles.Add(new TypingArticle
{
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));
_games.AddTypingArticle(Context.User, text);
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 DiscordShardedClient _client;
private readonly BotConfig _bc;
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
public IGuild Guild { get; }
public ITextChannel Channel { get; }
@ -44,14 +44,14 @@ namespace NadekoBot.Modules.Games.Trivia
public int WinRequirement { get; }
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)
{
_log = LogManager.GetCurrentClassLogger();
_strings = strings;
_client = client;
_bc = bc;
_ch = ch;
_cs = cs;
ShowHints = showHints;
Guild = guild;
@ -227,7 +227,7 @@ namespace NadekoBot.Modules.Games.Trivia
}
var reward = _bc.TriviaCurrencyReward;
if (reward > 0)
await _ch.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
await _cs.AddAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
return;
}

View File

@ -17,15 +17,15 @@ namespace NadekoBot.Modules.Games
[Group]
public class TriviaCommands : NadekoSubmodule
{
private readonly CurrencyHandler _ch;
private readonly CurrencyService _cs;
private readonly DiscordShardedClient _client;
private readonly BotConfig _bc;
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;
_bc = bc;
}
@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Games
var showHints = !additionalArgs.Contains("nohint");
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))
{
try

View File

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

View File

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

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions
[Group]
public class CmdCdsCommands : NadekoSubmodule
{
private readonly DbHandler _db;
private readonly DbService _db;
private readonly CmdCdService _service;
private ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns
@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Permissions
private ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> ActiveCooldowns
=> _service.ActiveCooldowns;
public CmdCdsCommands(CmdCdService service, DbHandler db)
public CmdCdsCommands(CmdCdService service, DbService db)
{
_service = service;
_db = db;
@ -88,40 +88,6 @@ namespace NadekoBot.Modules.Permissions
else
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]
public class FilterCommands : NadekoSubmodule
{
private readonly DbHandler _db;
private readonly DbService _db;
private readonly FilterService _service;
public FilterCommands(FilterService service, DbHandler db)
public FilterCommands(FilterService service, DbService db)
{
_service = service;
_db = db;

View File

@ -16,9 +16,9 @@ namespace NadekoBot.Modules.Permissions
public class GlobalPermissionCommands : NadekoSubmodule
{
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;
_db = db;

View File

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

View File

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

View File

@ -15,16 +15,16 @@ namespace NadekoBot.Modules.Pokemon
public class Pokemon : NadekoTopLevelModule
{
private readonly PokemonService _service;
private readonly DbHandler _db;
private readonly DbService _db;
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;
_db = db;
_bc = bc;
_ch = ch;
_cs = cs;
}
private int GetDamage(PokemonType usertype, PokemonType targetType)
@ -229,7 +229,7 @@ namespace NadekoBot.Modules.Pokemon
var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
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);
return;
@ -295,7 +295,7 @@ namespace NadekoBot.Modules.Pokemon
var amount = 1;
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);
return;

View File

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

View File

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

View File

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

View File

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

View File

@ -18,13 +18,13 @@ namespace NadekoBot.Modules.Utility
{
private readonly DiscordShardedClient _client;
private readonly IStatsService _stats;
private readonly CommandHandler _ch;
private readonly CommandHandler _cmdHandler;
public InfoCommands(DiscordShardedClient client, IStatsService stats, CommandHandler ch)
{
_client = client;
_stats = stats;
_ch = ch;
_cmdHandler = ch;
}
[NadekoCommand, Usage, Description, Aliases]
@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Utility
int startCount = page * activityPerPage;
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",
++startCount,
@ -141,7 +141,7 @@ namespace NadekoBot.Modules.Utility
.WithTitle(GetText("activity_page", page + 1))
.WithOkColor()
.WithFooter(efb => efb.WithText(GetText("activity_users_total",
_ch.UserMessagesSent.Count)))
_cmdHandler.UserMessagesSent.Count)))
.WithDescription(str.ToString()));
}
}

View File

@ -22,9 +22,9 @@ namespace NadekoBot.Modules.Utility
{
private readonly MessageRepeaterService _service;
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;
_client = client;

View File

@ -1,19 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Modules.Utility.Models;
using Newtonsoft.Json;
using System.Threading;
using System;
using System.Collections.Immutable;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using Discord;
using NLog;
using NadekoBot.Services.Utility;
namespace NadekoBot.Modules.Utility
{
@ -22,27 +15,26 @@ namespace NadekoBot.Modules.Utility
[Group]
public class PatreonCommands : NadekoSubmodule
{
//todo rename patreon thingy and move it to be a service, or a part of utility service
private readonly PatreonThingy patreon;
private readonly PatreonRewardsService _patreon;
private readonly IBotCredentials _creds;
private readonly BotConfig _config;
private readonly DbHandler _db;
private readonly CurrencyHandler _currency;
private readonly DbService _db;
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;
_config = config;
_db = db;
_currency = currency;
patreon = PatreonThingy.GetInstance(creds, db, currency);
_patreon = p;
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task PatreonRewardsReload()
{
await patreon.LoadPledges().ConfigureAwait(false);
await _patreon.LoadPledges().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("👌").ConfigureAwait(false);
}
@ -60,7 +52,7 @@ namespace NadekoBot.Modules.Utility
int amount = 0;
try
{
amount = await patreon.ClaimReward(Context.User.Id).ConfigureAwait(false);
amount = await _patreon.ClaimReward(Context.User.Id).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -72,7 +64,7 @@ namespace NadekoBot.Modules.Utility
await ReplyConfirmLocalized("clpa_success", amount + _config.CurrencySign).ConfigureAwait(false);
return;
}
var rem = (patreon.Interval - (DateTime.UtcNow - patreon.LastUpdate));
var rem = (_patreon.Interval - (DateTime.UtcNow - _patreon.LastUpdate));
var helpcmd = Format.Code(NadekoBot.Prefix + "donate");
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.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]
public class QuoteCommands : NadekoSubmodule
{
private readonly DbHandler _db;
private readonly DbService _db;
public QuoteCommands(DbHandler db)
public QuoteCommands(DbService db)
{
_db = db;
}

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Utility
public class RemindCommands : NadekoSubmodule
{
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;
_db = db;

View File

@ -494,7 +494,6 @@ namespace NadekoBot.Services.Utility
}
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}"));
}
}

View File

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

View File

@ -29,11 +29,8 @@
<ItemGroup>
<Compile Remove="data\**\*;credentials.json;credentials_example.json" />
<Compile Remove="Modules\Administration\**" />
<Compile Remove="Modules\NSFW\**" />
<EmbeddedResource Remove="Modules\Administration\**" />
<EmbeddedResource Remove="Modules\NSFW\**" />
<None Remove="Modules\Administration\**" />
<None Remove="Modules\NSFW\**" />
<Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" />
<Compile Include="Modules\Administration\Administration.cs" />
<Compile Include="Modules\Administration\Commands\AutoAssignRoleCommands.cs" />
@ -87,6 +84,7 @@
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<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" />
</ItemGroup>
@ -102,5 +100,6 @@
<ItemGroup>
<Folder Include="Modules\Music\Classes\" />
<Folder Include="Modules\Utility\Models\" />
</ItemGroup>
</Project>

View File

@ -15,10 +15,10 @@ namespace NadekoBot.Services.Administration
public readonly ConcurrentHashSet<ulong> GameVoiceChannels = new ConcurrentHashSet<ulong>();
private readonly Logger _log;
private readonly DbHandler _db;
private readonly DbService _db;
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();
_db = db;

View File

@ -26,9 +26,9 @@ namespace NadekoBot.Services.Administration
private readonly Logger _log = LogManager.GetCurrentClassLogger();
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;
_db = db;

View File

@ -1,4 +1,5 @@
using Discord.WebSocket;
using NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models;
using NLog;
@ -6,22 +7,22 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Discord;
using System.Threading.Tasks;
namespace NadekoBot.Services.Administration
{
public class RatelimitService
public class SlowmodeService : IEarlyBlocker
{
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>> IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>();
private readonly Logger _log;
private readonly DiscordShardedClient _client;
public RatelimitService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs)
public SlowmodeService(IEnumerable<GuildConfig> gcs)
{
_log = LogManager.GetCurrentClassLogger();
_client = client;
IgnoredRoles = new ConcurrentDictionary<ulong, HashSet<ulong>>(
gcs.ToDictionary(x => x.GuildId,
@ -30,24 +31,33 @@ namespace NadekoBot.Services.Administration
IgnoredUsers = new ConcurrentDictionary<ulong, HashSet<ulong>>(
gcs.ToDictionary(x => x.GuildId,
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;
var channel = usrMsg?.Channel as SocketTextChannel;
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();
await usrMsg.DeleteAsync();
return true;
}
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
{
private readonly RatelimitService _svc;
private readonly SlowmodeService _svc;
public class RatelimitedUser
{
@ -22,7 +22,7 @@ namespace NadekoBot.Services.Administration
public int MaxMessages { get; set; }
public int PerSeconds { get; set; }
public Ratelimiter(RatelimitService svc)
public Ratelimiter(SlowmodeService 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 Discord.WebSocket;
using System.Collections.Generic;
namespace NadekoBot.Services.Administration
{
public class SelfService
public class SelfService : ILateExecutor
{
public volatile bool ForwardDMs;
public volatile bool ForwardDMsToAllOwners;
private readonly NadekoBot _bot;
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,
BotConfig bc)
public SelfService(DiscordShardedClient client, NadekoBot bot, CommandHandler cmdHandler, DbService db,
BotConfig bc, ILocalization localization, NadekoStrings strings, IBotCredentials creds)
{
_bot = bot;
_cmdHandler = cmdHandler;
_db = db;
_log = LogManager.GetCurrentClassLogger();
_localization = localization;
_strings = strings;
_client = client;
_creds = creds;
using (var uow = _db.UnitOfWork)
{
@ -37,6 +57,112 @@ namespace NadekoBot.Services.Administration
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
{
private readonly Logger _log;
private readonly DbService _db;
private readonly DiscordShardedClient _client;
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();
_db = db;
_client = client;
client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
_client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated;
VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>();
var missingRoles = new List<VcRoleInfo>();
foreach (var gconf in gcs)
{
var g = client.GetGuild(gconf.GuildId);
var g = _client.GetGuild(gconf.GuildId);
if (g == null)
continue; //todo delete everything from db if guild doesn't exist?
continue;
var infos = new ConcurrentDictionary<ulong, IRole>();
VcRoles.TryAdd(gconf.GuildId, infos);
@ -34,11 +39,21 @@ namespace NadekoBot.Services.Administration
{
var role = g.GetRole(ri.RoleId);
if (role == null)
continue; //todo remove this entry from db
{
missingRoles.Add(ri);
continue;
}
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,

View File

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

View File

@ -18,14 +18,14 @@ namespace NadekoBot.Services.ClashOfClans
public class ClashOfClansService
{
private readonly DiscordShardedClient _client;
private readonly DbHandler _db;
private readonly DbService _db;
private readonly ILocalization _localization;
private readonly NadekoStrings _strings;
private readonly Timer checkWarTimer;
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;
_db = db;

View File

@ -87,20 +87,6 @@ namespace NadekoBot.Services
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.MessageUpdated += (oldmsg, newMsg, channel) =>
{
@ -126,80 +112,6 @@ namespace NadekoBot.Services
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 Task LogSuccessfulExecution(IUserMessage usrMsg, bool exec, ITextChannel channel, params int[] execPoints)
@ -233,166 +145,66 @@ namespace NadekoBot.Services
//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<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)
private async 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 (usrMsg == null) //has to be an user message, not system/other messages.
return;
if (usrMsg.Author.Id == 193022505026453504)
return;
if (!(msg is SocketUserMessage usrMsg))
return;
#if !GLOBAL_NADEKO
// track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
// track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
#endif
var channel = msg.Channel as SocketTextChannel;
var guild = channel?.Guild;
var channel = msg.Channel as ISocketMessageChannel;
var guild = (msg.Channel as SocketTextChannel)?.Guild;
await TryRunCommand(guild, channel, usrMsg);
}
catch (Exception ex)
await TryRunCommand(guild, channel, usrMsg);
}
catch (Exception ex)
{
_log.Warn("Error in CommandHandler");
_log.Warn(ex);
if (ex.InnerException != null)
{
_log.Warn("Error in CommandHandler");
_log.Warn(ex);
if (ex.InnerException != null)
{
_log.Warn("Inner Exception of the error in CommandHandler");
_log.Warn(ex.InnerException);
}
_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;
foreach (var svc in _services)
{
if (svc is IBlockingExecutor _executor &&
await _executor.TryExecute(_client, guild, usrMsg))
if (svc is IEarlyBlocker blocker &&
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);
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;
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);
execTime = Environment.TickCount - execTime;
////todo permissions
//if (exec.Result.IsSuccess)
//{
////todo commandHandler
if (exec)
{
// await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false);
// await LogSuccessfulExecution(usrMsg, exec, channel, exec2, exec3, execTime).ConfigureAwait(false);
// return;
//}
await LogSuccessfulExecution(usrMsg, exec, channel as ITextChannel, exec2, exec3, execTime).ConfigureAwait(false);
return;
}
//else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand)
//{
// LogErroredExecution(usrMsg, exec, channel, exec2, exec3, execTime);
@ -453,20 +265,20 @@ namespace NadekoBot.Services
// }
// 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
// users who are voting on private polls (sending a number in a DM)
if (int.TryParse(usrMsg.Content, out int vote)) return;
////todo help
//await usrMsg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
////todo selfcommands
//await SelfCommands.HandleDmForwarding(usrMsg, ownerChannels).ConfigureAwait(false);
if (svc is ILateExecutor exec)
{
await exec.LateExecute(_client, guild, usrMsg).ConfigureAwait(false);
}
}
}
public Task<bool> ExecuteCommandAsync(CommandContext context, int argPos, IServiceProvider serviceProvider, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
@ -540,16 +352,16 @@ namespace NadekoBot.Services
// }
//}
////////future
//////int price;
//////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);
////// if (!success)
////// {
////// return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command."));
////// }
//////}
//////future
////int price;
////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0)
////{
//// var success = await _cs.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false);
//// if (!success)
//// {
//// return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command."));
//// }
////}
}
////todo perms
@ -564,11 +376,18 @@ namespace NadekoBot.Services
// GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id))
return false;
//return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.");
//return SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.");
////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."));
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;
}
}
await commands[i].ExecuteAsync(context, parseResult, serviceProvider);
return true;

View File

@ -7,20 +7,20 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services
{
public class CurrencyHandler
public class CurrencyService
{
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;
_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)
try { await author.SendErrorAsync($"`You lost:` {amount} {_config.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { }
@ -28,12 +28,11 @@ namespace NadekoBot.Services
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)
throw new ArgumentNullException(nameof(amount));
if (uow == null)
{
using (uow = _db.UnitOfWork)
@ -61,15 +60,15 @@ namespace NadekoBot.Services
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)
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)
throw new ArgumentNullException(nameof(amount));

View File

@ -11,7 +11,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Services.CustomReactions
{
public class CustomReactionsService : IBlockingExecutor
public class CustomReactionsService : IEarlyBlockingExecutor
{
public CustomReaction[] GlobalReactions = new 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>();
private readonly Logger _log;
private readonly DbHandler _db;
private readonly DbService _db;
private readonly DiscordShardedClient _client;
public CustomReactionsService(DbHandler db, DiscordShardedClient client)
public CustomReactionsService(DbService db, DiscordShardedClient client)
{
_log = LogManager.GetCurrentClassLogger();
_db = db;
@ -90,16 +90,15 @@ namespace NadekoBot.Services.CustomReactions
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
// todo log custom reaction executions. return struct with info
var cr = await Task.Run(() => TryGetCustomReaction(msg)).ConfigureAwait(false);
if (cr != null) //if it was, don't execute the command
if (cr != null)
{
try
{
//todo permissions
//if (guild != null)
//{
// PermissionCache pc = Permissions.GetCache(guild.Id);
@ -107,7 +106,6 @@ namespace NadekoBot.Services.CustomReactions
// 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);

View File

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

View File

@ -126,10 +126,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.Where(gc => gc.RootPermission != null)
.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++)
{
query = query.ThenInclude(gc => gc.Next);

View File

@ -3,17 +3,15 @@ using NadekoBot.Services.Database;
namespace NadekoBot.Services
{
public class DbHandler
public class DbService
{
private readonly DbContextOptions options;
private string connectionString { get; }
private readonly string _connectionString;
static DbHandler() { }
public DbHandler(IBotCredentials creds)
public DbService(IBotCredentials creds)
{
connectionString = creds.Db.ConnectionString;
_connectionString = creds.Db.ConnectionString;
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
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 Logger _log;
public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
public readonly string TypingArticlesPath = "data/typing_articles2.json";
public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();
@ -52,11 +50,6 @@ namespace NadekoBot.Services.Games
}, 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
client.MessageReceived += PotentialFlowerGeneration;
GenerationChannels = new ConcurrentHashSet<ulong>(gcs
@ -73,55 +66,15 @@ namespace NadekoBot.Services.Games
}
}
public string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot)
public void AddTypingArticle(IUser user, string text)
{
var channel = msg.Channel as ITextChannel;
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))
TypingArticles.Add(new TypingArticle
{
message = msg.Content.Substring(normalMention.Length).Trim();
}
else if (msg.Content.StartsWith(nickMention))
{
message = msg.Content.Substring(nickMention.Length).Trim();
}
else
{
return null;
}
Title = $"Text added on {DateTime.UtcNow} by {user}",
Text = text.SanitizeMentions(),
});
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;
File.WriteAllText(TypingArticlesPath, JsonConvert.SerializeObject(TypingArticles));
}
public ConcurrentHashSet<ulong> GenerationChannels { get; }
@ -130,7 +83,9 @@ namespace NadekoBot.Services.Games
//channelId/last generation
public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
public KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage()
private ConcurrentDictionary<ulong, object> _locks { get; } = new ConcurrentDictionary<ulong, object>();
public (string Name, ImmutableArray<byte> Data) GetRandomCurrencyImage()
{
var rng = new NadekoRandom();
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)
=> _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep);
private Task PotentialFlowerGeneration(SocketMessage imsg)
private async Task PotentialFlowerGeneration(SocketMessage imsg)
{
var msg = imsg as SocketUserMessage;
if (msg == null || msg.Author.IsBot)
return Task.CompletedTask;
return;
var channel = imsg.Channel as ITextChannel;
if (channel == null)
return Task.CompletedTask;
return;
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 rng = new NadekoRandom();
var dropAmount = _bc.CurrencyDropAmount;
//todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup:
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)
if (dropAmount > 0)
{
LastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
var dropAmount = _bc.CurrencyDropAmount;
if (dropAmount > 0)
var msgs = new IUserMessage[dropAmount];
var prefix = NadekoBot.Prefix;
var toSend = dropAmount == 1
? GetText(channel, "curgen_sn", _bc.CurrencySign)
+ " " + 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 prefix = NadekoBot.Prefix;
var toSend = dropAmount == 1
? GetText(channel, "curgen_sn", _bc.CurrencySign)
+ " " + 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);
var sent = await channel.SendFileAsync(
fileStream,
file.Name,
toSend).ConfigureAwait(false);
msgs[0] = sent;
}
PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
msgs[0] = sent;
}
PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; });
}
}
catch (Exception ex)
{
LogManager.GetCurrentClassLogger().Warn(ex);
}
});
return Task.CompletedTask;
}
catch (Exception ex)
{
LogManager.GetCurrentClassLogger().Warn(ex);
}
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
{
private readonly DbHandler _db;
private readonly DbService _db;
public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache;
private readonly DiscordShardedClient _client;
private readonly Logger _log;
public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbHandler db)
public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbService db)
{
_db = db;
_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.Collections.Generic;
using System.Collections.Immutable;
namespace NadekoBot.Services
@ -9,8 +8,8 @@ namespace NadekoBot.Services
ImmutableArray<byte> Heads { get; }
ImmutableArray<byte> Tails { get; }
ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Currency { get; }
ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Dice { get; }
ImmutableArray<(string, ImmutableArray<byte>)> Currency { get; }
ImmutableArray<ImmutableArray<byte>> Dice { get; }
ImmutableArray<byte> SlotBackground { get; }
ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; }

View File

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

View File

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

View File

@ -10,13 +10,13 @@ namespace NadekoBot.Services
public class Localization : ILocalization
{
private readonly Logger _log;
private readonly DbHandler _db;
private readonly DbService _db;
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
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();
_db = db;

View File

@ -61,7 +61,7 @@ namespace NadekoBot.Services
}
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)
{

View File

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

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.Generic;
using System.Linq;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
using NadekoBot.Extensions;
using Discord.Net;
using NLog;
namespace NadekoBot.Services.Permissions
{
public class FilterService
public class FilterService : IEarlyBlocker
{
private readonly Logger _log;
public ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public ConcurrentHashSet<ulong> InviteFilteringServers { get; }
@ -34,6 +43,8 @@ namespace NadekoBot.Services.Permissions
public FilterService(IEnumerable<GuildConfig> gcs)
{
_log = LogManager.GetCurrentClassLogger();
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)));
@ -46,5 +57,61 @@ namespace NadekoBot.Services.Permissions
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 NadekoBot.DataStructures.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
@ -6,25 +7,27 @@ using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Services.Permissions
{
public class PermissionsService
public class PermissionsService : ILateBlocker
{
private readonly DbHandler _db;
private readonly DbService _db;
private readonly Logger _log;
//guildid, root permission
public ConcurrentDictionary<ulong, PermissionCache> Cache { get; } =
new ConcurrentDictionary<ulong, PermissionCache>();
public PermissionsService(DbHandler db)
public PermissionsService(DbService db, BotConfig bc)
{
_log = LogManager.GetCurrentClassLogger();
_db = db;
var sw = Stopwatch.StartNew();
TryMigratePermissions();
TryMigratePermissions(bc);
using (var uow = _db.UnitOfWork)
{
foreach (var x in uow.GuildConfigs.Permissionsv2ForAll())
@ -59,61 +62,67 @@ namespace NadekoBot.Services.Permissions
return pc;
}
private void TryMigratePermissions()
private void TryMigratePermissions(BotConfig bc)
{
var log = LogManager.GetCurrentClassLogger();
using (var uow = _db.UnitOfWork)
{
var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
.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())
if (bc.PermissionVersion <= 1)
{
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
log.Info("Permission version is 1, upgrading to 2.");
var oldCache = new ConcurrentDictionary<ulong, OldPermissionCache>(uow.GuildConfigs
.OldPermissionsForAll()
.Where(x => x.RootPermission != null) // there is a check inside already, but just in case
.ToDictionary(k => k.GuildId,
v => new OldPermissionCache()
{
Permissions = new PermissionsCollection<Permissionv2>(newPerms),
Verbose = gc.VerbosePermissions,
PermRole = gc.PermissionRole,
});
gc.Permissions = newPerms;
RootPermission = v.RootPermission,
Verbose = v.VerbosePermissions,
PermRole = v.PermissionRole
}));
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();
}
}
@ -151,5 +160,39 @@ namespace NadekoBot.Services.Permissions
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 IGoogleApiService _google;
private readonly DbHandler _db;
private readonly DbService _db;
private readonly Logger _log;
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<MagicItem> MagicItems { get; } = new List<MagicItem>();
public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbHandler db)
public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbService db)
{
_client = client;
_google = google;

View File

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

View File

@ -17,9 +17,9 @@ namespace NadekoBot.Services.Utility
private readonly Logger _log;
private Timer _timer;
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();
_db = db;

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Models
namespace NadekoBot.Services.Utility.Patreon
{
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