Add request chains

This commit is contained in:
Basique Evangelist 2019-12-08 16:41:10 +00:00
parent 636040c635
commit f47c48e1be
Signed by untrusted user: BasiqueEvangelist
GPG Key ID: B370219149301706
16 changed files with 456 additions and 409 deletions

View File

@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using Perfusion;
using JetKarmaBot.Services;
using JetKarmaBot.Services.Handling;
using NLog;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@ -16,120 +13,111 @@ namespace JetKarmaBot.Commands
{
public IReadOnlyCollection<string> Names => new[] { "award", "revoke" };
[Inject] private Logger log;
[Inject] private TimeoutManager Timeout;
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
var db = ctx.Database;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
var awarder = args.Message.From;
string awardTypeText = null;
int recipientId = default(int);
foreach (string arg in cmd.Parameters)
var awarder = ctx.EventArgs.Message.From;
string awardTypeText = null;
int recipientId = default(int);
foreach (string arg in ctx.Command.Parameters)
{
if (arg.StartsWith('@'))
{
if (arg.StartsWith('@'))
if (recipientId != default(int))
{
if (recipientId != default(int))
{
await Client.SendTextMessageAsync(args.Message.Chat.Id, currentLocale["jetkarmabot.award.errdup"]);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
recipientId = await db.Users.Where(x => x.Username == arg).Select(x => x.UserId).FirstOrDefaultAsync();
if (recipientId == default(int))
{
await Client.SendTextMessageAsync(args.Message.Chat.Id, currentLocale["jetkarmabot.award.errbadusername"]);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
await ctx.Client.SendTextMessageAsync(ctx.EventArgs.Message.Chat.Id, currentLocale["jetkarmabot.award.errdup"]);
return false;
}
recipientId = await db.Users.Where(x => x.Username == arg).Select(x => x.UserId).FirstOrDefaultAsync();
if (recipientId == default(int))
{
await ctx.Client.SendTextMessageAsync(ctx.EventArgs.Message.Chat.Id, currentLocale["jetkarmabot.award.errbadusername"]);
return false;
}
}
else
{
if (awardTypeText == null)
awardTypeText = arg;
else
{
if (awardTypeText == null)
awardTypeText = arg;
else
{
await Client.SendTextMessageAsync(args.Message.Chat.Id, currentLocale["jetkarmabot.award.errdup"]);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
await ctx.Client.SendTextMessageAsync(ctx.EventArgs.Message.Chat.Id, currentLocale["jetkarmabot.award.errdup"]);
return false;
}
}
if (args.Message.ReplyToMessage != null && recipientId == default(int))
{
recipientId = args.Message.ReplyToMessage.From.Id;
}
if (recipientId == default(int))
{
await Client.SendTextMessageAsync(args.Message.Chat.Id, currentLocale["jetkarmabot.award.errawardnoreply"]);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
bool awarding = cmd.Command == "award";
if (awarder.Id == recipientId)
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.award.errawardself"],
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
if (CommandRouter.Me.Id == recipientId)
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
awarding
? currentLocale["jetkarmabot.award.errawardbot"]
: currentLocale["jetkarmabot.award.errrevokebot"],
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("AwardFailure", awarder.Id, db);
return true;
}
var text = args.Message.Text;
global::JetKarmaBot.Models.AwardType awardType = awardTypeText != null
? await db.AwardTypes.FirstAsync(at => at.CommandName == awardTypeText)
: await db.AwardTypes.FindAsync((sbyte)1);
await db.Awards.AddAsync(new Models.Award()
{
AwardTypeId = awardType.AwardTypeId,
Amount = (sbyte)(awarding ? 1 : -1),
FromId = awarder.Id,
ToId = recipientId,
ChatId = args.Message.Chat.Id
});
await db.SaveChangesAsync();
var recUserName = (await db.Users.FindAsync(recipientId)).Username;
log.Debug($"Awarded {(awarding ? 1 : -1)}{awardType.Symbol} to {recUserName}");
string message = awarding
? string.Format(currentLocale["jetkarmabot.award.awardmessage"], getLocalizedName(awardType, currentLocale), recUserName)
: string.Format(currentLocale["jetkarmabot.award.revokemessage"], getLocalizedName(awardType, currentLocale), recUserName);
var currentCount = await db.Awards
.Where(aw => aw.ToId == recipientId && aw.AwardTypeId == awardType.AwardTypeId && aw.ChatId == args.Message.Chat.Id)
.SumAsync(aw => aw.Amount);
var response = message + "\n" + String.Format(currentLocale["jetkarmabot.award.statustext"], recUserName, currentCount, awardType.Symbol);
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
response,
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("AwardSuccess", awarder.Id, db);
return true;
}
if (ctx.EventArgs.Message.ReplyToMessage != null && recipientId == default(int))
{
recipientId = ctx.EventArgs.Message.ReplyToMessage.From.Id;
}
if (recipientId == default(int))
{
await ctx.Client.SendTextMessageAsync(ctx.EventArgs.Message.Chat.Id, currentLocale["jetkarmabot.award.errawardnoreply"]);
return false;
}
bool awarding = ctx.Command.Command == "award";
if (awarder.Id == recipientId)
{
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
currentLocale["jetkarmabot.award.errawardself"],
replyToMessageId: ctx.EventArgs.Message.MessageId);
return false;
}
if (ctx.GetFeature<ChatCommandRouter.Feature>().Router.Me.Id == recipientId)
{
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
awarding
? currentLocale["jetkarmabot.award.errawardbot"]
: currentLocale["jetkarmabot.award.errrevokebot"],
replyToMessageId: ctx.EventArgs.Message.MessageId);
return false;
}
var text = ctx.EventArgs.Message.Text;
global::JetKarmaBot.Models.AwardType awardType = awardTypeText != null
? await db.AwardTypes.FirstAsync(at => at.CommandName == awardTypeText)
: await db.AwardTypes.FindAsync((sbyte)1);
var prevCount = await db.Awards
.Where(aw => aw.ToId == recipientId && aw.AwardTypeId == awardType.AwardTypeId && aw.ChatId == ctx.EventArgs.Message.Chat.Id)
.SumAsync(aw => aw.Amount);
await db.Awards.AddAsync(new Models.Award()
{
AwardTypeId = awardType.AwardTypeId,
Amount = (sbyte)(awarding ? 1 : -1),
FromId = awarder.Id,
ToId = recipientId,
ChatId = ctx.EventArgs.Message.Chat.Id
});
var recUserName = (await db.Users.FindAsync(recipientId)).Username;
log.Debug($"Awarded {(awarding ? 1 : -1)}{awardType.Symbol} to {recUserName}");
string message = awarding
? string.Format(currentLocale["jetkarmabot.award.awardmessage"], getLocalizedName(awardType, currentLocale), recUserName)
: string.Format(currentLocale["jetkarmabot.award.revokemessage"], getLocalizedName(awardType, currentLocale), recUserName);
var response = message + "\n" + String.Format(currentLocale["jetkarmabot.award.statustext"], recUserName, prevCount + (awarding ? 1 : -1), awardType.Symbol);
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
response,
replyToMessageId: ctx.EventArgs.Message.MessageId);
return true;
}
private string getLocalizedName(global::JetKarmaBot.Models.AwardType awardType, Locale loc)
@ -144,10 +132,7 @@ namespace JetKarmaBot.Commands
}
}
[Inject] KarmaContextFactory Db { get; set; }
[Inject] TelegramBotClient Client { get; set; }
[Inject] Localization Locale { get; set; }
[Inject] ChatCommandRouter CommandRouter { get; set; }
public string Description => "Awards/revokes an award to a user.";
public string DescriptionID => "jetkarmabot.award.help";

View File

@ -1,8 +1,6 @@
using System.Collections.Generic;
using Telegram.Bot;
using Telegram.Bot.Args;
using Perfusion;
using JetKarmaBot.Services;
using JetKarmaBot.Services.Handling;
using NLog;
using System.Linq;
using System.Threading.Tasks;
@ -13,76 +11,68 @@ namespace JetKarmaBot.Commands
{
public IReadOnlyCollection<string> Names => new[] { "changelocale", "locale" };
[Inject] private Logger log;
[Inject] private TimeoutManager Timeout;
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
var db = ctx.Database;
var cmd = ctx.Command;
var args = ctx.EventArgs;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
if (cmd.Parameters.Length < 1)
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
if (cmd.Parameters.Length < 1)
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.getlocale"],
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("LocaleFailure", args.Message.From.Id, db);
return true;
}
else if (cmd.Parameters[0] == "list")
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.listalltext"] + "\n"
+ string.Join("\n", Locale.Select(a => a.Key)),
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("LocaleFailure", args.Message.From.Id, db);
return true;
}
else if (cmd.Parameters[0] == "all")
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.errorall"],
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("LocaleFailure", args.Message.From.Id, db);
return true;
}
string localeId;
if (Locale.ContainsLocale(cmd.Parameters[0]))
localeId = cmd.Parameters[0];
else
try
{
localeId = Locale.FindByCommonName(cmd.Parameters[0]).Name;
}
catch (LocalizationException e)
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.toomany"] + "\n" + string.Join("\n", (e.Data["LocaleNames"] as Locale[]).Select(x => x.Name)),
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("LocaleFailure", args.Message.From.Id, db);
return true;
}
(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale = localeId;
log.Debug($"Changed language of chat {args.Message.Chat.Id} to {localeId}");
await db.SaveChangesAsync();
currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
(currentLocale.HasNote ? currentLocale["jetkarmabot.changelocale.beforenote"] + currentLocale.Note + "\n" : "")
+ currentLocale["jetkarmabot.changelocale.justchanged"],
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("LocaleSuccess", args.Message.From.Id, db);
return true;
await ctx.Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.getlocale"],
replyToMessageId: args.Message.MessageId);
return false;
}
else if (cmd.Parameters[0] == "list")
{
await ctx.Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.listalltext"] + "\n"
+ string.Join("\n", Locale.Select(a => a.Key)),
replyToMessageId: args.Message.MessageId);
return false;
}
else if (cmd.Parameters[0] == "all")
{
await ctx.Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.errorall"],
replyToMessageId: args.Message.MessageId);
return false;
}
string localeId;
if (Locale.ContainsLocale(cmd.Parameters[0]))
localeId = cmd.Parameters[0];
else
try
{
localeId = Locale.FindByCommonName(cmd.Parameters[0]).Name;
}
catch (LocalizationException e)
{
await ctx.Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.changelocale.toomany"] + "\n" + string.Join("\n", (e.Data["LocaleNames"] as Locale[]).Select(x => x.Name)),
replyToMessageId: args.Message.MessageId);
return false;
}
(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale = localeId;
log.Debug($"Changed language of chat {args.Message.Chat.Id} to {localeId}");
currentLocale = Locale[localeId];
await ctx.Client.SendTextMessageAsync(
args.Message.Chat.Id,
(currentLocale.HasNote ? currentLocale["jetkarmabot.changelocale.beforenote"] + currentLocale.Note + "\n" : "")
+ currentLocale["jetkarmabot.changelocale.justchanged"],
replyToMessageId: args.Message.MessageId);
return true;
}
[Inject] KarmaContextFactory Db { get; set; }
[Inject] TelegramBotClient Client { get; set; }
[Inject] Localization Locale { get; set; }
public string Description => "Switches current chat locale to [locale]";

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace JetKarmaBot.Commands
@ -40,7 +38,7 @@ namespace JetKarmaBot.Commands
string username = match.Groups["name"].Captures.Count > 0 ? match.Groups["name"].Captures[0].Value : null;
string[] parameters = words.Skip(1).ToArray();
result = new CommandString(cmd, parameters) { UserName = username};
result = new CommandString(cmd, parameters) { UserName = username };
return true;
}

View File

@ -1,8 +1,6 @@
using System.Collections.Generic;
using Telegram.Bot.Args;
using Perfusion;
using JetKarmaBot.Services;
using Telegram.Bot;
using JetKarmaBot.Services.Handling;
using Telegram.Bot.Types.Enums;
using System.Linq;
using System.Threading.Tasks;
@ -12,10 +10,7 @@ namespace JetKarmaBot.Commands
{
public class CurrenciesCommand : IChatCommand
{
[Inject] KarmaContextFactory Db;
[Inject] TelegramBotClient Client { get; set; }
[Inject] Localization Locale { get; set; }
[Inject] TimeoutManager Timeout { get; set; }
public IReadOnlyCollection<string> Names => new[] { "currencies", "awardtypes" };
public string Description => "Shows all award types";
@ -24,21 +19,18 @@ namespace JetKarmaBot.Commands
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
};
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
string resp = currentLocale["jetkarmabot.currencies.listtext"] + "\n" + string.Join("\n",
(await db.AwardTypes.ToListAsync()).Select(x => $"{x.Symbol} ({x.CommandName}) <i>{currentLocale["jetkarmabot.awardtypes.nominative." + x.CommandName]}</i>"));
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
resp,
replyToMessageId: args.Message.MessageId,
parseMode: ParseMode.Html);
await Timeout.ApplyCost("Currencies", args.Message.From.Id, db);
return true;
}
var db = ctx.Database;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
string resp = currentLocale["jetkarmabot.currencies.listtext"] + "\n" + string.Join("\n",
(await db.AwardTypes.ToListAsync()).Select(x => $"{x.Symbol} ({x.CommandName}) <i>{currentLocale["jetkarmabot.awardtypes.nominative." + x.CommandName]}</i>"));
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
resp,
replyToMessageId: ctx.EventArgs.Message.MessageId,
parseMode: ParseMode.Html);
return true;
}
}
}

View File

@ -1,8 +1,6 @@
using System.Collections.Generic;
using Telegram.Bot.Args;
using Perfusion;
using JetKarmaBot.Services;
using Telegram.Bot;
using JetKarmaBot.Services.Handling;
using Telegram.Bot.Types.Enums;
using System.Threading.Tasks;
@ -10,11 +8,7 @@ namespace JetKarmaBot.Commands
{
public class HelpCommand : IChatCommand
{
[Inject] KarmaContextFactory Db;
[Inject] TelegramBotClient Client { get; set; }
[Inject] Localization Locale { get; set; }
[Inject] TimeoutManager Timeout { get; set; }
[Inject] ChatCommandRouter Router;
public IReadOnlyCollection<string> Names => new[] { "help" };
public string Description => "Displays help text for all(one) command(s)";
@ -30,30 +24,28 @@ namespace JetKarmaBot.Commands
}
};
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
var db = ctx.Database;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
var router = ctx.GetFeature<ChatCommandRouter.Feature>().Router;
if (ctx.Command.Parameters.Length < 1)
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
await Timeout.ApplyCost("Help", args.Message.From.Id, db);
if (cmd.Parameters.Length < 1)
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
Router.GetHelpText(currentLocale),
replyToMessageId: args.Message.MessageId,
parseMode: ParseMode.Html);
return true;
}
else
{
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
Router.GetHelpTextFor(cmd.Parameters[0], currentLocale),
replyToMessageId: args.Message.MessageId,
parseMode: ParseMode.Html);
return true;
}
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
router.GetHelpText(currentLocale),
replyToMessageId: ctx.EventArgs.Message.MessageId,
parseMode: ParseMode.Html);
return true;
}
else
{
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
router.GetHelpTextFor(ctx.Command.Parameters[0], currentLocale),
replyToMessageId: ctx.EventArgs.Message.MessageId,
parseMode: ParseMode.Html);
return true;
}
}
}

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Telegram.Bot.Args;
using JetKarmaBot.Services.Handling;
namespace JetKarmaBot.Commands
{
@ -11,7 +11,7 @@ namespace JetKarmaBot.Commands
string DescriptionID { get; }
IReadOnlyCollection<ChatCommandArgument> Arguments { get; }
Task<bool> Execute(CommandString cmd, MessageEventArgs messageEventArgs);
Task<bool> Execute(RequestContext ctx);
}
public struct ChatCommandArgument

View File

@ -1,15 +1,9 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Perfusion;
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using JetKarmaBot.Models;
using JetKarmaBot.Services;
using JetKarmaBot.Services.Handling;
namespace JetKarmaBot.Commands
{
@ -17,48 +11,42 @@ namespace JetKarmaBot.Commands
{
public IReadOnlyCollection<string> Names => new[] { "leaderboard" };
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
var asker = args.Message.From;
var awardTypeName = cmd.Parameters.FirstOrDefault();
var db = ctx.Database;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
var asker = ctx.EventArgs.Message.From;
var awardTypeName = ctx.Command.Parameters.FirstOrDefault();
string response;
string response;
if (string.IsNullOrWhiteSpace(awardTypeName))
awardTypeName = "star";
if (string.IsNullOrWhiteSpace(awardTypeName))
awardTypeName = "star";
var awardTypeIdQuery = from awt in db.AwardTypes
where awt.CommandName == awardTypeName
select awt.AwardTypeId;
var awardTypeId = await awardTypeIdQuery.FirstAsync();
var awardType = await db.AwardTypes.FindAsync(awardTypeId);
var awardTypeIdQuery = from awt in db.AwardTypes
where awt.CommandName == awardTypeName
select awt.AwardTypeId;
var awardTypeId = await awardTypeIdQuery.FirstAsync();
var awardType = await db.AwardTypes.FindAsync(awardTypeId);
response = string.Format(currentLocale["jetkarmabot.leaderboard.specifictext"], awardType.Symbol) + "\n" + string.Join('\n',
await Task.WhenAll((await db.Awards
.Where(x => x.ChatId == args.Message.Chat.Id && x.AwardTypeId == awardTypeId)
.GroupBy(x => x.ToId)
.Select(x => new { UserId = x.Key, Amount = x.Sum(y => y.Amount) })
.OrderByDescending(x => x.Amount)
.Take(5)
.ToListAsync())
.Select(async (x, index) => $"{index + 1}. {(await db.Users.FindAsync(x.UserId)).Username} - {x.Amount}"))
);
response = string.Format(currentLocale["jetkarmabot.leaderboard.specifictext"], awardType.Symbol) + "\n" + string.Join('\n',
await Task.WhenAll((await db.Awards
.Where(x => x.ChatId == ctx.EventArgs.Message.Chat.Id && x.AwardTypeId == awardTypeId)
.GroupBy(x => x.ToId)
.Select(x => new { UserId = x.Key, Amount = x.Sum(y => y.Amount) })
.OrderByDescending(x => x.Amount)
.Take(5)
.ToListAsync())
.Select(async (x, index) => $"{index + 1}. {(await db.Users.FindAsync(x.UserId)).Username} - {x.Amount}"))
);
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
response,
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("Leaderboard", args.Message.From.Id, db);
return true;
}
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
response,
replyToMessageId: ctx.EventArgs.Message.MessageId);
return true;
}
[Inject] KarmaContextFactory Db { get; set; }
[Inject] TelegramBotClient Client { get; set; }
[Inject] TimeoutManager Timeout { get; set; }
[Inject] Localization Locale { get; set; }
public string Description => "Shows the people with the most of a specific award.";

View File

@ -1,13 +1,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Perfusion;
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using JetKarmaBot.Models;
using JetKarmaBot.Services;
using JetKarmaBot.Services.Handling;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
@ -17,68 +11,62 @@ namespace JetKarmaBot.Commands
{
public IReadOnlyCollection<string> Names => new[] { "status" };
public async Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public async Task<bool> Execute(RequestContext ctx)
{
using (var db = Db.GetContext())
var db = ctx.Database;
var currentLocale = Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
var asker = ctx.EventArgs.Message.From;
var awardTypeName = ctx.Command.Parameters.FirstOrDefault();
bool isPrivate = ctx.EventArgs.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
string response;
if (string.IsNullOrWhiteSpace(awardTypeName))
{
var currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
var asker = args.Message.From;
var awardTypeName = cmd.Parameters.FirstOrDefault();
bool isPrivate = args.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
string response;
if (string.IsNullOrWhiteSpace(awardTypeName))
{
// var awards = db.Awards.Where(x => x.ToId == asker.Id)
// .GroupBy(x => x.AwardTypeId)
// .Select(x => new { AwardTypeId = x.Key, Amount = x.Sum(y => y.Amount) });
if (!db.Awards.Any(x => x.ToId == asker.Id && (x.ChatId == args.Message.Chat.Id || isPrivate)))
response = currentLocale["jetkarmabot.status.havenothing"];
else
{
var awardsQuery = from award in db.Awards
where award.ToId == asker.Id && (award.ChatId == args.Message.Chat.Id || isPrivate)
group award by award.AwardTypeId into g
join awardType in db.AwardTypes
on g.Key equals awardType.AwardTypeId
select new { AwardTypeId = g.Key, AwardTypeSymbol = awardType.Symbol, Amount = g.Sum(x => x.Amount) };
var awardsByType = await awardsQuery.ToListAsync();
response = currentLocale["jetkarmabot.status.listalltext"] + "\n"
+ string.Join("\n",
awardsByType.Select(a => $" - {a.AwardTypeSymbol} {a.Amount}")
);
}
}
// var awards = db.Awards.Where(x => x.ToId == asker.Id)
// .GroupBy(x => x.AwardTypeId)
// .Select(x => new { AwardTypeId = x.Key, Amount = x.Sum(y => y.Amount) });
if (!db.Awards.Any(x => x.ToId == asker.Id && (x.ChatId == ctx.EventArgs.Message.Chat.Id || isPrivate)))
response = currentLocale["jetkarmabot.status.havenothing"];
else
{
var awardTypeIdQuery = from awt in db.AwardTypes
where awt.CommandName == awardTypeName
select awt.AwardTypeId;
var awardTypeId = await awardTypeIdQuery.FirstAsync();
var awardType = await db.AwardTypes.FindAsync(awardTypeId);
var awardsQuery = from award in db.Awards
where award.ToId == asker.Id && (award.ChatId == ctx.EventArgs.Message.Chat.Id || isPrivate)
group award by award.AwardTypeId into g
join awardType in db.AwardTypes
on g.Key equals awardType.AwardTypeId
select new { AwardTypeId = g.Key, AwardTypeSymbol = awardType.Symbol, Amount = g.Sum(x => x.Amount) };
var awardsByType = await awardsQuery.ToListAsync();
response = currentLocale["jetkarmabot.status.listalltext"] + "\n"
+ string.Join("\n",
awardsByType.Select(a => $" - {a.AwardTypeSymbol} {a.Amount}")
);
response = string.Format(currentLocale["jetkarmabot.status.listspecifictext"],
await db.Awards.Where(
x => x.AwardTypeId == awardTypeId
&& x.ToId == asker.Id
&& x.ChatId == args.Message.Chat.Id)
.SumAsync(x => x.Amount), awardType.Symbol);
}
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
response,
replyToMessageId: args.Message.MessageId);
await Timeout.ApplyCost("Status", args.Message.From.Id, db);
return true;
}
else
{
var awardTypeIdQuery = from awt in db.AwardTypes
where awt.CommandName == awardTypeName
select awt.AwardTypeId;
var awardTypeId = await awardTypeIdQuery.FirstAsync();
var awardType = await db.AwardTypes.FindAsync(awardTypeId);
response = string.Format(currentLocale["jetkarmabot.status.listspecifictext"],
await db.Awards.Where(
x => x.AwardTypeId == awardTypeId
&& x.ToId == asker.Id
&& x.ChatId == ctx.EventArgs.Message.Chat.Id)
.SumAsync(x => x.Amount), awardType.Symbol);
}
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
response,
replyToMessageId: ctx.EventArgs.Message.MessageId);
return true;
}
[Inject] KarmaContextFactory Db { get; set; }
[Inject] TelegramBotClient Client { get; set; }
[Inject] TimeoutManager Timeout { get; set; }
[Inject] Localization Locale { get; set; }
public string Description => "Shows the amount of awards that you have";

View File

@ -27,8 +27,8 @@ namespace JetKarmaBot
public int DebtLimitSeconds { get; private set; } = 60 * 60 * 2;
public Dictionary<string, int> CommandCostsSeconds { get; private set; } = new Dictionary<string, int>()
{
{"AwardSuccessful", 60*15},
{"AwardFailed", 60*5},
{"JetKarmaBot.Commands.AwardCommand (OK)", 60*15},
{"JetKarmaBot.Commands.AwardCommand (ERR)", 60*5},
{"Default", 60*5}
};
public int SaveIntervalSeconds { get; private set; } = 60 * 5;

View File

@ -1,16 +1,15 @@
using JetKarmaBot.Commands;
using JetKarmaBot.Models;
using JetKarmaBot.Services;
using JetKarmaBot.Services.Handling;
using Perfusion;
using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Args;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace JetKarmaBot
@ -25,6 +24,7 @@ namespace JetKarmaBot
TelegramBotClient Client { get; set; }
ChatCommandRouter Commands;
RequestChain Chain;
Task timeoutWaitTask;
CancellationTokenSource timeoutWaitTaskToken;
@ -44,6 +44,7 @@ namespace JetKarmaBot
timeoutWaitTask = Timeout.SaveLoop(timeoutWaitTaskToken.Token);
await InitCommands(Container);
InitChain(Container);
Client.OnMessage += BotOnMessageReceived;
Client.StartReceiving();
@ -78,48 +79,12 @@ namespace JetKarmaBot
{
using (KarmaContext db = Db.GetContext())
{
await AddUserToDatabase(db, args.Message.From);
var checkResult = await Timeout.Check(args.Message.From.Id, db);
if (checkResult == TimeoutManager.CheckResult.Limited)
{
Locale currentLocale = Locale[(await db.Chats.FindAsync(args.Message.Chat.Id)).Locale];
await Client.SendTextMessageAsync(
args.Message.Chat.Id,
currentLocale["jetkarmabot.ratelimit"],
replyToMessageId: args.Message.MessageId);
await Timeout.SetMessaged(args.Message.From.Id, db);
return;
}
else if (checkResult != TimeoutManager.CheckResult.NonLimited)
{
return;
}
if (args.Message.ReplyToMessage != null)
await AddUserToDatabase(db, args.Message.ReplyToMessage.From);
if (!db.Chats.Any(x => x.ChatId == args.Message.Chat.Id))
db.Chats.Add(new Models.Chat
{
ChatId = args.Message.Chat.Id
});
await db.SaveChangesAsync();
RequestContext ctx = new RequestContext(Client, args, cmd, db);
await Chain.Handle(ctx);
}
await Commands.Execute(cmd, args);
});
}
private async Task AddUserToDatabase(KarmaContext db, Telegram.Bot.Types.User u)
{
string un;
if (u.Username == null)
un = u.FirstName + (u.LastName != null ? " " + u.LastName : "");
else
un = "@" + u.Username;
if (!db.Users.Any(x => x.UserId == u.Id))
await db.Users.AddAsync(new Models.User { UserId = u.Id, Username = un });
else
(await db.Users.FindAsync(u.Id)).Username = un;
}
async Task InitCommands(IContainer c)
{
c.Add<HelpCommand>();
@ -136,6 +101,15 @@ namespace JetKarmaBot
}
}
void InitChain(IContainer c)
{
Chain = new RequestChain();
Chain.Add(new DbHandler(DbHandler.SaveType.From | DbHandler.SaveType.LateDbChanges));
Chain.Add(Timeout);
Chain.Add(new DbHandler(DbHandler.SaveType.To | DbHandler.SaveType.Chat));
Chain.Add(Commands);
}
#endregion
#region IDisposable

View File

@ -11,7 +11,7 @@ namespace JetKarmaBot.Models
}
public long ChatId { get; set; }
public string Locale { get; set; }
public string Locale { get; set; } = "ru-RU";
public bool IsAdministrator { get; set; }
public virtual ICollection<Award> Awards { get; set; }

View File

@ -1,6 +1,4 @@
using JetKarmaBot.Commands;
using JetKarmaBot.Services;
using Microsoft.EntityFrameworkCore;
using NLog;
using Perfusion;
using System;
@ -8,12 +6,17 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Args;
namespace JetKarmaBot
namespace JetKarmaBot.Services.Handling
{
public class ChatCommandRouter
public class ChatCommandRouter : IRequestHandler
{
public class Feature
{
public Type CommandType;
public ChatCommandRouter Router;
public bool Succeded;
}
public Telegram.Bot.Types.User Me { get; private set; }
[Inject] private Logger log;
[Inject] private TelegramBotClient Client { get; set; }
@ -23,16 +26,21 @@ namespace JetKarmaBot
Me = await Client.GetMeAsync();
}
public Task<bool> Execute(CommandString cmd, MessageEventArgs args)
public Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
{
log.Debug("Message received");
CommandString cmd = ctx.Command;
Feature feature = new Feature() { Router = this };
ctx.Features.Add(feature);
try
{
if (commands.ContainsKey(cmd.Command))
{
log.Debug($"Handling message via {commands[cmd.Command].GetType().Name}");
return commands[cmd.Command].Execute(cmd, args);
feature.CommandType = commands[cmd.Command].GetType();
log.Debug($"Handling message via {feature.CommandType.Name}");
async Task processCommand() => feature.Succeded = await commands[cmd.Command].Execute(ctx);
return processCommand();
}
}
catch (Exception e)
@ -41,7 +49,7 @@ namespace JetKarmaBot
log.Error(e);
}
return Task.FromResult(false);
return next(ctx);
}
public void Add(IChatCommand c)

View File

@ -0,0 +1,54 @@
using System;
using System.Threading.Tasks;
using JetKarmaBot.Models;
using Microsoft.EntityFrameworkCore;
namespace JetKarmaBot.Services.Handling
{
public class DbHandler : IRequestHandler
{
[Flags]
public enum SaveType
{
From = 1 << 0,
To = 1 << 1,
Chat = 1 << 2,
LateDbChanges = 1 << 3
}
private SaveType type;
public DbHandler(SaveType type)
{
this.type = type;
}
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
{
KarmaContext db = ctx.Database;
if (type.HasFlag(SaveType.From))
await AddUserToDatabase(db, ctx.EventArgs.Message.From);
if (type.HasFlag(SaveType.To))
if (ctx.EventArgs.Message.ReplyToMessage != null)
await AddUserToDatabase(db, ctx.EventArgs.Message.ReplyToMessage.From);
if (type.HasFlag(SaveType.Chat))
if (!await db.Chats.AnyAsync(x => x.ChatId == ctx.EventArgs.Message.Chat.Id))
db.Chats.Add(new Models.Chat
{
ChatId = ctx.EventArgs.Message.Chat.Id
});
await next(ctx);
if (type.HasFlag(SaveType.LateDbChanges))
await db.SaveChangesAsync();
}
private async Task AddUserToDatabase(KarmaContext db, Telegram.Bot.Types.User u)
{
string un;
if (u.Username == null)
un = u.FirstName + (u.LastName != null ? " " + u.LastName : "");
else
un = "@" + u.Username;
if (!await db.Users.AnyAsync(x => x.UserId == u.Id))
await db.Users.AddAsync(new Models.User { UserId = u.Id, Username = un });
else
(await db.Users.FindAsync(u.Id)).Username = un;
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace JetKarmaBot.Services.Handling
{
public interface IRequestHandler
{
Task Handle(RequestContext ctx, Func<RequestContext, Task> next);
}
public class RequestChain : IRequestHandler
{
List<IRequestHandler> handlerStack = new List<IRequestHandler>();
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next = null)
{
int i = 0;
Func<RequestContext, Task> chainNext = null;
chainNext = (newCtx) =>
{
if (i == handlerStack.Count) return Task.CompletedTask;
IRequestHandler handler = handlerStack[i++];
return handler.Handle(newCtx, chainNext);
};
await chainNext(ctx);
if (next != null)
await next(ctx);
}
public void Add(IRequestHandler handler)
{
handlerStack.Add(handler);
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetKarmaBot.Commands;
using JetKarmaBot.Models;
using Telegram.Bot;
using Telegram.Bot.Args;
namespace JetKarmaBot.Services.Handling
{
public class RequestContext : IServiceProvider
{
public ITelegramBotClient Client { get; }
public MessageEventArgs EventArgs { get; }
public CommandString Command { get; }
public KarmaContext Database { get; }
public ICollection<object> Features { get; } = new List<object>();
public RequestContext(ITelegramBotClient client, MessageEventArgs args, CommandString cmd, KarmaContext db)
{
Client = client;
EventArgs = args;
Command = cmd;
Database = db;
}
public object GetService(Type serviceType) => Features.First(x => x.GetType() == serviceType);
public T GetFeature<T>() => (T)Features.First(x => x is T);
}
}

View File

@ -4,11 +4,12 @@ using System;
using System.Threading.Tasks;
using JetKarmaBot.Models;
using System.Threading;
using System.Linq;
namespace JetKarmaBot.Services
namespace JetKarmaBot.Services.Handling
{
[Singleton]
public class TimeoutManager
public class TimeoutManager : IRequestHandler
{
public struct TimeoutStats
{
@ -17,14 +18,16 @@ namespace JetKarmaBot.Services
}
[Inject] private KarmaContextFactory Db;
[Inject] private Config cfg;
[Inject] private Localization Locale;
public Dictionary<int, TimeoutStats> TimeoutCache = new Dictionary<int, TimeoutStats>();
public async Task ApplyCost(string name, int uid, KarmaContext db)
private async Task ApplyCost(string name, bool succeded, int uid, KarmaContext db)
{
if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name, out var costSeconds))
if (!cfg.Timeout.CommandCostsSeconds.TryGetValue("Default", out costSeconds))
{
throw new LocalizationException("Default key not present");
}
if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name + (succeded ? " (OK)" : "(ERR)"), out var costSeconds))
if (!cfg.Timeout.CommandCostsSeconds.TryGetValue(name, out costSeconds))
if (!cfg.Timeout.CommandCostsSeconds.TryGetValue("Default", out costSeconds))
{
throw new LocalizationException("Default key not present");
}
await PopulateStats(uid, db);
DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds);
if (TimeoutCache[uid].CooldownDate >= debtLimit)
@ -37,23 +40,7 @@ namespace JetKarmaBot.Services
};
TimeoutCache[uid] = TimeoutCache[uid];
}
public enum CheckResult
{
NonLimited, Limited, LimitedSent
}
public async Task<CheckResult> Check(int uid, KarmaContext db)
{
await PopulateStats(uid, db);
DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds);
return TimeoutCache[uid].CooldownDate < debtLimit
? CheckResult.NonLimited
: (TimeoutCache[uid].TimeoutMessaged ? CheckResult.LimitedSent : CheckResult.Limited);
}
public async Task SetMessaged(int uid, KarmaContext db)
{
await PopulateStats(uid, db);
TimeoutCache[uid] = new TimeoutStats() { TimeoutMessaged = true, CooldownDate = TimeoutCache[uid].CooldownDate };
}
private async Task PopulateStats(int uid, KarmaContext db)
{
if (!TimeoutCache.ContainsKey(uid))
@ -83,5 +70,35 @@ namespace JetKarmaBot.Services
await Save(ct);
}
}
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
{
int uid = ctx.EventArgs.Message.From.Id;
await PopulateStats(uid, ctx.Database);
DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds);
if (debtLimit < TimeoutCache[uid].CooldownDate)
{
if (!TimeoutCache[uid].TimeoutMessaged)
{
Locale currentLocale = Locale[(await ctx.Database.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id)).Locale];
await ctx.Client.SendTextMessageAsync(
ctx.EventArgs.Message.Chat.Id,
currentLocale["jetkarmabot.ratelimit"],
replyToMessageId: ctx.EventArgs.Message.MessageId);
TimeoutCache[uid] = new TimeoutStats() { TimeoutMessaged = true, CooldownDate = TimeoutCache[uid].CooldownDate };
}
return;
}
await next(ctx);
var routerFeature = ctx.GetFeature<ChatCommandRouter.Feature>();
await ApplyCost(getTypeName(routerFeature.CommandType), routerFeature.Succeded, uid, ctx.Database);
}
private string getTypeName(Type t)
{
return (t.DeclaringType == null ? t.Namespace + "." + t.Name : getTypeName(t.DeclaringType) + "." + t.Name)
+ (t.GenericTypeArguments.Length > 0 ? "<" + string.Join(",", t.GenericTypeArguments.Select(getTypeName)) + ">" : "");
}
}
}