mirror of
https://github.com/Jetsparrow/karmabot.git
synced 2026-01-21 00:56:09 +03:00
Usings and namespace cleanup
This commit is contained in:
parent
d2e8ed4d69
commit
2cc286ad9c
@ -1,162 +1,156 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using NLog;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NLog;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
class AwardCommand : IChatCommand
|
||||
{
|
||||
class AwardCommand : IChatCommand
|
||||
public IReadOnlyCollection<string> Names => new[] { "award", "revoke" };
|
||||
[Inject] private Logger log;
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
public IReadOnlyCollection<string> Names => new[] { "award", "revoke" };
|
||||
[Inject] private Logger log;
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
var awarder = ctx.EventArgs.Message.From;
|
||||
|
||||
if (Timeout.TimeoutCache[awarder.Id].PreviousAwardDate.AddSeconds(Config.Timeout.AwardTimeSeconds) > DateTime.Now)
|
||||
{
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
|
||||
var awarder = ctx.EventArgs.Message.From;
|
||||
|
||||
if (Timeout.TimeoutCache[awarder.Id].PreviousAwardDate.AddSeconds(Config.Timeout.AwardTimeSeconds) > DateTime.Now)
|
||||
{
|
||||
ctx.GetFeature<TimeoutManager.Feature>().Multiplier = 0; // Doesn't count as success or failure
|
||||
if (!Timeout.TimeoutCache[awarder.Id].TimeoutMessaged)
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]);
|
||||
Timeout.TimeoutCache[awarder.Id].TimeoutMessaged = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
string awardTypeText = null;
|
||||
long recipientId = default;
|
||||
foreach (string arg in ctx.Command.Parameters)
|
||||
{
|
||||
if (arg.StartsWith('@'))
|
||||
{
|
||||
if (recipientId != default(int))
|
||||
{
|
||||
await ctx.SendMessage(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.SendMessage(currentLocale["jetkarmabot.award.errbadusername"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (awardTypeText == null)
|
||||
awardTypeText = arg;
|
||||
else
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errdup"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.EventArgs.Message.ReplyToMessage != null && recipientId == default)
|
||||
{
|
||||
recipientId = ctx.EventArgs.Message.ReplyToMessage.From.Id;
|
||||
}
|
||||
|
||||
if (recipientId == default(int))
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardnoreply"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool awarding = ctx.Command.Command == "award";
|
||||
|
||||
if (awarder.Id == recipientId)
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardself"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx.GetFeature<ChatCommandRouter.Feature>().Router.Me.Id == recipientId)
|
||||
{
|
||||
await ctx.SendMessage(awarding
|
||||
? currentLocale["jetkarmabot.award.errawardbot"]
|
||||
: currentLocale["jetkarmabot.award.errrevokebot"]);
|
||||
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.SendMessage(response);
|
||||
Timeout.TimeoutCache[awarder.Id].PreviousAwardDate = DateTime.Now;
|
||||
return true;
|
||||
ctx.GetFeature<TimeoutManager.Feature>().Multiplier = 0; // Doesn't count as success or failure
|
||||
if (!Timeout.TimeoutCache[awarder.Id].TimeoutMessaged)
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]);
|
||||
Timeout.TimeoutCache[awarder.Id].TimeoutMessaged = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private string getLocalizedName(global::JetKarmaBot.Models.AwardType awardType, Locale loc)
|
||||
string awardTypeText = null;
|
||||
long recipientId = default;
|
||||
foreach (string arg in ctx.Command.Parameters)
|
||||
{
|
||||
if (loc.ContainsKey($"jetkarmabot.awardtypes.accusative.{awardType.CommandName}"))
|
||||
if (arg.StartsWith('@'))
|
||||
{
|
||||
return loc[$"jetkarmabot.awardtypes.accusative.{awardType.CommandName}"];
|
||||
if (recipientId != default(int))
|
||||
{
|
||||
await ctx.SendMessage(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.SendMessage(currentLocale["jetkarmabot.award.errbadusername"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return awardType.Name;
|
||||
if (awardTypeText == null)
|
||||
awardTypeText = arg;
|
||||
else
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errdup"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
[Inject] TimeoutManager Timeout { get; set; }
|
||||
[Inject] Config Config { get; set; }
|
||||
if (ctx.EventArgs.Message.ReplyToMessage != null && recipientId == default)
|
||||
{
|
||||
recipientId = ctx.EventArgs.Message.ReplyToMessage.From.Id;
|
||||
}
|
||||
|
||||
public string Description => "Awards/revokes an award to a user.";
|
||||
public string DescriptionID => "jetkarmabot.award.help";
|
||||
if (recipientId == default(int))
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardnoreply"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="awardtype",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The award to grant to/strip of the specified user",
|
||||
DescriptionID="jetkarmabot.award.awardtypehelp"
|
||||
},
|
||||
new ChatCommandArgument() {
|
||||
Name="to",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The user to award it to.",
|
||||
DescriptionID="jetkarmabot.award.tohelp"
|
||||
}
|
||||
};
|
||||
|
||||
bool awarding = ctx.Command.Command == "award";
|
||||
|
||||
if (awarder.Id == recipientId)
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardself"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx.GetFeature<ChatCommandRouter.Feature>().Router.Me.Id == recipientId)
|
||||
{
|
||||
await ctx.SendMessage(awarding
|
||||
? currentLocale["jetkarmabot.award.errawardbot"]
|
||||
: currentLocale["jetkarmabot.award.errrevokebot"]);
|
||||
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.SendMessage(response);
|
||||
Timeout.TimeoutCache[awarder.Id].PreviousAwardDate = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
private string getLocalizedName(global::JetKarmaBot.Models.AwardType awardType, Locale loc)
|
||||
{
|
||||
if (loc.ContainsKey($"jetkarmabot.awardtypes.accusative.{awardType.CommandName}"))
|
||||
{
|
||||
return loc[$"jetkarmabot.awardtypes.accusative.{awardType.CommandName}"];
|
||||
}
|
||||
else
|
||||
{
|
||||
return awardType.Name;
|
||||
}
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
[Inject] TimeoutManager Timeout { get; set; }
|
||||
[Inject] Config Config { get; set; }
|
||||
|
||||
public string Description => "Awards/revokes an award to a user.";
|
||||
public string DescriptionID => "jetkarmabot.award.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="awardtype",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The award to grant to/strip of the specified user",
|
||||
DescriptionID="jetkarmabot.award.awardtypehelp"
|
||||
},
|
||||
new ChatCommandArgument() {
|
||||
Name="to",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The user to award it to.",
|
||||
DescriptionID="jetkarmabot.award.tohelp"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,80 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using NLog;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
class LocaleCommand : IChatCommand
|
||||
{
|
||||
class LocaleCommand : IChatCommand
|
||||
public IReadOnlyCollection<string> Names => new[] { "changelocale", "locale" };
|
||||
[Inject] private Logger log;
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
public IReadOnlyCollection<string> Names => new[] { "changelocale", "locale" };
|
||||
[Inject] private Logger log;
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var cmd = ctx.Command;
|
||||
var args = ctx.EventArgs;
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
if (cmd.Parameters.Length < 1)
|
||||
{
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var cmd = ctx.Command;
|
||||
var args = ctx.EventArgs;
|
||||
|
||||
if (cmd.Parameters.Length < 1)
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.getlocale"]);
|
||||
return false;
|
||||
}
|
||||
else if (cmd.Parameters[0] == "list")
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.listalltext"] + "\n"
|
||||
+ string.Join("\n", Locale.Select(a => a.Key)));
|
||||
return false;
|
||||
}
|
||||
else if (cmd.Parameters[0] == "all")
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.errorall"]);
|
||||
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.SendMessage(
|
||||
currentLocale["jetkarmabot.changelocale.toomany"] + "\n"
|
||||
+ string.Join("\n", (e.Data["LocaleNames"] as Locale[]).Select(x => x.Name)));
|
||||
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.SendMessage(
|
||||
(currentLocale.HasNote ? currentLocale["jetkarmabot.changelocale.beforenote"] + currentLocale.Note + "\n" : "")
|
||||
+ currentLocale["jetkarmabot.changelocale.justchanged"]);
|
||||
return true;
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.getlocale"]);
|
||||
return false;
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
|
||||
public string Description => "Switches current chat locale to [locale]";
|
||||
public string DescriptionID => "jetkarmabot.changelocale.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="locale",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The locale to switch to. Can be \"list\" to list all possible locales. Also can be empty to get current locale.",
|
||||
DescriptionID="jetkarmabot.changelocale.localehelp"
|
||||
else if (cmd.Parameters[0] == "list")
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.listalltext"] + "\n"
|
||||
+ string.Join("\n", Locale.Select(a => a.Key)));
|
||||
return false;
|
||||
}
|
||||
else if (cmd.Parameters[0] == "all")
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.changelocale.errorall"]);
|
||||
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.SendMessage(
|
||||
currentLocale["jetkarmabot.changelocale.toomany"] + "\n"
|
||||
+ string.Join("\n", (e.Data["LocaleNames"] as Locale[]).Select(x => x.Name)));
|
||||
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.SendMessage(
|
||||
(currentLocale.HasNote ? currentLocale["jetkarmabot.changelocale.beforenote"] + currentLocale.Note + "\n" : "")
|
||||
+ currentLocale["jetkarmabot.changelocale.justchanged"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
|
||||
public string Description => "Switches current chat locale to [locale]";
|
||||
public string DescriptionID => "jetkarmabot.changelocale.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="locale",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The locale to switch to. Can be \"list\" to list all possible locales. Also can be empty to get current locale.",
|
||||
DescriptionID="jetkarmabot.changelocale.localehelp"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,51 +1,48 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
public class CommandString
|
||||
{
|
||||
public class CommandString
|
||||
public CommandString(string command, params string[] parameters)
|
||||
{
|
||||
public CommandString(string command, params string[] parameters)
|
||||
{
|
||||
Command = command;
|
||||
Parameters = parameters;
|
||||
}
|
||||
Command = command;
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
public string Command { get; }
|
||||
public string UserName { get; set; } = null;
|
||||
public string[] Parameters { get; }
|
||||
public string Command { get; }
|
||||
public string UserName { get; set; } = null;
|
||||
public string[] Parameters { get; }
|
||||
|
||||
public static readonly char[] WS_CHARS = new[] { ' ', '\r', '\n', '\n' };
|
||||
public static readonly char[] WS_CHARS = new[] { ' ', '\r', '\n', '\n' };
|
||||
|
||||
public static bool TryParse(string s, out CommandString result)
|
||||
{
|
||||
result = null;
|
||||
if (string.IsNullOrWhiteSpace(s) || s[0] != '/')
|
||||
return false;
|
||||
public static bool TryParse(string s, out CommandString result)
|
||||
{
|
||||
result = null;
|
||||
if (string.IsNullOrWhiteSpace(s) || s[0] != '/')
|
||||
return false;
|
||||
|
||||
string[] words = s.Split(WS_CHARS, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] words = s.Split(WS_CHARS, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (!words.Any())
|
||||
return false;
|
||||
if (!words.Any())
|
||||
return false;
|
||||
|
||||
var cmdRegex = new Regex(@"/(?<cmd>\w+)(@(?<name>\w+))?");
|
||||
var match = cmdRegex.Match(words.First());
|
||||
if (!match.Success)
|
||||
return false;
|
||||
var cmdRegex = new Regex(@"/(?<cmd>\w+)(@(?<name>\w+))?");
|
||||
var match = cmdRegex.Match(words.First());
|
||||
if (!match.Success)
|
||||
return false;
|
||||
|
||||
string cmd = match.Groups["cmd"].Captures[0].Value;
|
||||
string username = match.Groups["name"].Captures.Count > 0 ? match.Groups["name"].Captures[0].Value : null;
|
||||
string[] parameters = words.Skip(1).ToArray();
|
||||
string cmd = match.Groups["cmd"].Captures[0].Value;
|
||||
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 };
|
||||
return true;
|
||||
}
|
||||
result = new CommandString(cmd, parameters) { UserName = username };
|
||||
return true;
|
||||
}
|
||||
|
||||
public static CommandString Parse(string s)
|
||||
{
|
||||
if (TryParse(s, out var c)) return c;
|
||||
throw new ArgumentException($"\"{s}\" is not a command");
|
||||
}
|
||||
public static CommandString Parse(string s)
|
||||
{
|
||||
if (TryParse(s, out var c)) return c;
|
||||
throw new ArgumentException($"\"{s}\" is not a command");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Perfusion;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
public class CurrenciesCommand : IChatCommand
|
||||
{
|
||||
public class CurrenciesCommand : IChatCommand
|
||||
[Inject] Localization Locale { get; set; }
|
||||
public IReadOnlyCollection<string> Names => new[] { "currencies", "awardtypes" };
|
||||
|
||||
public string Description => "Shows all award types";
|
||||
public string DescriptionID => "jetkarmabot.currencies.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
};
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
[Inject] Localization Locale { get; set; }
|
||||
public IReadOnlyCollection<string> Names => new[] { "currencies", "awardtypes" };
|
||||
|
||||
public string Description => "Shows all award types";
|
||||
public string DescriptionID => "jetkarmabot.currencies.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
};
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
await ctx.SendMessage(
|
||||
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>")));
|
||||
return true;
|
||||
}
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
await ctx.SendMessage(
|
||||
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>")));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,44 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Models;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
public class HelpCommand : IChatCommand
|
||||
{
|
||||
public class HelpCommand : IChatCommand
|
||||
[Inject] Localization Locale { get; set; }
|
||||
public IReadOnlyCollection<string> Names => new[] { "help" };
|
||||
|
||||
public string Description => "Displays help text for all(one) command(s)";
|
||||
public string DescriptionID => "jetkarmabot.help.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="command",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The command to return help text for. If empty shows all commands.",
|
||||
DescriptionID="jetkarmabot.help.commandhelp"
|
||||
}
|
||||
};
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
[Inject] Localization Locale { get; set; }
|
||||
public IReadOnlyCollection<string> Names => new[] { "help" };
|
||||
|
||||
public string Description => "Displays help text for all(one) command(s)";
|
||||
public string DescriptionID => "jetkarmabot.help.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="command",
|
||||
Required=false,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The command to return help text for. If empty shows all commands.",
|
||||
DescriptionID="jetkarmabot.help.commandhelp"
|
||||
}
|
||||
};
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var router = ctx.GetFeature<ChatCommandRouter.Feature>().Router;
|
||||
if (ctx.Command.Parameters.Length < 1)
|
||||
{
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var router = ctx.GetFeature<ChatCommandRouter.Feature>().Router;
|
||||
if (ctx.Command.Parameters.Length < 1)
|
||||
{
|
||||
await ctx.SendMessage(router.GetHelpText(currentLocale));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SendMessage(router.GetHelpTextFor(ctx.Command.Parameters[0], currentLocale));
|
||||
return true;
|
||||
}
|
||||
await ctx.SendMessage(router.GetHelpText(currentLocale));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SendMessage(router.GetHelpTextFor(ctx.Command.Parameters[0], currentLocale));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
public interface IChatCommand
|
||||
{
|
||||
public interface IChatCommand
|
||||
{
|
||||
IReadOnlyCollection<string> Names { get; }
|
||||
string Description { get; }
|
||||
string DescriptionID { get; }
|
||||
IReadOnlyCollection<ChatCommandArgument> Arguments { get; }
|
||||
IReadOnlyCollection<string> Names { get; }
|
||||
string Description { get; }
|
||||
string DescriptionID { get; }
|
||||
IReadOnlyCollection<ChatCommandArgument> Arguments { get; }
|
||||
|
||||
Task<bool> Execute(RequestContext ctx);
|
||||
}
|
||||
|
||||
public struct ChatCommandArgument
|
||||
{
|
||||
public string Name;
|
||||
public bool Required;
|
||||
public ChatCommandArgumentType Type;
|
||||
public string Description;
|
||||
public string DescriptionID;
|
||||
}
|
||||
|
||||
public enum ChatCommandArgumentType
|
||||
{
|
||||
Boolean,
|
||||
String,
|
||||
Integer,
|
||||
}
|
||||
Task<bool> Execute(RequestContext ctx);
|
||||
}
|
||||
|
||||
public struct ChatCommandArgument
|
||||
{
|
||||
public string Name;
|
||||
public bool Required;
|
||||
public ChatCommandArgumentType Type;
|
||||
public string Description;
|
||||
public string DescriptionID;
|
||||
}
|
||||
|
||||
public enum ChatCommandArgumentType
|
||||
{
|
||||
Boolean,
|
||||
String,
|
||||
Integer,
|
||||
}
|
||||
|
||||
@ -1,70 +1,65 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
class LeaderboardCommand : IChatCommand
|
||||
{
|
||||
class LeaderboardCommand : IChatCommand
|
||||
public IReadOnlyCollection<string> Names => new[] { "leaderboard" };
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
public IReadOnlyCollection<string> Names => new[] { "leaderboard" };
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
bool isPrivate = ctx.EventArgs.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
if (isPrivate)
|
||||
{
|
||||
bool isPrivate = ctx.EventArgs.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
if (isPrivate)
|
||||
{
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardself"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var asker = ctx.EventArgs.Message.From;
|
||||
var awardTypeName = ctx.Command.Parameters.FirstOrDefault();
|
||||
|
||||
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 topEarners = await db.Awards
|
||||
.Where(x => x.ChatId == ctx.EventArgs.Message.Chat.Id && x.AwardTypeId == awardTypeId)
|
||||
.GroupBy(x => x.To)
|
||||
.Select(x => new { User = x.Key, Amount = x.Sum(y => y.Amount) })
|
||||
.OrderByDescending(x => x.Amount)
|
||||
.Take(5)
|
||||
.ToListAsync();
|
||||
|
||||
var response = string.Format(currentLocale["jetkarmabot.leaderboard.specifictext"], awardType.Symbol) + "\n"
|
||||
+ string.Join('\n', topEarners.Select((x, index)
|
||||
=> $"{index + 1}. {x.User.Username} - {x.Amount}")
|
||||
);
|
||||
|
||||
await ctx.SendMessage(response);
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.award.errawardself"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var asker = ctx.EventArgs.Message.From;
|
||||
var awardTypeName = ctx.Command.Parameters.FirstOrDefault();
|
||||
|
||||
public string Description => "Shows the people with the most of a specific award.";
|
||||
public string DescriptionID => "jetkarmabot.leaderboard.help";
|
||||
if (string.IsNullOrWhiteSpace(awardTypeName))
|
||||
awardTypeName = "star";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="awardtype",
|
||||
Required=true,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The awardtype to show a leaderboard for.",
|
||||
DescriptionID= "jetkarmabot.leaderboard.awardtypehelp"
|
||||
}
|
||||
};
|
||||
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 topEarners = await db.Awards
|
||||
.Where(x => x.ChatId == ctx.EventArgs.Message.Chat.Id && x.AwardTypeId == awardTypeId)
|
||||
.GroupBy(x => x.To)
|
||||
.Select(x => new { User = x.Key, Amount = x.Sum(y => y.Amount) })
|
||||
.OrderByDescending(x => x.Amount)
|
||||
.Take(5)
|
||||
.ToListAsync();
|
||||
|
||||
var response = string.Format(currentLocale["jetkarmabot.leaderboard.specifictext"], awardType.Symbol) + "\n"
|
||||
+ string.Join('\n', topEarners.Select((x, index)
|
||||
=> $"{index + 1}. {x.User.Username} - {x.Amount}")
|
||||
);
|
||||
|
||||
await ctx.SendMessage(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
|
||||
public string Description => "Shows the people with the most of a specific award.";
|
||||
public string DescriptionID => "jetkarmabot.leaderboard.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => new ChatCommandArgument[] {
|
||||
new ChatCommandArgument() {
|
||||
Name="awardtype",
|
||||
Required=true,
|
||||
Type=ChatCommandArgumentType.String,
|
||||
Description="The awardtype to show a leaderboard for.",
|
||||
DescriptionID= "jetkarmabot.leaderboard.awardtypehelp"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,56 +1,51 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Commands
|
||||
namespace JetKarmaBot.Commands;
|
||||
|
||||
class StatusCommand : IChatCommand
|
||||
{
|
||||
class StatusCommand : IChatCommand
|
||||
public IReadOnlyCollection<string> Names => ["status"];
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
{
|
||||
public IReadOnlyCollection<string> Names => ["status"];
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var asker = ctx.EventArgs.Message.From;
|
||||
bool isPrivate = ctx.EventArgs.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
|
||||
|
||||
public async Task<bool> Execute(RequestContext ctx)
|
||||
string response;
|
||||
|
||||
var awards = db.Awards.Where(x => x.ToId == asker.Id);
|
||||
if (!isPrivate)
|
||||
awards = awards.Where(x => x.ChatId == ctx.EventArgs.Message.Chat.Id);
|
||||
|
||||
if (!awards.Any())
|
||||
response = currentLocale["jetkarmabot.status.havenothing"];
|
||||
else
|
||||
{
|
||||
var db = ctx.GetFeature<KarmaContext>();
|
||||
var currentLocale = ctx.GetFeature<Locale>();
|
||||
var asker = ctx.EventArgs.Message.From;
|
||||
bool isPrivate = ctx.EventArgs.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Private;
|
||||
var aq = db.AwardTypes.GroupJoin(
|
||||
awards,
|
||||
at => at.AwardTypeId,
|
||||
aw => aw.AwardTypeId,
|
||||
(at, aws) => new {at.Symbol, Amount = aws.Sum(aw => aw.Amount) });
|
||||
|
||||
string response;
|
||||
var awardsByType = await aq.ToListAsync();
|
||||
|
||||
var awards = db.Awards.Where(x => x.ToId == asker.Id);
|
||||
if (!isPrivate)
|
||||
awards = awards.Where(x => x.ChatId == ctx.EventArgs.Message.Chat.Id);
|
||||
|
||||
if (!awards.Any())
|
||||
response = currentLocale["jetkarmabot.status.havenothing"];
|
||||
else
|
||||
{
|
||||
var aq = db.AwardTypes.GroupJoin(
|
||||
awards,
|
||||
at => at.AwardTypeId,
|
||||
aw => aw.AwardTypeId,
|
||||
(at, aws) => new {at.Symbol, Amount = aws.Sum(aw => aw.Amount) });
|
||||
|
||||
var awardsByType = await aq.ToListAsync();
|
||||
|
||||
response =
|
||||
currentLocale["jetkarmabot.status.listalltext"] + "\n"
|
||||
+ string.Join("\n", awardsByType.Select(a => $" - {a.Symbol} {a.Amount}"));
|
||||
}
|
||||
|
||||
await ctx.SendMessage(response);
|
||||
return true;
|
||||
response =
|
||||
currentLocale["jetkarmabot.status.listalltext"] + "\n"
|
||||
+ string.Join("\n", awardsByType.Select(a => $" - {a.Symbol} {a.Amount}"));
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
|
||||
public string Description => "Shows the amount of awards that you have";
|
||||
public string DescriptionID => "jetkarmabot.status.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => [];
|
||||
await ctx.SendMessage(response);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Inject] Localization Locale { get; set; }
|
||||
|
||||
public string Description => "Shows the amount of awards that you have";
|
||||
public string DescriptionID => "jetkarmabot.status.help";
|
||||
|
||||
public IReadOnlyCollection<ChatCommandArgument> Arguments => [];
|
||||
}
|
||||
|
||||
@ -2,76 +2,74 @@
|
||||
using Newtonsoft.Json;
|
||||
using JsonNet.PrivateSettersContractResolvers;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public class Config : ConfigBase
|
||||
{
|
||||
public class Config : ConfigBase
|
||||
public Config(string path) : base(path) { }
|
||||
|
||||
public string ApiKey { get; private set; }
|
||||
public string ConnectionString { get; private set; }
|
||||
|
||||
public class ProxySettings
|
||||
{
|
||||
public Config(string path) : base(path) { }
|
||||
|
||||
public string ApiKey { get; private set; }
|
||||
public string ConnectionString { get; private set; }
|
||||
|
||||
public class ProxySettings
|
||||
{
|
||||
public string Url { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
public string Login { get; private set; }
|
||||
public string Password { get; private set; }
|
||||
}
|
||||
|
||||
public ProxySettings Proxy { get; private set; }
|
||||
public class TimeoutConfig
|
||||
{
|
||||
public int DebtLimitSeconds { get; private set; } = 60 * 60 * 2;
|
||||
public Dictionary<string, int> CommandCostsSeconds { get; private set; } = new Dictionary<string, int>()
|
||||
{
|
||||
{"JetKarmaBot.Commands.AwardCommand (OK)", 60*15},
|
||||
{"JetKarmaBot.Commands.AwardCommand (ERR)", 60*5},
|
||||
{"Default", 60*5}
|
||||
};
|
||||
public int SaveIntervalSeconds { get; private set; } = 60 * 5;
|
||||
public double AwardTimeSeconds { get; private set; } = 60;
|
||||
}
|
||||
public TimeoutConfig Timeout { get; private set; } = new TimeoutConfig();
|
||||
public bool SqlDebug { get; private set; } = false;
|
||||
public string Url { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
public string Login { get; private set; }
|
||||
public string Password { get; private set; }
|
||||
}
|
||||
|
||||
public abstract class ConfigBase
|
||||
public ProxySettings Proxy { get; private set; }
|
||||
public class TimeoutConfig
|
||||
{
|
||||
public ConfigBase(string path)
|
||||
public int DebtLimitSeconds { get; private set; } = 60 * 60 * 2;
|
||||
public Dictionary<string, int> CommandCostsSeconds { get; private set; } = new Dictionary<string, int>()
|
||||
{
|
||||
JObject configJson;
|
||||
{"JetKarmaBot.Commands.AwardCommand (OK)", 60*15},
|
||||
{"JetKarmaBot.Commands.AwardCommand (ERR)", 60*5},
|
||||
{"Default", 60*5}
|
||||
};
|
||||
public int SaveIntervalSeconds { get; private set; } = 60 * 5;
|
||||
public double AwardTimeSeconds { get; private set; } = 60;
|
||||
}
|
||||
public TimeoutConfig Timeout { get; private set; } = new TimeoutConfig();
|
||||
public bool SqlDebug { get; private set; } = false;
|
||||
}
|
||||
|
||||
if (File.Exists(path))
|
||||
public abstract class ConfigBase
|
||||
{
|
||||
public ConfigBase(string path)
|
||||
{
|
||||
JObject configJson;
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
configJson = JObject.Parse(File.ReadAllText(path));
|
||||
|
||||
using (var sr = configJson.CreateReader())
|
||||
{
|
||||
configJson = JObject.Parse(File.ReadAllText(path));
|
||||
|
||||
using (var sr = configJson.CreateReader())
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new PrivateSetterContractResolver()
|
||||
};
|
||||
JsonSerializer.CreateDefault(settings).Populate(sr, this);
|
||||
}
|
||||
ContractResolver = new PrivateSetterContractResolver()
|
||||
};
|
||||
JsonSerializer.CreateDefault(settings).Populate(sr, this);
|
||||
}
|
||||
else configJson = new JObject();
|
||||
}
|
||||
else configJson = new JObject();
|
||||
|
||||
configJson.Merge(JToken.FromObject(this), new JsonMergeSettings
|
||||
{
|
||||
MergeArrayHandling = MergeArrayHandling.Union
|
||||
});
|
||||
configJson.Merge(JToken.FromObject(this), new JsonMergeSettings
|
||||
{
|
||||
MergeArrayHandling = MergeArrayHandling.Union
|
||||
});
|
||||
|
||||
try // populate possible missing properties in file
|
||||
{
|
||||
File.WriteAllText(path, configJson.ToString(Formatting.Indented));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(e);
|
||||
}
|
||||
try // populate possible missing properties in file
|
||||
{
|
||||
File.WriteAllText(path, configJson.ToString(Formatting.Indented));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
namespace JetKarmaBot;
|
||||
|
||||
namespace JetKarmaBot
|
||||
public static class IReadOnlyDictionaryExtensions
|
||||
{
|
||||
public static class IReadOnlyDictionaryExtensions
|
||||
public static TValue GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, TKey key)
|
||||
{
|
||||
public static TValue GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dict, TKey key)
|
||||
{
|
||||
TValue res = default(TValue);
|
||||
if (key != null)
|
||||
dict.TryGetValue(key, out res);
|
||||
return res;
|
||||
}
|
||||
TValue res = default;
|
||||
if (key != null)
|
||||
dict.TryGetValue(key, out res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,43 +3,42 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace JsonNet.PrivateSettersContractResolvers
|
||||
namespace JsonNet.PrivateSettersContractResolvers;
|
||||
|
||||
public class PrivateSetterContractResolver : DefaultContractResolver
|
||||
{
|
||||
public class PrivateSetterContractResolver : DefaultContractResolver
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
||||
if (jProperty.Writable)
|
||||
return jProperty;
|
||||
|
||||
jProperty.Writable = member.IsPropertyWithSetter();
|
||||
|
||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
||||
if (jProperty.Writable)
|
||||
return jProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public class PrivateSetterCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
||||
if (jProperty.Writable)
|
||||
return jProperty;
|
||||
jProperty.Writable = member.IsPropertyWithSetter();
|
||||
|
||||
jProperty.Writable = member.IsPropertyWithSetter();
|
||||
|
||||
return jProperty;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class MemberInfoExtensions
|
||||
{
|
||||
internal static bool IsPropertyWithSetter(this MemberInfo member)
|
||||
{
|
||||
var property = member as PropertyInfo;
|
||||
|
||||
return property?.GetSetMethod(true) != null;
|
||||
}
|
||||
return jProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public class PrivateSetterCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
||||
if (jProperty.Writable)
|
||||
return jProperty;
|
||||
|
||||
jProperty.Writable = member.IsPropertyWithSetter();
|
||||
|
||||
return jProperty;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class MemberInfoExtensions
|
||||
{
|
||||
internal static bool IsPropertyWithSetter(this MemberInfo member)
|
||||
{
|
||||
var property = member as PropertyInfo;
|
||||
|
||||
return property?.GetSetMethod(true) != null;
|
||||
}
|
||||
}
|
||||
6
JetKarmaBot/GlobalUsings.cs
Normal file
6
JetKarmaBot/GlobalUsings.cs
Normal file
@ -0,0 +1,6 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Linq;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using Perfusion;
|
||||
@ -3,140 +3,135 @@ using JetKarmaBot.Models;
|
||||
using JetKarmaBot.Services;
|
||||
using JetKarmaBot.Services.Handling;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Polling;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public class JetKarmaBot : IDisposable
|
||||
{
|
||||
public class JetKarmaBot : IDisposable
|
||||
[Inject] Config Config { get; set; }
|
||||
[Inject] IContainer Container { get; set; }
|
||||
[Inject] KarmaContextFactory Db { get; set; }
|
||||
[Inject] TimeoutManager Timeout { get; set; }
|
||||
[Inject] Localization Locale { get; set; }
|
||||
[Inject] Logger Log { get; set; }
|
||||
|
||||
|
||||
TelegramBotClient Client { get; set; }
|
||||
ChatCommandRouter Commands;
|
||||
RequestChain Chain;
|
||||
Task timeoutWaitTask;
|
||||
CancellationTokenSource timeoutWaitTaskToken;
|
||||
private bool stopped = false;
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
[Inject] Config Config { get; set; }
|
||||
[Inject] IContainer Container { get; set; }
|
||||
[Inject] KarmaContextFactory Db { get; set; }
|
||||
[Inject] TimeoutManager Timeout { get; set; }
|
||||
[Inject] Localization Locale { get; set; }
|
||||
[Inject] Logger Log { get; set; }
|
||||
using (KarmaContext db = Db.GetContext())
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
|
||||
Client = new TelegramBotClient(Config.ApiKey);
|
||||
Container.AddInstance(Client);
|
||||
|
||||
TelegramBotClient Client { get; set; }
|
||||
ChatCommandRouter Commands;
|
||||
RequestChain Chain;
|
||||
Task timeoutWaitTask;
|
||||
CancellationTokenSource timeoutWaitTaskToken;
|
||||
private bool stopped = false;
|
||||
timeoutWaitTaskToken = new CancellationTokenSource();
|
||||
timeoutWaitTask = Timeout.SaveLoop(timeoutWaitTaskToken.Token);
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
using (KarmaContext db = Db.GetContext())
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
await InitCommands(Container);
|
||||
InitChain(Container);
|
||||
|
||||
Client = new TelegramBotClient(Config.ApiKey);
|
||||
Container.AddInstance(Client);
|
||||
|
||||
timeoutWaitTaskToken = new CancellationTokenSource();
|
||||
timeoutWaitTask = Timeout.SaveLoop(timeoutWaitTaskToken.Token);
|
||||
|
||||
await InitCommands(Container);
|
||||
InitChain(Container);
|
||||
|
||||
var receiverOptions = new ReceiverOptions { AllowedUpdates = new[] { UpdateType.Message } };
|
||||
Client.StartReceiving(
|
||||
HandleUpdateAsync,
|
||||
HandleErrorAsync,
|
||||
receiverOptions);
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
if (stopped) return;
|
||||
Client?.CloseAsync();
|
||||
timeoutWaitTaskToken?.Cancel();
|
||||
try
|
||||
{
|
||||
if (timeoutWaitTask != null)
|
||||
await timeoutWaitTask;
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
await Timeout?.Save();
|
||||
Dispose();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
#region service
|
||||
|
||||
Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Error(exception, "Exception while handling API message");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
async Task HandleUpdateAsync(ITelegramBotClient sender, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
if (update.Type != UpdateType.Message || update?.Message?.Type != MessageType.Text)
|
||||
return;
|
||||
var message = update.Message!;
|
||||
|
||||
try
|
||||
{
|
||||
if (message == null || message.Type != MessageType.Text)
|
||||
return;
|
||||
if (!CommandString.TryParse(message.Text, out var cmd))
|
||||
return;
|
||||
if (cmd.UserName != null && cmd.UserName != Commands.Me.Username)
|
||||
return;
|
||||
|
||||
RequestContext ctx = new RequestContext(Client, update, cmd);
|
||||
await Chain.Handle(ctx);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Exception while handling message {0}", message);
|
||||
}
|
||||
}
|
||||
|
||||
async Task InitCommands(IContainer c)
|
||||
{
|
||||
c.Add<HelpCommand>();
|
||||
c.Add<AwardCommand>();
|
||||
c.Add<StatusCommand>();
|
||||
c.Add<LocaleCommand>();
|
||||
c.Add<CurrenciesCommand>();
|
||||
c.Add<LeaderboardCommand>();
|
||||
Commands = c.GetInstance<ChatCommandRouter>();
|
||||
await Commands.Start();
|
||||
foreach (IChatCommand cmd in c.GetInstances<IChatCommand>())
|
||||
{
|
||||
Commands.Add(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void InitChain(IContainer c)
|
||||
{
|
||||
Chain = c.ResolveObject(new RequestChain());
|
||||
Chain.Add(c.GetInstance<TimeoutManager.PreDbThrowout>());
|
||||
Chain.Add(c.GetInstance<DatabaseHandler>());
|
||||
Chain.Add(Timeout);
|
||||
Chain.Add(c.GetInstance<SaveData>());
|
||||
Chain.Add(Commands);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timeoutWaitTaskToken?.Dispose();
|
||||
timeoutWaitTask?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
var receiverOptions = new ReceiverOptions { AllowedUpdates = new[] { UpdateType.Message } };
|
||||
Client.StartReceiving(
|
||||
HandleUpdateAsync,
|
||||
HandleErrorAsync,
|
||||
receiverOptions);
|
||||
}
|
||||
|
||||
public async Task Stop()
|
||||
{
|
||||
if (stopped) return;
|
||||
Client?.CloseAsync();
|
||||
timeoutWaitTaskToken?.Cancel();
|
||||
try
|
||||
{
|
||||
if (timeoutWaitTask != null)
|
||||
await timeoutWaitTask;
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
await Timeout?.Save();
|
||||
Dispose();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
#region service
|
||||
|
||||
Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
Log.Error(exception, "Exception while handling API message");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
async Task HandleUpdateAsync(ITelegramBotClient sender, Update update, CancellationToken cancellationToken)
|
||||
{
|
||||
if (update.Type != UpdateType.Message || update?.Message?.Type != MessageType.Text)
|
||||
return;
|
||||
var message = update.Message!;
|
||||
|
||||
try
|
||||
{
|
||||
if (message == null || message.Type != MessageType.Text)
|
||||
return;
|
||||
if (!CommandString.TryParse(message.Text, out var cmd))
|
||||
return;
|
||||
if (cmd.UserName != null && cmd.UserName != Commands.Me.Username)
|
||||
return;
|
||||
|
||||
RequestContext ctx = new RequestContext(Client, update, cmd);
|
||||
await Chain.Handle(ctx);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Exception while handling message {0}", message);
|
||||
}
|
||||
}
|
||||
|
||||
async Task InitCommands(IContainer c)
|
||||
{
|
||||
c.Add<HelpCommand>();
|
||||
c.Add<AwardCommand>();
|
||||
c.Add<StatusCommand>();
|
||||
c.Add<LocaleCommand>();
|
||||
c.Add<CurrenciesCommand>();
|
||||
c.Add<LeaderboardCommand>();
|
||||
Commands = c.GetInstance<ChatCommandRouter>();
|
||||
await Commands.Start();
|
||||
foreach (IChatCommand cmd in c.GetInstances<IChatCommand>())
|
||||
{
|
||||
Commands.Add(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void InitChain(IContainer c)
|
||||
{
|
||||
Chain = c.ResolveObject(new RequestChain());
|
||||
Chain.Add(c.GetInstance<TimeoutManager.PreDbThrowout>());
|
||||
Chain.Add(c.GetInstance<DatabaseHandler>());
|
||||
Chain.Add(Timeout);
|
||||
Chain.Add(c.GetInstance<SaveData>());
|
||||
Chain.Add(Commands);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
timeoutWaitTaskToken?.Dispose();
|
||||
timeoutWaitTask?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -1,25 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace JetKarmaBot.Models
|
||||
namespace JetKarmaBot.Models;
|
||||
|
||||
public partial class Award
|
||||
{
|
||||
public partial class Award
|
||||
{
|
||||
public int AwardId { get; set; }
|
||||
public long ChatId { get; set; }
|
||||
public long FromId { get; set; }
|
||||
public long ToId { get; set; }
|
||||
public sbyte AwardTypeId { get; set; }
|
||||
public sbyte Amount { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
[ForeignKey("AwardTypeId")]
|
||||
public virtual AwardType AwardType { get; set; }
|
||||
[ForeignKey("ChatId")]
|
||||
public virtual Chat Chat { get; set; }
|
||||
[ForeignKey("FromId")]
|
||||
public virtual User From { get; set; }
|
||||
[ForeignKey("ToId")]
|
||||
public virtual User To { get; set; }
|
||||
}
|
||||
public int AwardId { get; set; }
|
||||
public long ChatId { get; set; }
|
||||
public long FromId { get; set; }
|
||||
public long ToId { get; set; }
|
||||
public sbyte AwardTypeId { get; set; }
|
||||
public sbyte Amount { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
[ForeignKey("AwardTypeId")]
|
||||
public virtual AwardType AwardType { get; set; }
|
||||
[ForeignKey("ChatId")]
|
||||
public virtual Chat Chat { get; set; }
|
||||
[ForeignKey("FromId")]
|
||||
public virtual User From { get; set; }
|
||||
[ForeignKey("ToId")]
|
||||
public virtual User To { get; set; }
|
||||
}
|
||||
|
||||
@ -1,21 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Models
|
||||
public partial class AwardType
|
||||
{
|
||||
public partial class AwardType
|
||||
public AwardType()
|
||||
{
|
||||
public AwardType()
|
||||
{
|
||||
Awards = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public sbyte AwardTypeId { get; set; }
|
||||
public string CommandName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public virtual ICollection<Award> Awards { get; set; }
|
||||
Awards = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public sbyte AwardTypeId { get; set; }
|
||||
public string CommandName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public string Description { get; set; }
|
||||
|
||||
public virtual ICollection<Award> Awards { get; set; }
|
||||
}
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Models
|
||||
public partial class Chat
|
||||
{
|
||||
public partial class Chat
|
||||
public Chat()
|
||||
{
|
||||
public Chat()
|
||||
{
|
||||
Awards = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public long ChatId { get; set; }
|
||||
public string Locale { get; set; } = "ru-RU";
|
||||
public bool IsAdministrator { get; set; }
|
||||
|
||||
public virtual ICollection<Award> Awards { get; set; }
|
||||
Awards = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public long ChatId { get; set; }
|
||||
public string Locale { get; set; } = "ru-RU";
|
||||
public bool IsAdministrator { get; set; }
|
||||
|
||||
public virtual ICollection<Award> Awards { get; set; }
|
||||
}
|
||||
|
||||
@ -1,183 +1,179 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Perfusion;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace JetKarmaBot.Models
|
||||
namespace JetKarmaBot.Models;
|
||||
|
||||
[Transient]
|
||||
public partial class KarmaContext : DbContext
|
||||
{
|
||||
[Transient]
|
||||
public partial class KarmaContext : DbContext
|
||||
public KarmaContext()
|
||||
{
|
||||
public KarmaContext()
|
||||
}
|
||||
|
||||
public KarmaContext(DbContextOptions<KarmaContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual DbSet<Award> Awards { get; set; }
|
||||
public virtual DbSet<AwardType> AwardTypes { get; set; }
|
||||
public virtual DbSet<Chat> Chats { get; set; }
|
||||
public virtual DbSet<User> Users { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Award>(entity =>
|
||||
{
|
||||
}
|
||||
entity.ToTable("award");
|
||||
|
||||
public KarmaContext(DbContextOptions<KarmaContext> options)
|
||||
: base(options)
|
||||
entity.HasIndex(e => e.AwardId)
|
||||
.HasDatabaseName("awardid_UNIQUE")
|
||||
.IsUnique();
|
||||
|
||||
entity.HasIndex(e => e.AwardTypeId)
|
||||
.HasDatabaseName("fk_awardtype_idx");
|
||||
|
||||
entity.HasIndex(e => e.ChatId)
|
||||
.HasDatabaseName("fk_chat_idx");
|
||||
|
||||
entity.HasIndex(e => e.FromId)
|
||||
.HasDatabaseName("fk_from_idx");
|
||||
|
||||
entity.HasIndex(e => e.ToId)
|
||||
.HasDatabaseName("fk_to_idx");
|
||||
|
||||
entity.Property(e => e.AwardId)
|
||||
.HasColumnName("awardid")
|
||||
.HasColumnType("int(11)");
|
||||
|
||||
entity.Property(e => e.Amount)
|
||||
.HasColumnName("amount")
|
||||
.HasColumnType("tinyint(3)")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
entity.Property(e => e.AwardTypeId)
|
||||
.HasColumnName("awardtypeid")
|
||||
.HasColumnType("tinyint(3)");
|
||||
|
||||
entity.Property(e => e.ChatId)
|
||||
.HasColumnName("chatid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.Date)
|
||||
.HasColumnName("date")
|
||||
.HasColumnType("datetime")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
entity.Property(e => e.FromId)
|
||||
.HasColumnName("fromid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.ToId)
|
||||
.HasColumnName("toid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.HasOne(d => d.AwardType)
|
||||
.WithMany(p => p.Awards)
|
||||
.HasForeignKey(d => d.AwardTypeId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_awardtype");
|
||||
|
||||
entity.HasOne(d => d.Chat)
|
||||
.WithMany(p => p.Awards)
|
||||
.HasForeignKey(d => d.ChatId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_chat");
|
||||
|
||||
entity.HasOne(d => d.From)
|
||||
.WithMany(p => p.AwardsFrom)
|
||||
.HasForeignKey(d => d.FromId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_from");
|
||||
|
||||
entity.HasOne(d => d.To)
|
||||
.WithMany(p => p.AwardsTo)
|
||||
.HasForeignKey(d => d.ToId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_to");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AwardType>(entity =>
|
||||
{
|
||||
}
|
||||
entity.ToTable("awardtype");
|
||||
|
||||
public virtual DbSet<Award> Awards { get; set; }
|
||||
public virtual DbSet<AwardType> AwardTypes { get; set; }
|
||||
public virtual DbSet<Chat> Chats { get; set; }
|
||||
public virtual DbSet<User> Users { get; set; }
|
||||
entity.HasIndex(e => e.AwardTypeId)
|
||||
.HasDatabaseName("awardtypeid_UNIQUE")
|
||||
.IsUnique();
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
entity.HasIndex(e => e.CommandName)
|
||||
.HasDatabaseName("commandname_UNIQUE")
|
||||
.IsUnique();
|
||||
|
||||
entity.Property(e => e.AwardTypeId)
|
||||
.HasColumnName("awardtypeid")
|
||||
.HasColumnType("tinyint(3)");
|
||||
|
||||
entity.Property(e => e.CommandName)
|
||||
.IsRequired()
|
||||
.HasColumnName("commandname")
|
||||
.HasColumnType("varchar(35)");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
.IsRequired()
|
||||
.HasColumnName("description")
|
||||
.HasColumnType("text");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasColumnName("name")
|
||||
.HasColumnType("varchar(32)");
|
||||
|
||||
entity.Property(e => e.Symbol)
|
||||
.IsRequired()
|
||||
.HasColumnName("symbol")
|
||||
.HasColumnType("varchar(16)");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Chat>(entity =>
|
||||
{
|
||||
entity.ToTable("chat");
|
||||
|
||||
}
|
||||
entity.Property(e => e.ChatId)
|
||||
.HasColumnName("chatid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
entity.Property(e => e.Locale)
|
||||
.IsRequired()
|
||||
.HasColumnName("locale")
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasDefaultValueSql("'ru-RU'");
|
||||
|
||||
entity.Property(e => e.IsAdministrator)
|
||||
.HasColumnName("isadministrator")
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasDefaultValueSql("'0'");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<User>(entity =>
|
||||
{
|
||||
modelBuilder.Entity<Award>(entity =>
|
||||
{
|
||||
entity.ToTable("award");
|
||||
entity.ToTable("user");
|
||||
|
||||
entity.HasIndex(e => e.AwardId)
|
||||
.HasDatabaseName("awardid_UNIQUE")
|
||||
.IsUnique();
|
||||
entity.Property(e => e.UserId)
|
||||
.HasColumnName("userid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.HasIndex(e => e.AwardTypeId)
|
||||
.HasDatabaseName("fk_awardtype_idx");
|
||||
entity.Property(e => e.Username)
|
||||
.HasColumnName("username")
|
||||
.HasColumnType("varchar(45)");
|
||||
|
||||
entity.HasIndex(e => e.ChatId)
|
||||
.HasDatabaseName("fk_chat_idx");
|
||||
|
||||
entity.HasIndex(e => e.FromId)
|
||||
.HasDatabaseName("fk_from_idx");
|
||||
|
||||
entity.HasIndex(e => e.ToId)
|
||||
.HasDatabaseName("fk_to_idx");
|
||||
|
||||
entity.Property(e => e.AwardId)
|
||||
.HasColumnName("awardid")
|
||||
.HasColumnType("int(11)");
|
||||
|
||||
entity.Property(e => e.Amount)
|
||||
.HasColumnName("amount")
|
||||
.HasColumnType("tinyint(3)")
|
||||
.HasDefaultValueSql("'1'");
|
||||
|
||||
entity.Property(e => e.AwardTypeId)
|
||||
.HasColumnName("awardtypeid")
|
||||
.HasColumnType("tinyint(3)");
|
||||
|
||||
entity.Property(e => e.ChatId)
|
||||
.HasColumnName("chatid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.Date)
|
||||
.HasColumnName("date")
|
||||
.HasColumnType("datetime")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
|
||||
entity.Property(e => e.FromId)
|
||||
.HasColumnName("fromid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.ToId)
|
||||
.HasColumnName("toid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.HasOne(d => d.AwardType)
|
||||
.WithMany(p => p.Awards)
|
||||
.HasForeignKey(d => d.AwardTypeId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_awardtype");
|
||||
|
||||
entity.HasOne(d => d.Chat)
|
||||
.WithMany(p => p.Awards)
|
||||
.HasForeignKey(d => d.ChatId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_chat");
|
||||
|
||||
entity.HasOne(d => d.From)
|
||||
.WithMany(p => p.AwardsFrom)
|
||||
.HasForeignKey(d => d.FromId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_from");
|
||||
|
||||
entity.HasOne(d => d.To)
|
||||
.WithMany(p => p.AwardsTo)
|
||||
.HasForeignKey(d => d.ToId)
|
||||
.OnDelete(DeleteBehavior.ClientSetNull)
|
||||
.HasConstraintName("fk_to");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AwardType>(entity =>
|
||||
{
|
||||
entity.ToTable("awardtype");
|
||||
|
||||
entity.HasIndex(e => e.AwardTypeId)
|
||||
.HasDatabaseName("awardtypeid_UNIQUE")
|
||||
.IsUnique();
|
||||
|
||||
entity.HasIndex(e => e.CommandName)
|
||||
.HasDatabaseName("commandname_UNIQUE")
|
||||
.IsUnique();
|
||||
|
||||
entity.Property(e => e.AwardTypeId)
|
||||
.HasColumnName("awardtypeid")
|
||||
.HasColumnType("tinyint(3)");
|
||||
|
||||
entity.Property(e => e.CommandName)
|
||||
.IsRequired()
|
||||
.HasColumnName("commandname")
|
||||
.HasColumnType("varchar(35)");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
.IsRequired()
|
||||
.HasColumnName("description")
|
||||
.HasColumnType("text");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasColumnName("name")
|
||||
.HasColumnType("varchar(32)");
|
||||
|
||||
entity.Property(e => e.Symbol)
|
||||
.IsRequired()
|
||||
.HasColumnName("symbol")
|
||||
.HasColumnType("varchar(16)");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Chat>(entity =>
|
||||
{
|
||||
entity.ToTable("chat");
|
||||
|
||||
entity.Property(e => e.ChatId)
|
||||
.HasColumnName("chatid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.Locale)
|
||||
.IsRequired()
|
||||
.HasColumnName("locale")
|
||||
.HasColumnType("varchar(10)")
|
||||
.HasDefaultValueSql("'ru-RU'");
|
||||
|
||||
entity.Property(e => e.IsAdministrator)
|
||||
.HasColumnName("isadministrator")
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasDefaultValueSql("'0'");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<User>(entity =>
|
||||
{
|
||||
entity.ToTable("user");
|
||||
|
||||
entity.Property(e => e.UserId)
|
||||
.HasColumnName("userid")
|
||||
.HasColumnType("bigint(20)");
|
||||
|
||||
entity.Property(e => e.Username)
|
||||
.HasColumnName("username")
|
||||
.HasColumnType("varchar(45)");
|
||||
|
||||
entity.Property(e => e.CooldownDate)
|
||||
.HasColumnName("cooldowndate")
|
||||
.HasColumnType("timestamp")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
}
|
||||
entity.Property(e => e.CooldownDate)
|
||||
.HasColumnName("cooldowndate")
|
||||
.HasColumnType("timestamp")
|
||||
.HasDefaultValueSql("CURRENT_TIMESTAMP");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace JetKarmaBot.Models
|
||||
namespace JetKarmaBot.Models;
|
||||
|
||||
public partial class User
|
||||
{
|
||||
public partial class User
|
||||
public User()
|
||||
{
|
||||
public User()
|
||||
{
|
||||
AwardsFrom = new HashSet<Award>();
|
||||
AwardsTo = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public long UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public DateTime CooldownDate { get; set; }
|
||||
[InverseProperty("From")]
|
||||
public virtual ICollection<Award> AwardsFrom { get; set; }
|
||||
[InverseProperty("To")]
|
||||
public virtual ICollection<Award> AwardsTo { get; set; }
|
||||
AwardsFrom = new HashSet<Award>();
|
||||
AwardsTo = new HashSet<Award>();
|
||||
}
|
||||
|
||||
public long UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public DateTime CooldownDate { get; set; }
|
||||
[InverseProperty("From")]
|
||||
public virtual ICollection<Award> AwardsFrom { get; set; }
|
||||
[InverseProperty("To")]
|
||||
public virtual ICollection<Award> AwardsTo { get; set; }
|
||||
}
|
||||
|
||||
@ -1,79 +1,73 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using JetKarmaBot.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
public static class Program
|
||||
private static Logger log = LogManager.GetCurrentClassLogger();
|
||||
public enum ExitCode : int
|
||||
{
|
||||
private static Logger log = LogManager.GetCurrentClassLogger();
|
||||
public enum ExitCode : int
|
||||
Ok = 0,
|
||||
ErrorNotStarted = 0x80,
|
||||
ErrorRunning = 0x81,
|
||||
ErrorException = 0x82,
|
||||
ErrorInvalidCommandLine = 0x100
|
||||
};
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
log.Info("Starting JetKarmaBot.");
|
||||
Container c = new Container();
|
||||
var cfg = new Config("karma.cfg.json");
|
||||
c.AddInstance(cfg);
|
||||
|
||||
var connStr = cfg.ConnectionString + (cfg.ConnectionString.EndsWith(";") ? "" : ";") + "TreatTinyAsBoolean=false";
|
||||
var serverVersion = ServerVersion.AutoDetect(connStr);
|
||||
|
||||
var dbOptions = new DbContextOptionsBuilder<KarmaContext>()
|
||||
.UseMySql(connStr, serverVersion);
|
||||
c.AddInfo<Logger>(new LogInfo());
|
||||
if (cfg.SqlDebug)
|
||||
{
|
||||
Ok = 0,
|
||||
ErrorNotStarted = 0x80,
|
||||
ErrorRunning = 0x81,
|
||||
ErrorException = 0x82,
|
||||
ErrorInvalidCommandLine = 0x100
|
||||
};
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
log.Info("Starting JetKarmaBot.");
|
||||
Container c = new Container();
|
||||
var cfg = new Config("karma.cfg.json");
|
||||
c.AddInstance(cfg);
|
||||
|
||||
var connStr = cfg.ConnectionString + (cfg.ConnectionString.EndsWith(";") ? "" : ";") + "TreatTinyAsBoolean=false";
|
||||
var serverVersion = ServerVersion.AutoDetect(connStr);
|
||||
|
||||
var dbOptions = new DbContextOptionsBuilder<KarmaContext>()
|
||||
.UseMySql(connStr, serverVersion);
|
||||
c.AddInfo<Logger>(new LogInfo());
|
||||
if (cfg.SqlDebug)
|
||||
{
|
||||
dbOptions = dbOptions.UseLoggerFactory(c.GetInstance<NLoggerFactory>());
|
||||
}
|
||||
c.AddTransient(() => new KarmaContext(dbOptions.Options));
|
||||
c.Add<JetKarmaBot>();
|
||||
|
||||
var bot = c.GetInstance<JetKarmaBot>();
|
||||
|
||||
try
|
||||
{
|
||||
bot.Init().GetAwaiter().GetResult();
|
||||
log.Info("JetKarmaBot started. Press Ctrl-C to exit...");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex);
|
||||
return (int)ExitCode.ErrorException;
|
||||
}
|
||||
ManualResetEvent quitEvent = new ManualResetEvent(false);
|
||||
try
|
||||
{
|
||||
Console.CancelKeyPress += (sender, eArgs) => // ctrl-c
|
||||
{
|
||||
eArgs.Cancel = true;
|
||||
quitEvent.Set();
|
||||
};
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, args) =>
|
||||
{
|
||||
log.Info("Received stop request, waiting for exit...");
|
||||
bot?.Stop()?.GetAwaiter().GetResult();
|
||||
};
|
||||
}
|
||||
catch { }
|
||||
|
||||
quitEvent.WaitOne(Timeout.Infinite);
|
||||
log.Info("Waiting for exit...");
|
||||
bot?.Stop()?.GetAwaiter().GetResult();
|
||||
|
||||
return (int)ExitCode.Ok;
|
||||
dbOptions = dbOptions.UseLoggerFactory(c.GetInstance<NLoggerFactory>());
|
||||
}
|
||||
c.AddTransient(() => new KarmaContext(dbOptions.Options));
|
||||
c.Add<JetKarmaBot>();
|
||||
|
||||
var bot = c.GetInstance<JetKarmaBot>();
|
||||
|
||||
try
|
||||
{
|
||||
bot.Init().GetAwaiter().GetResult();
|
||||
log.Info("JetKarmaBot started. Press Ctrl-C to exit...");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.Error(ex);
|
||||
return (int)ExitCode.ErrorException;
|
||||
}
|
||||
ManualResetEvent quitEvent = new ManualResetEvent(false);
|
||||
try
|
||||
{
|
||||
Console.CancelKeyPress += (sender, eArgs) => // ctrl-c
|
||||
{
|
||||
eArgs.Cancel = true;
|
||||
quitEvent.Set();
|
||||
};
|
||||
AppDomain.CurrentDomain.ProcessExit += (sender, args) =>
|
||||
{
|
||||
log.Info("Received stop request, waiting for exit...");
|
||||
bot?.Stop()?.GetAwaiter().GetResult();
|
||||
};
|
||||
}
|
||||
catch { }
|
||||
|
||||
quitEvent.WaitOne(Timeout.Infinite);
|
||||
log.Info("Waiting for exit...");
|
||||
bot?.Stop()?.GetAwaiter().GetResult();
|
||||
|
||||
return (int)ExitCode.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,111 +1,105 @@
|
||||
using JetKarmaBot.Commands;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using Telegram.Bot;
|
||||
using JetKarmaBot.Commands;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
public class ChatCommandRouter : IRequestHandler
|
||||
{
|
||||
public class ChatCommandRouter : IRequestHandler
|
||||
public class Feature
|
||||
{
|
||||
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; }
|
||||
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; }
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
Me = await Client.GetMeAsync();
|
||||
}
|
||||
public async Task Start()
|
||||
{
|
||||
Me = await Client.GetMeAsync();
|
||||
}
|
||||
|
||||
public Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
log.Debug("Message received");
|
||||
CommandString cmd = ctx.Command;
|
||||
Feature feature = new Feature() { Router = this };
|
||||
ctx.AddFeature(feature);
|
||||
public Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
log.Debug("Message received");
|
||||
CommandString cmd = ctx.Command;
|
||||
Feature feature = new Feature() { Router = this };
|
||||
ctx.AddFeature(feature);
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
if (commands.ContainsKey(cmd.Command))
|
||||
{
|
||||
if (commands.ContainsKey(cmd.Command))
|
||||
{
|
||||
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)
|
||||
{
|
||||
log.Error($"Error while handling command {cmd.Command}!");
|
||||
log.Error(e);
|
||||
}
|
||||
|
||||
return next(ctx);
|
||||
}
|
||||
|
||||
public void Add(IChatCommand c)
|
||||
{
|
||||
log.ConditionalTrace($"Adding command {c.GetType().Name}");
|
||||
foreach (var name in c.Names)
|
||||
{
|
||||
log.ConditionalTrace($"Mounting {c.GetType().Name} to {name}");
|
||||
if (commands.ContainsKey(name))
|
||||
throw new Exception($"command collision for name {name}, commands {commands[name].GetType()} and {c.GetType()}");
|
||||
commands[name] = c;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetHelpText(Locale loc)
|
||||
catch (Exception e)
|
||||
{
|
||||
List<string> pieces = new List<string>();
|
||||
foreach (IChatCommand c in commands.Values.Distinct())
|
||||
{
|
||||
string build = "";
|
||||
List<string> names = c.Names.ToList();
|
||||
for (int i = 0; i < names.Count - 1; i++)
|
||||
{
|
||||
build = build + "/" + names[i] + "\n";
|
||||
}
|
||||
build += "/" + names[names.Count - 1] + " " + string.Join(" ", c.Arguments.Select(x => (!x.Required ? "[" : "") + x.Name + (!x.Required ? "]" : ""))) + " <i>" + getLocalizedCMDDesc(c, loc) + "</i>";
|
||||
pieces.Add(build);
|
||||
}
|
||||
return string.Join("\n", pieces);
|
||||
log.Error($"Error while handling command {cmd.Command}!");
|
||||
log.Error(e);
|
||||
}
|
||||
|
||||
internal string GetHelpTextFor(string commandname, Locale loc)
|
||||
return next(ctx);
|
||||
}
|
||||
|
||||
public void Add(IChatCommand c)
|
||||
{
|
||||
log.ConditionalTrace($"Adding command {c.GetType().Name}");
|
||||
foreach (var name in c.Names)
|
||||
{
|
||||
log.ConditionalTrace($"Mounting {c.GetType().Name} to {name}");
|
||||
if (commands.ContainsKey(name))
|
||||
throw new Exception($"command collision for name {name}, commands {commands[name].GetType()} and {c.GetType()}");
|
||||
commands[name] = c;
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetHelpText(Locale loc)
|
||||
{
|
||||
List<string> pieces = new List<string>();
|
||||
foreach (IChatCommand c in commands.Values.Distinct())
|
||||
{
|
||||
IChatCommand c = commands[commandname];
|
||||
string build = "";
|
||||
List<string> names = c.Names.ToList();
|
||||
for (int i = 0; i < names.Count - 1; i++)
|
||||
{
|
||||
build = build + "/" + names[i] + "\n";
|
||||
}
|
||||
build += "/" + names[names.Count - 1] + " " + string.Join(" ", c.Arguments.Select(x => (!x.Required ? "[" : "") + x.Name + (!x.Required ? "]" : ""))) + " <i>" + getLocalizedCMDDesc(c, loc) + "</i>\n";
|
||||
build += string.Join("\n", c.Arguments.Select(ca => (!ca.Required ? "[" : "") + ca.Name + (!ca.Required ? "]" : "") + ": <i>" + getLocalizedCMDArgDesc(ca, loc) + "</i>"));
|
||||
return build;
|
||||
build += "/" + names[names.Count - 1] + " " + string.Join(" ", c.Arguments.Select(x => (!x.Required ? "[" : "") + x.Name + (!x.Required ? "]" : ""))) + " <i>" + getLocalizedCMDDesc(c, loc) + "</i>";
|
||||
pieces.Add(build);
|
||||
}
|
||||
|
||||
private string getLocalizedCMDDesc(IChatCommand cmd, Locale loc)
|
||||
{
|
||||
if (loc.ContainsKey(cmd.DescriptionID)) return loc[cmd.DescriptionID];
|
||||
else return cmd.Description;
|
||||
}
|
||||
private string getLocalizedCMDArgDesc(ChatCommandArgument arg, Locale loc)
|
||||
{
|
||||
if (loc.ContainsKey(arg.DescriptionID)) return loc[arg.DescriptionID];
|
||||
else return arg.Description;
|
||||
}
|
||||
|
||||
Dictionary<string, IChatCommand> commands = new Dictionary<string, IChatCommand>();
|
||||
return string.Join("\n", pieces);
|
||||
}
|
||||
|
||||
internal string GetHelpTextFor(string commandname, Locale loc)
|
||||
{
|
||||
IChatCommand c = commands[commandname];
|
||||
string build = "";
|
||||
List<string> names = c.Names.ToList();
|
||||
for (int i = 0; i < names.Count - 1; i++)
|
||||
{
|
||||
build = build + "/" + names[i] + "\n";
|
||||
}
|
||||
build += "/" + names[names.Count - 1] + " " + string.Join(" ", c.Arguments.Select(x => (!x.Required ? "[" : "") + x.Name + (!x.Required ? "]" : ""))) + " <i>" + getLocalizedCMDDesc(c, loc) + "</i>\n";
|
||||
build += string.Join("\n", c.Arguments.Select(ca => (!ca.Required ? "[" : "") + ca.Name + (!ca.Required ? "]" : "") + ": <i>" + getLocalizedCMDArgDesc(ca, loc) + "</i>"));
|
||||
return build;
|
||||
}
|
||||
|
||||
private string getLocalizedCMDDesc(IChatCommand cmd, Locale loc)
|
||||
{
|
||||
if (loc.ContainsKey(cmd.DescriptionID)) return loc[cmd.DescriptionID];
|
||||
else return cmd.Description;
|
||||
}
|
||||
private string getLocalizedCMDArgDesc(ChatCommandArgument arg, Locale loc)
|
||||
{
|
||||
if (loc.ContainsKey(arg.DescriptionID)) return loc[arg.DescriptionID];
|
||||
else return arg.Description;
|
||||
}
|
||||
|
||||
Dictionary<string, IChatCommand> commands = new Dictionary<string, IChatCommand>();
|
||||
}
|
||||
|
||||
@ -1,23 +1,17 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Perfusion;
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
public class DatabaseHandler : IRequestHandler
|
||||
{
|
||||
public class DatabaseHandler : IRequestHandler
|
||||
[Inject] private KarmaContextFactory Db;
|
||||
[Inject] private Localization Locale;
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
[Inject] private KarmaContextFactory Db;
|
||||
[Inject] private Localization Locale;
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
using (var db = Db.GetContext())
|
||||
{
|
||||
using (var db = Db.GetContext())
|
||||
{
|
||||
ctx.AddFeature(db); // KarmaContext
|
||||
ctx.AddFeature(Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id))?.Locale ?? "ru-ru"]); // Locale
|
||||
await next(ctx);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
ctx.AddFeature(db); // KarmaContext
|
||||
ctx.AddFeature(Locale[(await db.Chats.FindAsync(ctx.EventArgs.Message.Chat.Id))?.Locale ?? "ru-ru"]); // Locale
|
||||
await next(ctx);
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
public interface IRequestHandler
|
||||
{
|
||||
public interface IRequestHandler
|
||||
Task Handle(RequestContext ctx, Func<RequestContext, Task> next);
|
||||
}
|
||||
public class RequestChain : IRequestHandler
|
||||
{
|
||||
[Inject] private Logger log;
|
||||
List<IRequestHandler> handlerStack = new List<IRequestHandler>();
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next = null)
|
||||
{
|
||||
Task Handle(RequestContext ctx, Func<RequestContext, Task> next);
|
||||
}
|
||||
public class RequestChain : IRequestHandler
|
||||
{
|
||||
[Inject] private Logger log;
|
||||
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) =>
|
||||
{
|
||||
int i = 0;
|
||||
Func<RequestContext, Task> chainNext = null;
|
||||
chainNext = (newCtx) =>
|
||||
if (i == handlerStack.Count)
|
||||
{
|
||||
if (i == handlerStack.Count)
|
||||
{
|
||||
log.ConditionalTrace("(next) End of request chain");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
IRequestHandler handler = handlerStack[i++];
|
||||
log.ConditionalTrace($"(next) Executing handler {handler.GetType().Name}");
|
||||
return handler.Handle(newCtx, chainNext);
|
||||
};
|
||||
await chainNext(ctx);
|
||||
if (next != null)
|
||||
await next(ctx);
|
||||
}
|
||||
public void Add(IRequestHandler handler)
|
||||
{
|
||||
log.ConditionalTrace($"Adding {handler.GetType().Name} to reqchain");
|
||||
handlerStack.Add(handler);
|
||||
}
|
||||
log.ConditionalTrace("(next) End of request chain");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
IRequestHandler handler = handlerStack[i++];
|
||||
log.ConditionalTrace($"(next) Executing handler {handler.GetType().Name}");
|
||||
return handler.Handle(newCtx, chainNext);
|
||||
};
|
||||
await chainNext(ctx);
|
||||
if (next != null)
|
||||
await next(ctx);
|
||||
}
|
||||
public void Add(IRequestHandler handler)
|
||||
{
|
||||
log.ConditionalTrace($"Adding {handler.GetType().Name} to reqchain");
|
||||
handlerStack.Add(handler);
|
||||
}
|
||||
}
|
||||
@ -1,39 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Commands;
|
||||
using JetKarmaBot.Models;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Args;
|
||||
using Telegram.Bot.Types;
|
||||
using JetKarmaBot.Commands;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
public class RequestContext : IServiceProvider
|
||||
{
|
||||
public class RequestContext : IServiceProvider
|
||||
public ITelegramBotClient Client { get; }
|
||||
public Update EventArgs { get; }
|
||||
public CommandString Command { get; }
|
||||
public Dictionary<Type, object> Features { get; } = new Dictionary<Type, object>();
|
||||
public RequestContext(ITelegramBotClient client, Update args, CommandString cmd)
|
||||
{
|
||||
public ITelegramBotClient Client { get; }
|
||||
public Update EventArgs { get; }
|
||||
public CommandString Command { get; }
|
||||
public Dictionary<Type, object> Features { get; } = new Dictionary<Type, object>();
|
||||
public RequestContext(ITelegramBotClient client, Update args, CommandString cmd)
|
||||
{
|
||||
Client = client;
|
||||
EventArgs = args;
|
||||
Command = cmd;
|
||||
}
|
||||
public object GetService(Type serviceType) => Features[serviceType];
|
||||
public T GetFeature<T>() => (T)Features[typeof(T)];
|
||||
public void AddFeature<T>(T feat) => Features[typeof(T)] = feat;
|
||||
|
||||
//Method to reduce WET in commands
|
||||
public Task SendMessage(string text)
|
||||
=> Client.SendMessage(
|
||||
chatId: EventArgs.Message.Chat.Id,
|
||||
text: text,
|
||||
disableNotification: true,
|
||||
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
|
||||
replyParameters: new ReplyParameters { MessageId = EventArgs.Message.MessageId }
|
||||
);
|
||||
Client = client;
|
||||
EventArgs = args;
|
||||
Command = cmd;
|
||||
}
|
||||
public object GetService(Type serviceType) => Features[serviceType];
|
||||
public T GetFeature<T>() => (T)Features[typeof(T)];
|
||||
public void AddFeature<T>(T feat) => Features[typeof(T)] = feat;
|
||||
|
||||
//Method to reduce WET in commands
|
||||
public Task SendMessage(string text)
|
||||
=> Client.SendMessage(
|
||||
chatId: EventArgs.Message.Chat.Id,
|
||||
text: text,
|
||||
disableNotification: true,
|
||||
parseMode: Telegram.Bot.Types.Enums.ParseMode.Html,
|
||||
replyParameters: new ReplyParameters { MessageId = EventArgs.Message.MessageId }
|
||||
);
|
||||
}
|
||||
@ -1,36 +1,33 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
public class SaveData : IRequestHandler
|
||||
{
|
||||
public class SaveData : IRequestHandler
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
KarmaContext db = ctx.GetFeature<KarmaContext>();
|
||||
await AddUserToDatabase(db, ctx.EventArgs.Message.From);
|
||||
if (ctx.EventArgs.Message.ReplyToMessage != null)
|
||||
await AddUserToDatabase(db, ctx.EventArgs.Message.ReplyToMessage.From);
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
KarmaContext db = ctx.GetFeature<KarmaContext>();
|
||||
await AddUserToDatabase(db, ctx.EventArgs.Message.From);
|
||||
if (ctx.EventArgs.Message.ReplyToMessage != null)
|
||||
await AddUserToDatabase(db, ctx.EventArgs.Message.ReplyToMessage.From);
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1,133 +1,126 @@
|
||||
using Perfusion;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using JetKarmaBot.Models;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using JetKarmaBot.Models;
|
||||
|
||||
namespace JetKarmaBot.Services.Handling
|
||||
namespace JetKarmaBot.Services.Handling;
|
||||
|
||||
[Singleton]
|
||||
public class TimeoutManager : IRequestHandler
|
||||
{
|
||||
[Singleton]
|
||||
public class TimeoutManager : IRequestHandler
|
||||
public class Feature
|
||||
{
|
||||
public class Feature
|
||||
public double Multiplier = 1;
|
||||
}
|
||||
public class PreDbThrowout : IRequestHandler
|
||||
{
|
||||
public TimeoutManager Timeout { get; }
|
||||
public PreDbThrowout(TimeoutManager timeout)
|
||||
{
|
||||
public double Multiplier = 1;
|
||||
}
|
||||
public class PreDbThrowout : IRequestHandler
|
||||
{
|
||||
public TimeoutManager Timeout { get; }
|
||||
public PreDbThrowout(TimeoutManager timeout)
|
||||
{
|
||||
Timeout = timeout;
|
||||
}
|
||||
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
var uid = ctx.EventArgs.Message.From.Id;
|
||||
if (Timeout.TimeoutCache.TryGetValue(uid, out var stats))
|
||||
{
|
||||
DateTime debtLimit = DateTime.Now.AddSeconds(Timeout.cfg.Timeout.DebtLimitSeconds);
|
||||
if (debtLimit < stats.CooldownDate && stats.TimeoutMessaged)
|
||||
return;
|
||||
}
|
||||
await next(ctx);
|
||||
}
|
||||
}
|
||||
public class TimeoutStats
|
||||
{
|
||||
public DateTime CooldownDate;
|
||||
public bool TimeoutMessaged;
|
||||
public DateTime PreviousAwardDate;
|
||||
}
|
||||
[Inject] private KarmaContextFactory Db;
|
||||
[Inject] private Config cfg;
|
||||
[Inject] private Localization Locale;
|
||||
[Inject] private Logger log;
|
||||
public Dictionary<long, TimeoutStats> TimeoutCache = new ();
|
||||
private async Task ApplyCost(string name, bool succeded, long uid, KarmaContext db, Feature feature)
|
||||
{
|
||||
if (feature.Multiplier == 0)
|
||||
return;
|
||||
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)
|
||||
//Programming error
|
||||
throw new NotImplementedException();
|
||||
TimeoutCache[uid].CooldownDate = (TimeoutCache[uid].CooldownDate <= DateTime.Now ? DateTime.Now : TimeoutCache[uid].CooldownDate)
|
||||
.AddSeconds(feature.Multiplier * costSeconds);
|
||||
TimeoutCache[uid].TimeoutMessaged = false;
|
||||
}
|
||||
|
||||
private async Task PopulateStats(long uid, KarmaContext db)
|
||||
{
|
||||
if (!TimeoutCache.ContainsKey(uid))
|
||||
{
|
||||
log.ConditionalTrace($"User {uid} not present: saving to cache");
|
||||
TimeoutCache[uid] = new TimeoutStats()
|
||||
{
|
||||
CooldownDate = (await db.Users.FindAsync(uid))?.CooldownDate ?? DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
public async Task Save(CancellationToken ct = default(CancellationToken))
|
||||
{
|
||||
log.Debug("Saving timeout info to database");
|
||||
using (KarmaContext db = Db.GetContext())
|
||||
{
|
||||
foreach (var i in TimeoutCache.Keys)
|
||||
{
|
||||
(await db.Users.FindAsync(new object[] { i }, ct)).CooldownDate = TimeoutCache[i].CooldownDate;
|
||||
}
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
log.Debug("Saved timeout info to database");
|
||||
}
|
||||
public async Task SaveLoop(CancellationToken ct = default(CancellationToken))
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(cfg.Timeout.SaveIntervalSeconds * 1000, ct);
|
||||
await Save(ct);
|
||||
}
|
||||
Timeout = timeout;
|
||||
}
|
||||
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
var uid = ctx.EventArgs.Message.From.Id;
|
||||
KarmaContext db = ctx.GetFeature<KarmaContext>();
|
||||
await PopulateStats(uid, db);
|
||||
DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds);
|
||||
if (debtLimit < TimeoutCache[uid].CooldownDate)
|
||||
if (Timeout.TimeoutCache.TryGetValue(uid, out var stats))
|
||||
{
|
||||
if (!TimeoutCache[uid].TimeoutMessaged)
|
||||
{
|
||||
Locale currentLocale = ctx.GetFeature<Locale>();
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]);
|
||||
TimeoutCache[uid].TimeoutMessaged = true;
|
||||
}
|
||||
return;
|
||||
DateTime debtLimit = DateTime.Now.AddSeconds(Timeout.cfg.Timeout.DebtLimitSeconds);
|
||||
if (debtLimit < stats.CooldownDate && stats.TimeoutMessaged)
|
||||
return;
|
||||
}
|
||||
Feature feature = new Feature();
|
||||
ctx.AddFeature(feature);
|
||||
|
||||
await next(ctx);
|
||||
|
||||
var routerFeature = ctx.GetFeature<ChatCommandRouter.Feature>();
|
||||
await ApplyCost(getTypeName(routerFeature.CommandType), routerFeature.Succeded, uid, db, feature);
|
||||
}
|
||||
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)) + ">" : "");
|
||||
}
|
||||
}
|
||||
public class TimeoutStats
|
||||
{
|
||||
public DateTime CooldownDate;
|
||||
public bool TimeoutMessaged;
|
||||
public DateTime PreviousAwardDate;
|
||||
}
|
||||
[Inject] private KarmaContextFactory Db;
|
||||
[Inject] private Config cfg;
|
||||
[Inject] private Localization Locale;
|
||||
[Inject] private Logger log;
|
||||
public Dictionary<long, TimeoutStats> TimeoutCache = new ();
|
||||
private async Task ApplyCost(string name, bool succeded, long uid, KarmaContext db, Feature feature)
|
||||
{
|
||||
if (feature.Multiplier == 0)
|
||||
return;
|
||||
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)
|
||||
//Programming error
|
||||
throw new NotImplementedException();
|
||||
TimeoutCache[uid].CooldownDate = (TimeoutCache[uid].CooldownDate <= DateTime.Now ? DateTime.Now : TimeoutCache[uid].CooldownDate)
|
||||
.AddSeconds(feature.Multiplier * costSeconds);
|
||||
TimeoutCache[uid].TimeoutMessaged = false;
|
||||
}
|
||||
|
||||
private async Task PopulateStats(long uid, KarmaContext db)
|
||||
{
|
||||
if (!TimeoutCache.ContainsKey(uid))
|
||||
{
|
||||
log.ConditionalTrace($"User {uid} not present: saving to cache");
|
||||
TimeoutCache[uid] = new TimeoutStats()
|
||||
{
|
||||
CooldownDate = (await db.Users.FindAsync(uid))?.CooldownDate ?? DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
public async Task Save(CancellationToken ct = default(CancellationToken))
|
||||
{
|
||||
log.Debug("Saving timeout info to database");
|
||||
using (KarmaContext db = Db.GetContext())
|
||||
{
|
||||
foreach (var i in TimeoutCache.Keys)
|
||||
{
|
||||
(await db.Users.FindAsync(new object[] { i }, ct)).CooldownDate = TimeoutCache[i].CooldownDate;
|
||||
}
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
log.Debug("Saved timeout info to database");
|
||||
}
|
||||
public async Task SaveLoop(CancellationToken ct = default(CancellationToken))
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(cfg.Timeout.SaveIntervalSeconds * 1000, ct);
|
||||
await Save(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Handle(RequestContext ctx, Func<RequestContext, Task> next)
|
||||
{
|
||||
var uid = ctx.EventArgs.Message.From.Id;
|
||||
KarmaContext db = ctx.GetFeature<KarmaContext>();
|
||||
await PopulateStats(uid, db);
|
||||
DateTime debtLimit = DateTime.Now.AddSeconds(cfg.Timeout.DebtLimitSeconds);
|
||||
if (debtLimit < TimeoutCache[uid].CooldownDate)
|
||||
{
|
||||
if (!TimeoutCache[uid].TimeoutMessaged)
|
||||
{
|
||||
Locale currentLocale = ctx.GetFeature<Locale>();
|
||||
await ctx.SendMessage(currentLocale["jetkarmabot.ratelimit"]);
|
||||
TimeoutCache[uid].TimeoutMessaged = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Feature feature = new Feature();
|
||||
ctx.AddFeature(feature);
|
||||
|
||||
await next(ctx);
|
||||
|
||||
var routerFeature = ctx.GetFeature<ChatCommandRouter.Feature>();
|
||||
await ApplyCost(getTypeName(routerFeature.CommandType), routerFeature.Succeded, uid, db, feature);
|
||||
}
|
||||
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)) + ">" : "");
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,10 @@
|
||||
using JetKarmaBot.Models;
|
||||
using Perfusion;
|
||||
|
||||
namespace JetKarmaBot.Services
|
||||
namespace JetKarmaBot.Services;
|
||||
|
||||
public class KarmaContextFactory
|
||||
{
|
||||
public class KarmaContextFactory
|
||||
{
|
||||
[Inject] IContainer C { get; set; }
|
||||
[Inject] IContainer C { get; set; }
|
||||
|
||||
public KarmaContext GetContext() => C.GetInstance<KarmaContext>();
|
||||
}
|
||||
public KarmaContext GetContext() => C.GetInstance<KarmaContext>();
|
||||
}
|
||||
|
||||
@ -1,171 +1,166 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public class Localization : IReadOnlyDictionary<string, Locale>
|
||||
{
|
||||
public class Localization : IReadOnlyDictionary<string, Locale>
|
||||
private Dictionary<string, Locale> locales = new Dictionary<string, Locale>();
|
||||
|
||||
public Localization(IContainer c)
|
||||
{
|
||||
private Dictionary<string, Locale> locales = new Dictionary<string, Locale>();
|
||||
c.ResolveObject(this);
|
||||
log.Info("Initializing...");
|
||||
string langsFolder = "lang";
|
||||
if (!Directory.Exists(langsFolder))
|
||||
Directory.CreateDirectory(langsFolder);
|
||||
|
||||
public Localization(IContainer c)
|
||||
foreach (string langFilePath in Directory.EnumerateFiles(langsFolder, "*.json"))
|
||||
{
|
||||
c.ResolveObject(this);
|
||||
log.Info("Initializing...");
|
||||
string langsFolder = "lang";
|
||||
if (!Directory.Exists(langsFolder))
|
||||
Directory.CreateDirectory(langsFolder);
|
||||
|
||||
foreach (string langFilePath in Directory.EnumerateFiles(langsFolder, "*.json"))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
string langName = Path.GetFileNameWithoutExtension(langFilePath);
|
||||
string langKey = langName.ToLowerInvariant();
|
||||
locales[langKey] = new Locale(JObject.Parse(File.ReadAllText(langFilePath)), langKey);
|
||||
log.Debug("Found " + langName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error($"Error while parsing {langFilePath}!");
|
||||
log.Error(e);
|
||||
}
|
||||
string langName = Path.GetFileNameWithoutExtension(langFilePath);
|
||||
string langKey = langName.ToLowerInvariant();
|
||||
locales[langKey] = new Locale(JObject.Parse(File.ReadAllText(langFilePath)), langKey);
|
||||
log.Debug("Found " + langName);
|
||||
}
|
||||
|
||||
if (locales.Any())
|
||||
log.Info("Initialized!");
|
||||
else
|
||||
throw new FileNotFoundException($"No locales found in {langsFolder}!");
|
||||
}
|
||||
|
||||
public Locale this[string locale]
|
||||
{
|
||||
get
|
||||
catch (Exception e)
|
||||
{
|
||||
locale = locale.ToLowerInvariant();
|
||||
return locales[locale];
|
||||
log.Error($"Error while parsing {langFilePath}!");
|
||||
log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private Logger log;
|
||||
if (locales.Any())
|
||||
log.Info("Initialized!");
|
||||
else
|
||||
throw new FileNotFoundException($"No locales found in {langsFolder}!");
|
||||
}
|
||||
|
||||
public Locale FindByCommonName(string name)
|
||||
{
|
||||
log.ConditionalTrace("Trying to find locale " + name);
|
||||
foreach (Locale l in locales.Values)
|
||||
{
|
||||
if (l.CommonNames.Contains(name))
|
||||
{
|
||||
log.ConditionalTrace("Found locale " + l.Name);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
// Try to find as locale prefix
|
||||
IEnumerable<Locale> matchinglocales = locales.Values.Where(x => x.Name.StartsWith(name + "-"));
|
||||
if (matchinglocales.Count() > 1)
|
||||
{
|
||||
LocalizationException l = new LocalizationException("Too many locales");
|
||||
l.Data["LocaleNames"] = matchinglocales.ToArray();
|
||||
throw l;
|
||||
}
|
||||
else if (matchinglocales.Count() == 1)
|
||||
return matchinglocales.First();
|
||||
log.Warn("Failed to find locale " + name);
|
||||
return null;
|
||||
}
|
||||
public bool ContainsLocale(string locale)
|
||||
public Locale this[string locale]
|
||||
{
|
||||
get
|
||||
{
|
||||
locale = locale.ToLowerInvariant();
|
||||
return locales.ContainsKey(locale);
|
||||
}
|
||||
|
||||
void Log(Exception e) => Console.WriteLine(e);
|
||||
|
||||
public IEnumerable<string> Keys => locales.Keys;
|
||||
|
||||
public IEnumerable<Locale> Values => locales.Values;
|
||||
|
||||
public int Count => locales.Count;
|
||||
|
||||
public bool ContainsKey(string key) => locales.ContainsKey(key.ToLower());
|
||||
|
||||
public bool TryGetValue(string key, out Locale value)
|
||||
{
|
||||
return locales.TryGetValue(key.ToLower(), out value);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, Locale>> GetEnumerator()
|
||||
{
|
||||
return locales.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
|
||||
}
|
||||
public class Locale : IReadOnlyDictionary<string, string>
|
||||
{
|
||||
private Dictionary<string, string> locale;
|
||||
private string localeName;
|
||||
private string[] commonNames;
|
||||
private string note;
|
||||
|
||||
public Locale(JObject locale, string localeName)
|
||||
{
|
||||
this.locale = locale.Property("strings").Value.ToObject<Dictionary<string, string>>();
|
||||
this.localeName = localeName;
|
||||
this.commonNames = locale.Property("names").Value.ToObject<string[]>();
|
||||
if (locale.ContainsKey("note")) this.note = locale.Property("note").Value.ToObject<string>();
|
||||
}
|
||||
public string[] CommonNames => commonNames;
|
||||
public string Name => localeName;
|
||||
public bool HasNote => note != null;
|
||||
|
||||
public string Note => note;
|
||||
|
||||
public IEnumerable<string> Keys => ((IReadOnlyDictionary<string, string>)locale).Keys;
|
||||
|
||||
public IEnumerable<string> Values => ((IReadOnlyDictionary<string, string>)locale).Values;
|
||||
|
||||
public int Count => ((IReadOnlyDictionary<string, string>)locale).Count;
|
||||
|
||||
public string this[string name] => locale.ContainsKey(name) ? locale[name] : "Unmapped locale key: " + name;
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).GetEnumerator();
|
||||
return locales[locale];
|
||||
}
|
||||
}
|
||||
[System.Serializable]
|
||||
public class LocalizationException : Exception
|
||||
|
||||
[Inject]
|
||||
private Logger log;
|
||||
|
||||
public Locale FindByCommonName(string name)
|
||||
{
|
||||
public LocalizationException() { }
|
||||
public LocalizationException(string message) : base(message) { }
|
||||
public LocalizationException(string message, Exception inner) : base(message, inner) { }
|
||||
protected LocalizationException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context) { }
|
||||
log.ConditionalTrace("Trying to find locale " + name);
|
||||
foreach (Locale l in locales.Values)
|
||||
{
|
||||
if (l.CommonNames.Contains(name))
|
||||
{
|
||||
log.ConditionalTrace("Found locale " + l.Name);
|
||||
return l;
|
||||
}
|
||||
}
|
||||
// Try to find as locale prefix
|
||||
IEnumerable<Locale> matchinglocales = locales.Values.Where(x => x.Name.StartsWith(name + "-"));
|
||||
if (matchinglocales.Count() > 1)
|
||||
{
|
||||
LocalizationException l = new LocalizationException("Too many locales");
|
||||
l.Data["LocaleNames"] = matchinglocales.ToArray();
|
||||
throw l;
|
||||
}
|
||||
else if (matchinglocales.Count() == 1)
|
||||
return matchinglocales.First();
|
||||
log.Warn("Failed to find locale " + name);
|
||||
return null;
|
||||
}
|
||||
public bool ContainsLocale(string locale)
|
||||
{
|
||||
locale = locale.ToLowerInvariant();
|
||||
return locales.ContainsKey(locale);
|
||||
}
|
||||
|
||||
void Log(Exception e) => Console.WriteLine(e);
|
||||
|
||||
public IEnumerable<string> Keys => locales.Keys;
|
||||
|
||||
public IEnumerable<Locale> Values => locales.Values;
|
||||
|
||||
public int Count => locales.Count;
|
||||
|
||||
public bool ContainsKey(string key) => locales.ContainsKey(key.ToLower());
|
||||
|
||||
public bool TryGetValue(string key, out Locale value)
|
||||
{
|
||||
return locales.TryGetValue(key.ToLower(), out value);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, Locale>> GetEnumerator()
|
||||
{
|
||||
return locales.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
|
||||
}
|
||||
public class Locale : IReadOnlyDictionary<string, string>
|
||||
{
|
||||
private Dictionary<string, string> locale;
|
||||
private string localeName;
|
||||
private string[] commonNames;
|
||||
private string note;
|
||||
|
||||
public Locale(JObject locale, string localeName)
|
||||
{
|
||||
this.locale = locale.Property("strings").Value.ToObject<Dictionary<string, string>>();
|
||||
this.localeName = localeName;
|
||||
this.commonNames = locale.Property("names").Value.ToObject<string[]>();
|
||||
if (locale.ContainsKey("note")) this.note = locale.Property("note").Value.ToObject<string>();
|
||||
}
|
||||
public string[] CommonNames => commonNames;
|
||||
public string Name => localeName;
|
||||
public bool HasNote => note != null;
|
||||
|
||||
public string Note => note;
|
||||
|
||||
public IEnumerable<string> Keys => ((IReadOnlyDictionary<string, string>)locale).Keys;
|
||||
|
||||
public IEnumerable<string> Values => ((IReadOnlyDictionary<string, string>)locale).Values;
|
||||
|
||||
public int Count => ((IReadOnlyDictionary<string, string>)locale).Count;
|
||||
|
||||
public string this[string name] => locale.ContainsKey(name) ? locale[name] : "Unmapped locale key: " + name;
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IReadOnlyDictionary<string, string>)locale).GetEnumerator();
|
||||
}
|
||||
}
|
||||
[System.Serializable]
|
||||
public class LocalizationException : Exception
|
||||
{
|
||||
public LocalizationException() { }
|
||||
public LocalizationException(string message) : base(message) { }
|
||||
public LocalizationException(string message, Exception inner) : base(message, inner) { }
|
||||
protected LocalizationException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
@ -1,22 +1,18 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public class LogInfo : ObjectInfo
|
||||
{
|
||||
public class LogInfo : ObjectInfo
|
||||
{
|
||||
public override ObjectInfo Clone() => new LogInfo();
|
||||
public override ObjectInfo Clone() => new LogInfo();
|
||||
|
||||
public override object GetInstance(IContainer c, Type requester = null)
|
||||
{
|
||||
return LogManager.GetLogger(requester != null ? getTypeName(requester) : "<type unspecified>");
|
||||
}
|
||||
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)) + ">" : "");
|
||||
}
|
||||
public override object GetInstance(IContainer c, Type requester = null)
|
||||
{
|
||||
return LogManager.GetLogger(requester != null ? getTypeName(requester) : "<type unspecified>");
|
||||
}
|
||||
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)) + ">" : "");
|
||||
}
|
||||
}
|
||||
@ -1,103 +1,100 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog;
|
||||
using Perfusion;
|
||||
|
||||
namespace JetKarmaBot
|
||||
namespace JetKarmaBot;
|
||||
|
||||
public class NLoggerFactory : ILoggerFactory
|
||||
{
|
||||
public class NLoggerFactory : ILoggerFactory
|
||||
[Inject]
|
||||
private NLoggerProvider c;
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
[Inject]
|
||||
private NLoggerProvider c;
|
||||
public void AddProvider(ILoggerProvider provider)
|
||||
{
|
||||
}
|
||||
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) => c.CreateLogger(categoryName);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
public class NLoggerProvider : ILoggerProvider
|
||||
{
|
||||
[Inject]
|
||||
private Container c;
|
||||
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) => new LoggerVirtualizer(LogManager.GetLogger(categoryName));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class LoggerVirtualizer : Microsoft.Extensions.Logging.ILogger
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) => c.CreateLogger(categoryName);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
public class NLoggerProvider : ILoggerProvider
|
||||
{
|
||||
[Inject]
|
||||
private Container c;
|
||||
|
||||
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) => new LoggerVirtualizer(LogManager.GetLogger(categoryName));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class LoggerVirtualizer : Microsoft.Extensions.Logging.ILogger
|
||||
{
|
||||
private Logger logger;
|
||||
|
||||
public LoggerVirtualizer(Logger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private Microsoft.Extensions.Logging.LogLevel getAppropriate(NLog.LogLevel level)
|
||||
{
|
||||
if (level == NLog.LogLevel.Trace)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Trace;
|
||||
else if (level == NLog.LogLevel.Debug)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Debug;
|
||||
else if (level == NLog.LogLevel.Info)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Information;
|
||||
else if (level == NLog.LogLevel.Warn)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Warning;
|
||||
else if (level == NLog.LogLevel.Error)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Error;
|
||||
else if (level == NLog.LogLevel.Fatal)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Critical;
|
||||
else if (level == NLog.LogLevel.Off)
|
||||
return Microsoft.Extensions.Logging.LogLevel.None;
|
||||
else
|
||||
return Microsoft.Extensions.Logging.LogLevel.None;
|
||||
}
|
||||
private NLog.LogLevel getAppropriate(Microsoft.Extensions.Logging.LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Microsoft.Extensions.Logging.LogLevel.Trace:
|
||||
return NLog.LogLevel.Trace;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Debug:
|
||||
return NLog.LogLevel.Debug;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Information:
|
||||
return NLog.LogLevel.Info;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Warning:
|
||||
return NLog.LogLevel.Warn;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Error:
|
||||
return NLog.LogLevel.Error;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Critical:
|
||||
return NLog.LogLevel.Fatal;
|
||||
default:
|
||||
return NLog.LogLevel.Off;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) => new SomeDisposable();
|
||||
|
||||
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
|
||||
{
|
||||
return logger.IsEnabled(getAppropriate(logLevel));
|
||||
}
|
||||
|
||||
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
string message = state.ToString();
|
||||
if (exception != null) logger.Log(getAppropriate(logLevel), exception, formatter(state, exception));
|
||||
else logger.Log(getAppropriate(logLevel), state);
|
||||
}
|
||||
}
|
||||
public class SomeDisposable : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
private Logger logger;
|
||||
|
||||
public LoggerVirtualizer(Logger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private Microsoft.Extensions.Logging.LogLevel getAppropriate(NLog.LogLevel level)
|
||||
{
|
||||
if (level == NLog.LogLevel.Trace)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Trace;
|
||||
else if (level == NLog.LogLevel.Debug)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Debug;
|
||||
else if (level == NLog.LogLevel.Info)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Information;
|
||||
else if (level == NLog.LogLevel.Warn)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Warning;
|
||||
else if (level == NLog.LogLevel.Error)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Error;
|
||||
else if (level == NLog.LogLevel.Fatal)
|
||||
return Microsoft.Extensions.Logging.LogLevel.Critical;
|
||||
else if (level == NLog.LogLevel.Off)
|
||||
return Microsoft.Extensions.Logging.LogLevel.None;
|
||||
else
|
||||
return Microsoft.Extensions.Logging.LogLevel.None;
|
||||
}
|
||||
private NLog.LogLevel getAppropriate(Microsoft.Extensions.Logging.LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case Microsoft.Extensions.Logging.LogLevel.Trace:
|
||||
return NLog.LogLevel.Trace;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Debug:
|
||||
return NLog.LogLevel.Debug;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Information:
|
||||
return NLog.LogLevel.Info;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Warning:
|
||||
return NLog.LogLevel.Warn;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Error:
|
||||
return NLog.LogLevel.Error;
|
||||
case Microsoft.Extensions.Logging.LogLevel.Critical:
|
||||
return NLog.LogLevel.Fatal;
|
||||
default:
|
||||
return NLog.LogLevel.Off;
|
||||
}
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state) => new SomeDisposable();
|
||||
|
||||
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel)
|
||||
{
|
||||
return logger.IsEnabled(getAppropriate(logLevel));
|
||||
}
|
||||
|
||||
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
string message = state.ToString();
|
||||
if (exception != null) logger.Log(getAppropriate(logLevel), exception, formatter(state, exception));
|
||||
else logger.Log(getAppropriate(logLevel), state);
|
||||
}
|
||||
}
|
||||
public class SomeDisposable : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user