mirror of
https://github.com/Jetsparrow/karmabot.git
synced 2026-01-21 09:06:09 +03:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bbc85ebf0 | ||
|
|
178f1ee089 | ||
|
|
ff63dbe386 | ||
|
|
7bef7255f8 | ||
|
|
45b94e7561 | ||
|
|
79e78f41c5 | ||
|
|
8322f4f871 | ||
|
|
61166ea0c9 | ||
|
|
e7c53fa2ad | ||
|
|
1ee1abc672 | ||
|
|
71a2328eb1 | ||
|
|
4353428d41 | ||
|
|
bf8778c7d9 | ||
|
|
2cd40cb85d | ||
|
|
68c2715c17 | ||
| bcda3b9282 | |||
| e2ee46eba9 | |||
|
|
d410d7ae47 | ||
| e5d81925f0 | |||
|
|
6835ba4843 | ||
|
|
d986a59844 | ||
|
|
7f9e5918d4 | ||
|
|
4be28f3c1b | ||
|
|
83d1b458af | ||
|
|
b93d20e07e | ||
|
|
fadd970c5d | ||
|
|
09d2ce7bb6 | ||
|
|
98361572dc | ||
|
|
08ebdbaa37 | ||
|
|
0a3d969bec | ||
|
|
fdfea2bbb1 | ||
|
|
3e7ace4a7e | ||
|
|
4aee536c63 | ||
|
|
6a4ee6e791 | ||
|
|
1159af1785 | ||
|
|
a9cca3ee59 | ||
|
|
51dce7f588 | ||
| d20cc89f28 | |||
| fd732d8719 | |||
|
|
d95dae4ddb | ||
| d3bbfe8c52 | |||
|
|
23dbd6ca17 | ||
|
|
c8adaaa0d4 | ||
|
|
bff68a9748 | ||
|
|
12b90d010f | ||
| efb1a532bd | |||
|
|
8918d576c4 | ||
|
|
6fb4fdf544 | ||
|
|
58c6d52232 | ||
|
|
8d0d70553c | ||
| 0025be1474 | |||
| 2fbfde551b | |||
| 37236d50ff | |||
| 4947351b89 | |||
| d535d2e8f4 | |||
| fff2c0d3fa | |||
| a55f7a1d97 | |||
|
|
1394062930 |
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "perfusion"]
|
||||||
|
path = perfusion
|
||||||
|
url = https://github.com/PorezPorezych/perfusion
|
||||||
@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio 15
|
# Visual Studio 15
|
||||||
VisualStudioVersion = 15.0.28010.2036
|
VisualStudioVersion = 15.0.28010.2036
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JetKarmaBot", "JetKarmaBot\JetKarmaBot.csproj", "{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JetKarmaBot", "JetKarmaBot\JetKarmaBot.csproj", "{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Perfusion", "perfusion\Perfusion\Perfusion.csproj", "{C3525FCF-0EE0-4802-B18D-83233BF0C85A}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -15,6 +17,10 @@ Global
|
|||||||
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Release|Any CPU.Build.0 = Release|Any CPU
|
{729E88EE-BE5E-4D12-B83F-EDC5FC5E2D07}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C3525FCF-0EE0-4802-B18D-83233BF0C85A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3525FCF-0EE0-4802-B18D-83233BF0C85A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C3525FCF-0EE0-4802-B18D-83233BF0C85A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C3525FCF-0EE0-4802-B18D-83233BF0C85A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@ -1,36 +1,124 @@
|
|||||||
using JetKarmaBot.Commands;
|
using JetKarmaBot.Commands;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using NLog;
|
||||||
|
using Perfusion;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Args;
|
using Telegram.Bot.Args;
|
||||||
|
|
||||||
namespace JetKarmaBot
|
namespace JetKarmaBot
|
||||||
{
|
{
|
||||||
class ChatCommandRouter
|
public class ChatCommandRouter
|
||||||
{
|
{
|
||||||
Dictionary<string, IChatCommand> commands = new Dictionary<string, IChatCommand>();
|
Telegram.Bot.Types.User BotUser { get; }
|
||||||
|
[Inject] private Logger log;
|
||||||
|
[Inject] private KarmaContextFactory Db;
|
||||||
|
[Inject] private TelegramBotClient Client { get; set; }
|
||||||
|
|
||||||
|
public ChatCommandRouter(Telegram.Bot.Types.User botUser)
|
||||||
|
{
|
||||||
|
BotUser = botUser;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Execute(object sender, MessageEventArgs args)
|
public bool Execute(object sender, MessageEventArgs args)
|
||||||
{
|
{
|
||||||
|
log.Debug("Message received");
|
||||||
var text = args.Message.Text;
|
var text = args.Message.Text;
|
||||||
|
if (CommandString.TryParse(text, out var cmd))
|
||||||
if (CommandString.TryParse(text, out var cs))
|
|
||||||
{
|
{
|
||||||
if (commands.ContainsKey(cs.Name))
|
if (cmd.UserName != null && cmd.UserName != BotUser.Username)
|
||||||
return commands[cs.Name].Execute(sender,args);
|
{
|
||||||
|
// directed not at us!
|
||||||
|
log.Debug("Message not directed at us");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (commands.ContainsKey(cmd.Command))
|
||||||
|
{
|
||||||
|
log.Debug($"Handling message via {commands[cmd.Command].GetType().Name}");
|
||||||
|
return commands[cmd.Command].Execute(cmd, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"Error while handling command {cmd.Command}!");
|
||||||
|
log.Error(e);
|
||||||
|
ReportToAdministratorChats($"Error while handling command {cmd.Command}!\n{e.ToString()}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReportToAdministratorChats(string text)
|
||||||
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
foreach (long chatid in db.Chats.Where(x => x.IsAdministrator).Select(x => x.ChatId))
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(chatid, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Add(IChatCommand c)
|
public void Add(IChatCommand c)
|
||||||
{
|
{
|
||||||
|
log.ConditionalTrace($"Adding command {c.GetType().Name}");
|
||||||
foreach (var name in c.Names)
|
foreach (var name in c.Names)
|
||||||
{
|
{
|
||||||
|
log.ConditionalTrace($"Mounting {c.GetType().Name} to {name}");
|
||||||
if (commands.ContainsKey(name))
|
if (commands.ContainsKey(name))
|
||||||
throw new Exception($"command collision for name {name}, commands {commands[name].GetType()} and {c.GetType()}");
|
throw new Exception($"command collision for name {name}, commands {commands[name].GetType()} and {c.GetType()}");
|
||||||
commands[name] = c;
|
commands[name] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal string GetHelpText(Locale loc)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,34 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Args;
|
using Telegram.Bot.Args;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Perfusion;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
namespace JetKarmaBot.Commands
|
namespace JetKarmaBot.Commands
|
||||||
{
|
{
|
||||||
class AwardCommand : IChatCommand
|
class AwardCommand : IChatCommand
|
||||||
{
|
{
|
||||||
public IReadOnlyCollection<string> Names => new[] { "award", "revoke"};
|
public IReadOnlyCollection<string> Names => new[] { "award", "revoke" };
|
||||||
|
[Inject]
|
||||||
|
private Logger log;
|
||||||
|
|
||||||
public bool Execute(object sender, MessageEventArgs args)
|
public bool Execute(CommandString cmd, MessageEventArgs args)
|
||||||
{
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
var currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
if (args.Message.ReplyToMessage == null)
|
if (args.Message.ReplyToMessage == null)
|
||||||
{
|
{
|
||||||
Client.SendTextMessageAsync(args.Message.Chat.Id, "Please use this command in reply to another user.");
|
Client.SendTextMessageAsync(args.Message.Chat.Id, currentLocale["jetkarmabot.award.errawardnoreply"]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var awarder = args.Message.From;
|
var awarder = args.Message.From;
|
||||||
var recipient = args.Message.ReplyToMessage.From;
|
var recipient = args.Message.ReplyToMessage.From;
|
||||||
|
|
||||||
|
bool awarding = cmd.Command == "award";
|
||||||
|
|
||||||
if (awarder.Id == recipient.Id)
|
if (awarder.Id == recipient.Id)
|
||||||
{
|
{
|
||||||
Client.SendTextMessageAsync(
|
Client.SendTextMessageAsync(
|
||||||
args.Message.Chat.Id,
|
args.Message.Chat.Id,
|
||||||
"Please stop playing with yourself.",
|
currentLocale["jetkarmabot.award.errawardself"],
|
||||||
replyToMessageId: args.Message.MessageId);
|
replyToMessageId: args.Message.MessageId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -37,36 +45,88 @@ namespace JetKarmaBot.Commands
|
|||||||
{
|
{
|
||||||
Client.SendTextMessageAsync(
|
Client.SendTextMessageAsync(
|
||||||
args.Message.Chat.Id,
|
args.Message.Chat.Id,
|
||||||
"I am a bot, and have no use for your foolish fake internet points.",
|
awarding
|
||||||
|
? currentLocale["jetkarmabot.award.errawardbot"]
|
||||||
|
: currentLocale["jetkarmabot.award.errrevokebot"],
|
||||||
replyToMessageId: args.Message.MessageId);
|
replyToMessageId: args.Message.MessageId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var text = args.Message.Text;
|
var text = args.Message.Text;
|
||||||
var command = CommandString.Parse(text);
|
var awardTypeText = cmd.Parameters.FirstOrDefault();
|
||||||
var awardTypeId = Db.GetAwardTypeId(command.Parameters.FirstOrDefault());
|
global::JetKarmaBot.Models.AwardType awardType = awardTypeText != null
|
||||||
var awardType = Db.AwardTypes[awardTypeId];
|
? db.AwardTypes.First(at => at.CommandName == awardTypeText)
|
||||||
|
: db.AwardTypes.Find((sbyte)1);
|
||||||
|
DateTime cutoff = DateTime.Now - TimeSpan.FromMinutes(5);
|
||||||
|
if (db.Awards.Where(x => x.Date > cutoff && x.FromId == awarder.Id).Count() >= 10)
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
currentLocale["jetkarmabot.award.ratelimit"],
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
db.Awards.Add(new Models.Award()
|
||||||
|
{
|
||||||
|
AwardTypeId = awardType.AwardTypeId,
|
||||||
|
Amount = (sbyte)(awarding ? 1 : -1),
|
||||||
|
FromId = awarder.Id,
|
||||||
|
ToId = recipient.Id,
|
||||||
|
ChatId = args.Message.Chat.Id
|
||||||
|
});
|
||||||
|
log.Debug($"Awarded {(awarding ? 1 : -1)}{awardType.Symbol} to {recipient.Username}");
|
||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
Db.AddAward(awardTypeId, awarder.Id, recipient.Id, args.Message.Chat.Id, 1);
|
string message = awarding
|
||||||
|
? string.Format(currentLocale["jetkarmabot.award.awardmessage"], getLocalizedName(awardType, currentLocale), "@" + recipient.Username)
|
||||||
|
: string.Format(currentLocale["jetkarmabot.award.revokemessage"], getLocalizedName(awardType, currentLocale), "@" + recipient.Username);
|
||||||
|
|
||||||
|
var currentCount = db.Awards
|
||||||
|
.Where(aw => aw.ToId == recipient.Id && aw.AwardTypeId == awardType.AwardTypeId)
|
||||||
|
.Sum(aw => aw.Amount);
|
||||||
|
|
||||||
|
var response = message + "\n" + String.Format(currentLocale["jetkarmabot.award.statustext"], "@" + recipient.Username, currentCount, awardType.Symbol);
|
||||||
|
|
||||||
var response = $"Awarded a {awardType.Name} to {recipient.Username}!\n" +
|
|
||||||
$"{recipient.Username} is at {Db.CountAwards(recipient.Id, awardTypeId)}{awardType.Symbol} now.";
|
|
||||||
Client.SendTextMessageAsync(
|
Client.SendTextMessageAsync(
|
||||||
args.Message.Chat.Id,
|
args.Message.Chat.Id,
|
||||||
response,
|
response,
|
||||||
replyToMessageId: args.Message.MessageId);
|
replyToMessageId: args.Message.MessageId);
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Db Db { get; }
|
[Inject] KarmaContextFactory Db { get; set; }
|
||||||
TelegramBotClient Client { get; }
|
[Inject] TelegramBotClient Client { get; set; }
|
||||||
|
[Inject] Localization Locale { get; set; }
|
||||||
User Me { get; }
|
User Me { get; }
|
||||||
|
|
||||||
public AwardCommand(Db db, TelegramBotClient client, User me)
|
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"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public AwardCommand(User me)
|
||||||
{
|
{
|
||||||
Db = db;
|
|
||||||
Client = client;
|
|
||||||
Me = me;
|
Me = me;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
JetKarmaBot/Commands/ChangeLocaleCommand.cs
Normal file
95
JetKarmaBot/Commands/ChangeLocaleCommand.cs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Args;
|
||||||
|
using Perfusion;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using NLog;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Commands
|
||||||
|
{
|
||||||
|
class LocaleCommand : IChatCommand
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<string> Names => new[] { "changelocale", "locale" };
|
||||||
|
[Inject]
|
||||||
|
private Logger log;
|
||||||
|
|
||||||
|
public bool Execute(CommandString cmd, MessageEventArgs args)
|
||||||
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
var currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
|
if (cmd.Parameters.Length < 1)
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
currentLocale["jetkarmabot.changelocale.getlocale"],
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (cmd.Parameters[0] == "list")
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
currentLocale["jetkarmabot.changelocale.listalltext"] + "\n"
|
||||||
|
+ string.Join("\n", Locale.Select(a => a.Key)),
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (cmd.Parameters[0] == "all")
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
currentLocale["jetkarmabot.changelocale.errorall"],
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
string localeId;
|
||||||
|
if (Locale.ContainsLocale(cmd.Parameters[0]))
|
||||||
|
localeId = cmd.Parameters[0];
|
||||||
|
else
|
||||||
|
try
|
||||||
|
{
|
||||||
|
localeId = Locale.FindByCommonName(cmd.Parameters[0]).Name;
|
||||||
|
}
|
||||||
|
catch (LocalizationException e)
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
currentLocale["jetkarmabot.changelocale.toomany"] + "\n" + string.Join("\n", (e.Data["LocaleNames"] as Locale[]).Select(x => x.Name)),
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
db.Chats.Find(args.Message.Chat.Id).Locale = localeId;
|
||||||
|
log.Debug($"Changed language of chat {args.Message.Chat.Id} to {localeId}");
|
||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
|
currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
|
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
(currentLocale.HasNote ? currentLocale["jetkarmabot.changelocale.beforenote"] + currentLocale.Note + "\n" : "")
|
||||||
|
+ currentLocale["jetkarmabot.changelocale.justchanged"],
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inject] KarmaContextFactory Db { get; set; }
|
||||||
|
[Inject] TelegramBotClient Client { get; set; }
|
||||||
|
[Inject] Localization Locale { get; set; }
|
||||||
|
|
||||||
|
public string Description => "Switches current chat locale to [locale]";
|
||||||
|
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,31 +1,46 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace JetKarmaBot.Commands
|
namespace JetKarmaBot.Commands
|
||||||
{
|
{
|
||||||
public class CommandString
|
public class CommandString
|
||||||
{
|
{
|
||||||
public CommandString(string name, params string[] parameters)
|
public CommandString(string command, params string[] parameters)
|
||||||
{
|
{
|
||||||
Name = name;
|
Command = command;
|
||||||
Parameters = parameters;
|
Parameters = parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; }
|
public string Command { get; }
|
||||||
|
public string UserName { get; set; } = null;
|
||||||
public string[] Parameters { get; }
|
public string[] Parameters { get; }
|
||||||
|
|
||||||
|
public static readonly char[] WS_CHARS = new[] { ' ', '\r', '\n', '\n' };
|
||||||
|
|
||||||
public static bool TryParse(string s, out CommandString result)
|
public static bool TryParse(string s, out CommandString result)
|
||||||
{
|
{
|
||||||
result = null;
|
result = null;
|
||||||
if (string.IsNullOrWhiteSpace(s) || s[0] != '/')
|
if (string.IsNullOrWhiteSpace(s) || s[0] != '/')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int space = s.IndexOf(' ');
|
string[] words = s.Split(WS_CHARS, StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (space < 0)
|
|
||||||
result = new CommandString(s.Substring(1));
|
if (!words.Any())
|
||||||
else
|
return false;
|
||||||
result = new CommandString(s.Substring(1, space - 1), s.Substring(space).Split(' ', StringSplitOptions.RemoveEmptyEntries));
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
result = new CommandString(cmd, parameters) { UserName = username};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
JetKarmaBot/Commands/CurrenciesCommand.cs
Normal file
40
JetKarmaBot/Commands/CurrenciesCommand.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Telegram.Bot.Args;
|
||||||
|
using Perfusion;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Commands
|
||||||
|
{
|
||||||
|
public class CurrenciesCommand : IChatCommand
|
||||||
|
{
|
||||||
|
[Inject] KarmaContextFactory Db;
|
||||||
|
[Inject] TelegramBotClient Client { get; set; }
|
||||||
|
[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 bool Execute(CommandString cmd, MessageEventArgs args)
|
||||||
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
var currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
|
string resp = currentLocale["jetkarmabot.currencies.listtext"] + "\n" + string.Join("\n",
|
||||||
|
db.AwardTypes.ToList().Select(x => $"{x.Symbol} ({x.CommandName}) <i>{currentLocale["jetkarmabot.awardtypes.nominative." + x.CommandName]}</i>"));
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
resp,
|
||||||
|
replyToMessageId: args.Message.MessageId,
|
||||||
|
parseMode: ParseMode.Html);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
JetKarmaBot/Commands/HelpCommand.cs
Normal file
61
JetKarmaBot/Commands/HelpCommand.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Telegram.Bot.Args;
|
||||||
|
using Perfusion;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Commands
|
||||||
|
{
|
||||||
|
public class HelpCommand : IChatCommand
|
||||||
|
{
|
||||||
|
[Inject] KarmaContextFactory Db;
|
||||||
|
[Inject] TelegramBotClient Client { get; set; }
|
||||||
|
[Inject] Localization Locale { get; set; }
|
||||||
|
ChatCommandRouter Router;
|
||||||
|
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 bool Execute(CommandString cmd, MessageEventArgs args)
|
||||||
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
var currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
|
if (cmd.Parameters.Length < 1)
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
Router.GetHelpText(currentLocale),
|
||||||
|
replyToMessageId: args.Message.MessageId,
|
||||||
|
parseMode: ParseMode.Html);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
Router.GetHelpTextFor(cmd.Parameters[0], currentLocale),
|
||||||
|
replyToMessageId: args.Message.MessageId,
|
||||||
|
parseMode: ParseMode.Html);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public HelpCommand(ChatCommandRouter router)
|
||||||
|
{
|
||||||
|
Router = router;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,26 @@ namespace JetKarmaBot.Commands
|
|||||||
public interface IChatCommand
|
public interface IChatCommand
|
||||||
{
|
{
|
||||||
IReadOnlyCollection<string> Names { get; }
|
IReadOnlyCollection<string> Names { get; }
|
||||||
bool Execute(object sender, MessageEventArgs messageEventArgs);
|
string Description { get; }
|
||||||
|
string DescriptionID { get; }
|
||||||
|
IReadOnlyCollection<ChatCommandArgument> Arguments { get; }
|
||||||
|
|
||||||
|
bool Execute(CommandString cmd, MessageEventArgs messageEventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,24 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Telegram.Bot.Args;
|
|
||||||
|
|
||||||
namespace JetKarmaBot.Commands
|
|
||||||
{
|
|
||||||
public class StartCommand : IChatCommand
|
|
||||||
{
|
|
||||||
Db Db;
|
|
||||||
|
|
||||||
public StartCommand(Db db)
|
|
||||||
{
|
|
||||||
Db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IReadOnlyCollection<string> Names => new[] { "start" };
|
|
||||||
|
|
||||||
public bool Execute(object sender, MessageEventArgs args)
|
|
||||||
{
|
|
||||||
Db.AddChat(new Db.Chat { ChatId = args.Message.Chat.Id });
|
|
||||||
Db.AddUser(new Db.User { UserId = args.Message.From.Id });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
83
JetKarmaBot/Commands/StatusCommand.cs
Normal file
83
JetKarmaBot/Commands/StatusCommand.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Perfusion;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Args;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
using JetKarmaBot.Models;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Commands
|
||||||
|
{
|
||||||
|
class StatusCommand : IChatCommand
|
||||||
|
{
|
||||||
|
public IReadOnlyCollection<string> Names => new[] { "status" };
|
||||||
|
|
||||||
|
public bool Execute(CommandString cmd, MessageEventArgs args)
|
||||||
|
{
|
||||||
|
using (var db = Db.GetContext())
|
||||||
|
{
|
||||||
|
var currentLocale = Locale[db.Chats.Find(args.Message.Chat.Id).Locale];
|
||||||
|
var asker = args.Message.From;
|
||||||
|
var awardTypeName = cmd.Parameters.FirstOrDefault();
|
||||||
|
|
||||||
|
string response;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(awardTypeName))
|
||||||
|
{
|
||||||
|
// var awards = db.Awards.Where(x => x.ToId == asker.Id)
|
||||||
|
// .GroupBy(x => x.AwardTypeId)
|
||||||
|
// .Select(x => new { AwardTypeId = x.Key, Amount = x.Sum(y => y.Amount) });
|
||||||
|
if (!db.Awards.Any(x => x.ToId == asker.Id))
|
||||||
|
response = currentLocale["jetkarmabot.status.havenothing"];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var awardsQuery = from award in db.Awards
|
||||||
|
where award.ToId == asker.Id
|
||||||
|
group award by award.AwardTypeId into g
|
||||||
|
select new { AwardTypeId = g.Key, Amount = g.Sum(x => x.Amount) };
|
||||||
|
var awardsByType = awardsQuery.ToList();
|
||||||
|
response = currentLocale["jetkarmabot.status.listalltext"] + "\n"
|
||||||
|
+ string.Join("\n", awardsByType.Select(a => $" - {db.AwardTypes.Find(a.AwardTypeId).Symbol} {a.Amount}"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var awardTypeIdQuery = from awt in db.AwardTypes
|
||||||
|
where awt.CommandName == awardTypeName
|
||||||
|
select awt.AwardTypeId;
|
||||||
|
var awardTypeId = awardTypeIdQuery.First();
|
||||||
|
var awardType = db.AwardTypes.Find(awardTypeId);
|
||||||
|
|
||||||
|
response = string.Format(currentLocale["jetkarmabot.status.listspecifictext"], db.Awards.Where(x => x.AwardTypeId == awardTypeId && x.ToId == asker.Id).Sum(x => x.Amount), awardType.Symbol);
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.SendTextMessageAsync(
|
||||||
|
args.Message.Chat.Id,
|
||||||
|
response,
|
||||||
|
replyToMessageId: args.Message.MessageId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inject] KarmaContextFactory Db { get; set; }
|
||||||
|
[Inject] TelegramBotClient Client { get; set; }
|
||||||
|
[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 => new ChatCommandArgument[] {
|
||||||
|
new ChatCommandArgument(){
|
||||||
|
Name="awardtype",
|
||||||
|
Required=false,
|
||||||
|
Type=ChatCommandArgumentType.String,
|
||||||
|
Description="The awardtype to show. If empty shows everything.",
|
||||||
|
DescriptionID= "jetkarmabot.status.awardtypehelp"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using JsonNet.PrivateSettersContractResolvers;
|
using JsonNet.PrivateSettersContractResolvers;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@ -12,10 +11,17 @@ namespace JetKarmaBot
|
|||||||
|
|
||||||
public string ApiKey { get; private set; }
|
public string ApiKey { get; private set; }
|
||||||
public string ConnectionString { get; private set; }
|
public string ConnectionString { get; private set; }
|
||||||
public string ProxyUrl { get; private set; }
|
|
||||||
public int ProxyPort { get; private set; }
|
public class ProxySettings
|
||||||
public string ProxyLogin { get; private set; }
|
{
|
||||||
public string ProxyPassword { get; private set; }
|
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 bool SqlDebug { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class ConfigBase
|
public abstract class ConfigBase
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
using JetKarmaBot.Commands;
|
using JetKarmaBot.Commands;
|
||||||
|
using JetKarmaBot.Models;
|
||||||
|
using JetKarmaBot.Services;
|
||||||
|
using Perfusion;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
@ -16,57 +16,79 @@ namespace JetKarmaBot
|
|||||||
{
|
{
|
||||||
public class JetKarmaBot : IDisposable
|
public class JetKarmaBot : IDisposable
|
||||||
{
|
{
|
||||||
public void Broadcast(string message)
|
[Inject] Config Config { get; set; }
|
||||||
{
|
[Inject] IContainer Container { get; set; }
|
||||||
foreach (var u in db.Chats)
|
[Inject] KarmaContextFactory Db { get; set; }
|
||||||
client.SendTextMessageAsync(u.Value.ChatId, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public JetKarmaBot(Config cfg, Db db)
|
TelegramBotClient Client { get; set; }
|
||||||
|
ChatCommandRouter Commands;
|
||||||
|
Telegram.Bot.Types.User Me { get; set; }
|
||||||
|
|
||||||
|
public async Task Init()
|
||||||
{
|
{
|
||||||
this.db = db;
|
using (KarmaContext db = Db.GetContext())
|
||||||
var httpProxy = new WebProxy($"{cfg.ProxyUrl}:{cfg.ProxyPort}")
|
await db.Database.EnsureCreatedAsync();
|
||||||
|
var httpProxy = new WebProxy($"{Config.Proxy.Url}:{Config.Proxy.Port}")
|
||||||
{
|
{
|
||||||
Credentials = new NetworkCredential(cfg.ProxyLogin, cfg.ProxyPassword)
|
Credentials = new NetworkCredential(Config.Proxy.Login, Config.Proxy.Password)
|
||||||
};
|
};
|
||||||
var botClient = new TelegramBotClient(cfg.ApiKey, httpProxy);
|
|
||||||
var cred = new NetworkCredential(cfg.ProxyLogin, cfg.ProxyPassword);
|
Client = new TelegramBotClient(Config.ApiKey, httpProxy);
|
||||||
client = new TelegramBotClient(cfg.ApiKey, httpProxy);
|
Container.AddInstance(Client);
|
||||||
me = client.GetMeAsync().Result;
|
Me = await Client.GetMeAsync();
|
||||||
InitCommands();
|
|
||||||
client.OnMessage += BotOnMessageReceived;
|
InitCommands(Container);
|
||||||
client.StartReceiving();
|
|
||||||
|
Client.OnMessage += BotOnMessageReceived;
|
||||||
|
Client.StartReceiving();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
public async Task Stop()
|
||||||
public void Dispose()
|
|
||||||
{
|
{
|
||||||
client.StopReceiving();
|
Dispose();
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region service
|
#region service
|
||||||
Db db { get; }
|
|
||||||
TelegramBotClient client { get; }
|
|
||||||
User me { get; }
|
|
||||||
|
|
||||||
ChatCommandRouter commands;
|
|
||||||
void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
|
void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)
|
||||||
{
|
{
|
||||||
var message = messageEventArgs.Message;
|
var message = messageEventArgs.Message;
|
||||||
if (message == null || message.Type != MessageType.Text)
|
if (message == null || message.Type != MessageType.Text)
|
||||||
return;
|
return;
|
||||||
|
using (KarmaContext db = Db.GetContext())
|
||||||
|
{
|
||||||
|
if (!db.Users.Any(x => x.UserId == messageEventArgs.Message.From.Id))
|
||||||
|
db.Users.Add(new Models.User { UserId = messageEventArgs.Message.From.Id });
|
||||||
|
if (messageEventArgs.Message.ReplyToMessage != null)
|
||||||
|
if (!db.Users.Any(x => x.UserId == messageEventArgs.Message.ReplyToMessage.From.Id))
|
||||||
|
db.Users.Add(new Models.User { UserId = messageEventArgs.Message.ReplyToMessage.From.Id });
|
||||||
|
if (!db.Chats.Any(x => x.ChatId == messageEventArgs.Message.Chat.Id))
|
||||||
|
db.Chats.Add(new Models.Chat { ChatId = messageEventArgs.Message.Chat.Id });
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
string s = message.Text;
|
string s = message.Text;
|
||||||
long id = message.Chat.Id;
|
long id = message.Chat.Id;
|
||||||
long from = message.From.Id;
|
long from = message.From.Id;
|
||||||
Task.Run(() => commands.Execute(sender, messageEventArgs));
|
Task.Run(() => Commands.Execute(sender, messageEventArgs));
|
||||||
}
|
}
|
||||||
void InitCommands()
|
|
||||||
|
void InitCommands(IContainer c)
|
||||||
{
|
{
|
||||||
commands = new ChatCommandRouter();
|
Commands = c.ResolveObject(new ChatCommandRouter(Me));
|
||||||
commands.Add(new StartCommand(db));
|
Commands.Add(c.ResolveObject(new HelpCommand(Commands)));
|
||||||
commands.Add(new AwardCommand(db, client, me));
|
Commands.Add(c.ResolveObject(new AwardCommand(Me)));
|
||||||
|
Commands.Add(c.ResolveObject(new StatusCommand()));
|
||||||
|
Commands.Add(c.ResolveObject(new LocaleCommand()));
|
||||||
|
Commands.Add(c.ResolveObject(new CurrenciesCommand()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Client.StopReceiving();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -1,21 +1,35 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFrameworks>netcoreapp2.2;</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="1.50.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.2.4">
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.13" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
<PackageReference Include="Telegram.Bot" Version="14.10.0" />
|
</PackageReference>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||||
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Telegram.Bot" Version="14.12.0" />
|
||||||
|
<PackageReference Include="NLog" Version="4.6.5" />
|
||||||
|
<PackageReference Include="NLog.Config" Version="4.6.5" />
|
||||||
|
<ProjectReference Include="..\perfusion\Perfusion\Perfusion.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="karma.cfg.json">
|
<None Update="karma.cfg.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
|
<None Update="lang\be-BY.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lang\en-US.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="lang\ru-RU.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
<None Update="NLog.config">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
25
JetKarmaBot/Models/Award.cs
Normal file
25
JetKarmaBot/Models/Award.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Models
|
||||||
|
{
|
||||||
|
public partial class Award
|
||||||
|
{
|
||||||
|
public int AwardId { get; set; }
|
||||||
|
public long ChatId { get; set; }
|
||||||
|
public int FromId { get; set; }
|
||||||
|
public int 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
21
JetKarmaBot/Models/AwardType.cs
Normal file
21
JetKarmaBot/Models/AwardType.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Models
|
||||||
|
{
|
||||||
|
public partial class 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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
JetKarmaBot/Models/Chat.cs
Normal file
19
JetKarmaBot/Models/Chat.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Models
|
||||||
|
{
|
||||||
|
public partial class Chat
|
||||||
|
{
|
||||||
|
public Chat()
|
||||||
|
{
|
||||||
|
Awards = new HashSet<Award>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long ChatId { get; set; }
|
||||||
|
public string Locale { get; set; }
|
||||||
|
public bool IsAdministrator { get; set; }
|
||||||
|
|
||||||
|
public virtual ICollection<Award> Awards { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
178
JetKarmaBot/Models/KarmaContext.cs
Normal file
178
JetKarmaBot/Models/KarmaContext.cs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Perfusion;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Models
|
||||||
|
{
|
||||||
|
[Transient]
|
||||||
|
public partial class KarmaContext : DbContext
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.AwardId)
|
||||||
|
.HasName("awardid_UNIQUE")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.AwardTypeId)
|
||||||
|
.HasName("fk_awardtype_idx");
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.ChatId)
|
||||||
|
.HasName("fk_chat_idx");
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.FromId)
|
||||||
|
.HasName("fk_from_idx");
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.ToId)
|
||||||
|
.HasName("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)
|
||||||
|
.HasName("awardtypeid_UNIQUE")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
entity.HasIndex(e => e.CommandName)
|
||||||
|
.HasName("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)");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
JetKarmaBot/Models/User.cs
Normal file
22
JetKarmaBot/Models/User.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Models
|
||||||
|
{
|
||||||
|
public partial class User
|
||||||
|
{
|
||||||
|
public User()
|
||||||
|
{
|
||||||
|
AwardsFrom = new HashSet<Award>();
|
||||||
|
AwardsTo = new HashSet<Award>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
[InverseProperty("From")]
|
||||||
|
public virtual ICollection<Award> AwardsFrom { get; set; }
|
||||||
|
[InverseProperty("To")]
|
||||||
|
public virtual ICollection<Award> AwardsTo { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
JetKarmaBot/NLog.config
Executable file
14
JetKarmaBot/NLog.config
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
|
<targets>
|
||||||
|
<target name="logfile" xsi:type="File" fileName="karmabot.log" />
|
||||||
|
<target name="logconsole" xsi:type="Console" />
|
||||||
|
</targets>
|
||||||
|
|
||||||
|
<rules>
|
||||||
|
<logger name="*" minlevel="Info" writeTo="logconsole" />
|
||||||
|
<logger name="*" minlevel="Trace" writeTo="logfile" />
|
||||||
|
</rules>
|
||||||
|
</nlog>
|
||||||
@ -1,28 +1,70 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using JetKarmaBot.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NLog;
|
||||||
|
using Perfusion;
|
||||||
|
|
||||||
namespace JetKarmaBot
|
namespace JetKarmaBot
|
||||||
{
|
{
|
||||||
public class App
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
private static Logger log = LogManager.GetCurrentClassLogger();
|
||||||
|
public enum ExitCode : int
|
||||||
{
|
{
|
||||||
Current = new App(new Config("karma.cfg.json"));
|
Ok = 0,
|
||||||
|
ErrorNotStarted = 0x80,
|
||||||
|
ErrorRunning = 0x81,
|
||||||
|
ErrorException = 0x82,
|
||||||
|
ErrorInvalidCommandLine = 0x100
|
||||||
|
};
|
||||||
|
|
||||||
Console.ReadKey();
|
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 dbOptions = new DbContextOptionsBuilder<KarmaContext>()
|
||||||
|
.UseMySql(cfg.ConnectionString + (cfg.ConnectionString.EndsWith(";") ? "" : ";") + "TreatTinyAsBoolean=false");
|
||||||
|
c.AddInfo<Logger>(new LogInfo());
|
||||||
|
if (cfg.SqlDebug)
|
||||||
|
{
|
||||||
|
dbOptions = dbOptions.UseLoggerFactory(c.GetInstance<NLoggerFactory>());
|
||||||
}
|
}
|
||||||
|
c.AddTransient(() => new KarmaContext(dbOptions.Options));
|
||||||
|
c.Add<JetKarmaBot>();
|
||||||
|
|
||||||
public static App Current { get; private set; }
|
var bot = c.GetInstance<JetKarmaBot>();
|
||||||
|
|
||||||
public App(Config cfg)
|
try
|
||||||
{
|
{
|
||||||
Config = cfg;
|
bot.Init().Wait();
|
||||||
Db = new Db(Config);
|
log.Info("JetKarmaBot started. Press Ctrl-C to exit...");
|
||||||
Watcher = new JetKarmaBot(Config, Db);
|
Environment.ExitCode = (int)ExitCode.ErrorRunning;
|
||||||
Console.WriteLine("JetKarmaBot started!");
|
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.Error(ex);
|
||||||
|
Environment.ExitCode = (int)ExitCode.ErrorException;
|
||||||
|
}
|
||||||
|
ManualResetEvent quitEvent = new ManualResetEvent(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.CancelKeyPress += (sender, eArgs) => // ctrl-c
|
||||||
|
{
|
||||||
|
eArgs.Cancel = true;
|
||||||
|
quitEvent.Set();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
Config Config { get; }
|
quitEvent.WaitOne(Timeout.Infinite);
|
||||||
Db Db { get; }
|
log.Info("Waiting for exit...");
|
||||||
JetKarmaBot Watcher { get; }
|
bot?.Stop()?.Wait();
|
||||||
|
|
||||||
|
return (int)ExitCode.Ok;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,133 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using Dapper;
|
|
||||||
using MySql.Data.MySqlClient;
|
|
||||||
|
|
||||||
namespace JetKarmaBot
|
|
||||||
{
|
|
||||||
public class Db
|
|
||||||
{
|
|
||||||
Dictionary<long, Chat> m_Chats;
|
|
||||||
public IReadOnlyDictionary<long, Chat> Chats => m_Chats;
|
|
||||||
public void AddChat(Chat chat)
|
|
||||||
{
|
|
||||||
lock (m_SyncRoot)
|
|
||||||
if (!m_Chats.ContainsKey(chat.ChatId))
|
|
||||||
{
|
|
||||||
Conn.Execute(@"INSERT INTO chat
|
|
||||||
(chatid)
|
|
||||||
VALUES
|
|
||||||
(@ChatId)",
|
|
||||||
chat);
|
|
||||||
m_Chats.Add(chat.ChatId, chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<long, User> m_Users;
|
|
||||||
public IReadOnlyDictionary<long, User> Users => m_Users;
|
|
||||||
public void AddUser(User user)
|
|
||||||
{
|
|
||||||
lock (m_SyncRoot)
|
|
||||||
if (!m_Users.ContainsKey(user.UserId))
|
|
||||||
{
|
|
||||||
Conn.Execute(@"INSERT INTO user
|
|
||||||
(userid)
|
|
||||||
VALUES
|
|
||||||
(@UserId)",
|
|
||||||
user);
|
|
||||||
m_Users.Add(user.UserId, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Dictionary<byte, AwardType> m_AwardTypes;
|
|
||||||
public byte DefaultAwardTypeId { get; } = 1;
|
|
||||||
public IReadOnlyDictionary<byte, AwardType> AwardTypes => m_AwardTypes;
|
|
||||||
public IReadOnlyDictionary<string, AwardType> AwardTypesByCommandName { get; private set; }
|
|
||||||
|
|
||||||
public int CountAwards(long userId, byte awardTypeId)
|
|
||||||
{
|
|
||||||
return Conn.QuerySingle<int?>
|
|
||||||
(
|
|
||||||
"SELECT SUM(amount) FROM award WHERE toid = @userId AND awardtypeid = @awardTypeId",
|
|
||||||
new { userId, awardTypeId }
|
|
||||||
) ?? 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte GetAwardTypeId(string name)
|
|
||||||
=> AwardTypesByCommandName.GetOrDefault(name)?.AwardTypeId ?? DefaultAwardTypeId;
|
|
||||||
|
|
||||||
public bool AddAward(byte awardTypeId, long fromId, long toId, long chatId, int amount)
|
|
||||||
{
|
|
||||||
AddChat(new Chat() { ChatId = chatId });
|
|
||||||
AddUser(new User() { UserId = fromId});
|
|
||||||
AddUser(new User() { UserId = toId });
|
|
||||||
|
|
||||||
int affected = Conn.ExecuteScalar<int>(
|
|
||||||
@"INSERT INTO award
|
|
||||||
(chatid, fromid, toid, awardtypeid, amount)
|
|
||||||
VALUES
|
|
||||||
(@chatId, @fromId, @toId, @awardTypeId, @amount)",
|
|
||||||
new { awardTypeId, fromId, toId, chatId, amount });
|
|
||||||
return affected == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region types
|
|
||||||
public class Chat
|
|
||||||
{
|
|
||||||
public long ChatId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class User
|
|
||||||
{
|
|
||||||
public long UserId { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AwardType
|
|
||||||
{
|
|
||||||
public byte AwardTypeId { get; set; }
|
|
||||||
public string CommandName { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string Symbol { get; set; }
|
|
||||||
public string Description { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Award
|
|
||||||
{
|
|
||||||
public int AwardId { get; set; }
|
|
||||||
public byte AwardTypeId { get; set; }
|
|
||||||
public long FromId { get; set; }
|
|
||||||
public long ToId { get; set; }
|
|
||||||
public long ChatId { get; set; }
|
|
||||||
public byte Amount { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region service
|
|
||||||
public Db(Config cfg)
|
|
||||||
{
|
|
||||||
Log("Initializing...");
|
|
||||||
Conn = new MySqlConnection(cfg.ConnectionString);
|
|
||||||
Conn.ExecuteScalar("select 1");
|
|
||||||
Load();
|
|
||||||
Log("Initialized!");
|
|
||||||
}
|
|
||||||
|
|
||||||
object m_SyncRoot = new object();
|
|
||||||
|
|
||||||
IDbConnection Conn { get; }
|
|
||||||
void Load()
|
|
||||||
{
|
|
||||||
Log("Populating cache...");
|
|
||||||
m_Chats = Conn.Query<Chat>("SELECT * FROM chat").ToDictionary(u => u.ChatId);
|
|
||||||
m_Users = Conn.Query<User>("SELECT * FROM user").ToDictionary(s => s.UserId);
|
|
||||||
m_AwardTypes = Conn.Query<AwardType>("SELECT * FROM awardtype").ToDictionary(c => c.AwardTypeId);
|
|
||||||
AwardTypesByCommandName = m_AwardTypes.Values.ToDictionary(kvp => kvp.CommandName);
|
|
||||||
Log("Cache populated!");
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
void Log (string Message) => Console.WriteLine($"[{nameof(Db)}]: {Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
JetKarmaBot/Services/KarmaContextFactory.cs
Normal file
12
JetKarmaBot/Services/KarmaContextFactory.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using JetKarmaBot.Models;
|
||||||
|
using Perfusion;
|
||||||
|
|
||||||
|
namespace JetKarmaBot.Services
|
||||||
|
{
|
||||||
|
public class KarmaContextFactory
|
||||||
|
{
|
||||||
|
[Inject] IContainer C { get; set; }
|
||||||
|
|
||||||
|
public KarmaContext GetContext() => C.GetInstance<KarmaContext>();
|
||||||
|
}
|
||||||
|
}
|
||||||
172
JetKarmaBot/Services/Localization.cs
Normal file
172
JetKarmaBot/Services/Localization.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
public class Localization : IReadOnlyDictionary<string, Locale>
|
||||||
|
{
|
||||||
|
private Dictionary<string, Locale> locales = new Dictionary<string, Locale>();
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public Localization(IContainer c)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locales.Any())
|
||||||
|
log.Info("Initialized!");
|
||||||
|
else
|
||||||
|
throw new FileNotFoundException($"No locales found in {langsFolder}!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale this[string locale]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
locale = locale.ToLowerInvariant();
|
||||||
|
return locales[locale];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private Logger log;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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] : "unknown";
|
||||||
|
|
||||||
|
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) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
20
JetKarmaBot/Services/LogInfo.cs
Normal file
20
JetKarmaBot/Services/LogInfo.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NLog;
|
||||||
|
using Perfusion;
|
||||||
|
|
||||||
|
namespace JetKarmaBot
|
||||||
|
{
|
||||||
|
public class LogInfo : ObjectInfo
|
||||||
|
{
|
||||||
|
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)) + ">" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
JetKarmaBot/Services/LoggerVirtualizer.cs
Normal file
103
JetKarmaBot/Services/LoggerVirtualizer.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NLog;
|
||||||
|
using Perfusion;
|
||||||
|
|
||||||
|
namespace JetKarmaBot
|
||||||
|
{
|
||||||
|
public class NLoggerFactory : ILoggerFactory
|
||||||
|
{
|
||||||
|
[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
|
||||||
|
{
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
JetKarmaBot/lang/be-BY.json
Normal file
49
JetKarmaBot/lang/be-BY.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"belarusian",
|
||||||
|
"белорусский",
|
||||||
|
"беларуская"
|
||||||
|
],
|
||||||
|
"note": "This is a joke. And made with google translate.",
|
||||||
|
"strings": {
|
||||||
|
"jetkarmabot.award.errawardnoreply": "Калі ласка выкарыстоўвайце гэтую каманду ў адказе іншаму карыстальніку.",
|
||||||
|
"jetkarmabot.award.errawardself": "Хопіць з сабой гуляцца.",
|
||||||
|
"jetkarmabot.award.errawardbot": "Я бот, і мяне вашы нікчэмныя пузамеркі не вабяць.",
|
||||||
|
"jetkarmabot.award.errrevokebot": "ಠ_ಠ",
|
||||||
|
"jetkarmabot.award.awardmessage": "Ўручыў \"{0}\" {1}!",
|
||||||
|
"jetkarmabot.award.revokemessage": "Адабраў \"{0}\" у {1}!",
|
||||||
|
"jetkarmabot.award.statustext": "У {0} цяпер {1}{2}.",
|
||||||
|
"jetkarmabot.award.ratelimit": "Павольны, чувак!",
|
||||||
|
"jetkarmabot.award.help": "Уручае ачко карыстачу (або адымае)",
|
||||||
|
"jetkarmabot.award.awardtypehelp": "Тып ачкі",
|
||||||
|
"jetkarmabot.status.listalltext": "У вас :",
|
||||||
|
"jetkarmabot.status.listspecifictext": "У вас зараз {0}{1}.",
|
||||||
|
"jetkarmabot.status.havenothing": "У вас пакуль нічога няма.",
|
||||||
|
"jetkarmabot.status.help": "Паказвае справаздачу па ўзнагародах.",
|
||||||
|
"jetkarmabot.status.awardtypehelp": "Тып ўзнагароды, па якой падасца справаздачу. Калі пуста, то падасца зводны справаздачу.",
|
||||||
|
"jetkarmabot.changelocale.justchanged": "Так дакладна.",
|
||||||
|
"jetkarmabot.changelocale.getlocale": "Я зараз кажу па-беларускай.",
|
||||||
|
"jetkarmabot.changelocale.listalltext": "Я ведаю:",
|
||||||
|
"jetkarmabot.changelocale.errorall": "Мне б хацелася гаварыць на ўсіх мовах у той жа самы час, але з-за абмежаванняў сусвету, мне гэта не дазваляецца.",
|
||||||
|
"jetkarmabot.changelocale.beforenote": "Увага: ",
|
||||||
|
"jetkarmabot.changelocale.toomany": "Не ведаю, на якім дыялекце хочаш, каб я казаў.\nЯ ведаю гэтыя:",
|
||||||
|
"jetkarmabot.changelocale.help": "Змяняе мову, на якім бот кажа ў гэтым чаце",
|
||||||
|
"jetkarmabot.changelocale.localehelp": "Новая лакаль, альбо каманда list - спіс перакладаў. Калі пуста - паказваецца бягучая лакаль.",
|
||||||
|
"jetkarmabot.help.help": "Паказвае інструкцыю да каманды, ці спіс каманд.",
|
||||||
|
"jetkarmabot.help.commandhelp": "Каманда, да якой трэба паказаць інструкцыю. Калі пуста, то паказваецца спіс каманд.",
|
||||||
|
"jetkarmabot.currencies.help": "Паказвае ўсе тыпы узнагарод",
|
||||||
|
"jetkarmabot.currencies.listtext": "Тыпы узнагарод:",
|
||||||
|
"jetkarmabot.awardtypes.nominative.star": "зорачка",
|
||||||
|
"jetkarmabot.awardtypes.nominative.pie": "з паліцы піражок",
|
||||||
|
"jetkarmabot.awardtypes.nominative.dream": "мара",
|
||||||
|
"jetkarmabot.awardtypes.nominative.banana": "стыкер-бананчык",
|
||||||
|
"jetkarmabot.awardtypes.nominative.determination": "DETERMINATION",
|
||||||
|
"jetkarmabot.awardtypes.nominative.raisin": "разынка",
|
||||||
|
"jetkarmabot.awardtypes.accusative.star": "зорачку",
|
||||||
|
"jetkarmabot.awardtypes.accusative.pie": "з паліцы піражкі",
|
||||||
|
"jetkarmabot.awardtypes.accusative.dream": "мары",
|
||||||
|
"jetkarmabot.awardtypes.accusative.banana": "стыкера-бананчыка",
|
||||||
|
"jetkarmabot.awardtypes.accusative.determination": "DETERMINATION",
|
||||||
|
"jetkarmabot.awardtypes.accusative.raisin": "разыначкі"
|
||||||
|
}
|
||||||
|
}
|
||||||
48
JetKarmaBot/lang/en-US.json
Normal file
48
JetKarmaBot/lang/en-US.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"english",
|
||||||
|
"английский",
|
||||||
|
"англійская"
|
||||||
|
],
|
||||||
|
"strings": {
|
||||||
|
"jetkarmabot.award.errawardnoreply": "Please use this command in reply to another user.",
|
||||||
|
"jetkarmabot.award.errawardself": "Please stop playing with yourself.",
|
||||||
|
"jetkarmabot.award.errawardbot": "I am a bot, and have no use for your foolish fake internet points.",
|
||||||
|
"jetkarmabot.award.errrevokebot": "ಠ_ಠ",
|
||||||
|
"jetkarmabot.award.awardmessage": "Awarded a {0} to {1}!",
|
||||||
|
"jetkarmabot.award.revokemessage": "Revoked a {0} from {1}!",
|
||||||
|
"jetkarmabot.award.statustext": "{0} is at {1}{2} now.",
|
||||||
|
"jetkarmabot.award.ratelimit": "Slow down there, turbo!",
|
||||||
|
"jetkarmabot.award.help": "Awards/revokes an award to a user.",
|
||||||
|
"jetkarmabot.award.awardtypehelp": "The award to grant to/strip of the specified user",
|
||||||
|
"jetkarmabot.status.listalltext": "Your badges report:",
|
||||||
|
"jetkarmabot.status.listspecifictext": "You are at {0}{1} now.",
|
||||||
|
"jetkarmabot.status.havenothing": "You don't have anything yet.",
|
||||||
|
"jetkarmabot.status.help": "Shows the amount of awards that you have",
|
||||||
|
"jetkarmabot.status.awardtypehelp": "The awardtype to show. If empty shows everything.",
|
||||||
|
"jetkarmabot.changelocale.justchanged": "Roger that.",
|
||||||
|
"jetkarmabot.changelocale.getlocale": "I'm currently speaking English.",
|
||||||
|
"jetkarmabot.changelocale.listalltext": "I know:",
|
||||||
|
"jetkarmabot.changelocale.errorall": "I would like to speak all languages at once, but because of the rules of the universe, I am not allowed to do that.",
|
||||||
|
"jetkarmabot.changelocale.beforenote": "Warning: ",
|
||||||
|
"jetkarmabot.changelocale.toomany": "I don't know what dialect you want me to speak in.\nI know these:",
|
||||||
|
"jetkarmabot.changelocale.help": "Switches current chat locale to [locale]",
|
||||||
|
"jetkarmabot.changelocale.localehelp": "The locale to switch to. Can be \"list\" to list all possible locales. Also can be empty to get current locale",
|
||||||
|
"jetkarmabot.help.help": "Displays help text for all(one) command(s)",
|
||||||
|
"jetkarmabot.help.commandhelp": "The command to return help text for. If empty shows all commands.",
|
||||||
|
"jetkarmabot.currencies.help": "Shows all award types",
|
||||||
|
"jetkarmabot.currencies.listtext": "Award types:",
|
||||||
|
"jetkarmabot.awardtypes.nominative.star": "star",
|
||||||
|
"jetkarmabot.awardtypes.nominative.pie": "pie from the shelf",
|
||||||
|
"jetkarmabot.awardtypes.nominative.dream": "dream",
|
||||||
|
"jetkarmabot.awardtypes.nominative.banana": "banana sticker",
|
||||||
|
"jetkarmabot.awardtypes.nominative.determination": "DETERMINATION",
|
||||||
|
"jetkarmabot.awardtypes.nominative.raisin": "raisin",
|
||||||
|
"jetkarmabot.awardtypes.accusative.star": "star",
|
||||||
|
"jetkarmabot.awardtypes.accusative.pie": "pie from the shelf",
|
||||||
|
"jetkarmabot.awardtypes.accusative.dream": "dream",
|
||||||
|
"jetkarmabot.awardtypes.accusative.banana": "banana sticker",
|
||||||
|
"jetkarmabot.awardtypes.accusative.determination": "DETERMINATION",
|
||||||
|
"jetkarmabot.awardtypes.accusative.raisin": "raisin"
|
||||||
|
}
|
||||||
|
}
|
||||||
48
JetKarmaBot/lang/ru-RU.json
Normal file
48
JetKarmaBot/lang/ru-RU.json
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"names": [
|
||||||
|
"russian",
|
||||||
|
"русский",
|
||||||
|
"руская"
|
||||||
|
],
|
||||||
|
"strings": {
|
||||||
|
"jetkarmabot.award.errawardnoreply": "Пожалуйста используйте эту команду в ответе другому пользователю.",
|
||||||
|
"jetkarmabot.award.errawardself": "Хватит с собой играться.",
|
||||||
|
"jetkarmabot.award.errawardbot": "Я бот, и меня ваши жалкие пузомерки не интересуют.",
|
||||||
|
"jetkarmabot.award.errrevokebot": "ಠ_ಠ",
|
||||||
|
"jetkarmabot.award.awardmessage": "Вручил \"{0}\" {1}!",
|
||||||
|
"jetkarmabot.award.revokemessage": "Отнял \"{0}\" у {1}!",
|
||||||
|
"jetkarmabot.award.statustext": "У {0} теперь {1}{2}.",
|
||||||
|
"jetkarmabot.award.ratelimit": "Помедленней, чувак!",
|
||||||
|
"jetkarmabot.award.help": "Вручает очко пользователю (или отнимает)",
|
||||||
|
"jetkarmabot.award.awardtypehelp": "Тип очка",
|
||||||
|
"jetkarmabot.status.listalltext": "У вас :",
|
||||||
|
"jetkarmabot.status.listspecifictext": "У вас сейчас {0}{1}.",
|
||||||
|
"jetkarmabot.status.havenothing": "У вас пока ничего нет.",
|
||||||
|
"jetkarmabot.status.help": "Показывает отчет по наградам.",
|
||||||
|
"jetkarmabot.status.awardtypehelp": "Тип награды, по которой покажется отчет. Если пусто, то покажется сводный отчет.",
|
||||||
|
"jetkarmabot.changelocale.justchanged": "Так точно.",
|
||||||
|
"jetkarmabot.changelocale.getlocale": "Я сейчас говорю по-русски.",
|
||||||
|
"jetkarmabot.changelocale.listalltext": "Я знаю:",
|
||||||
|
"jetkarmabot.changelocale.errorall": "Мне бы хотелось говорить на всех языках в то же самое время, но из-за ограничений вселенной, мне это не позволяется.",
|
||||||
|
"jetkarmabot.changelocale.beforenote": "Внимание: ",
|
||||||
|
"jetkarmabot.changelocale.toomany": "Не знаю, на каком диалекте хочешь, чтобы я говорил.\nЯ знаю эти:",
|
||||||
|
"jetkarmabot.changelocale.help": "Меняет язык, на котором бот говорит в этом чате",
|
||||||
|
"jetkarmabot.changelocale.localehelp": "Новая локаль, либо команда list - список переводов. Если пусто - показывается текущая локаль.",
|
||||||
|
"jetkarmabot.help.help": "Показывает инструкцию к команде, или список команд.",
|
||||||
|
"jetkarmabot.help.commandhelp": "Команда, к которой нужно показать инструкцию. Если пусто, то показывается список команд.",
|
||||||
|
"jetkarmabot.currencies.help": "Показывает все типы наград",
|
||||||
|
"jetkarmabot.currencies.listtext": "Типы наград:",
|
||||||
|
"jetkarmabot.awardtypes.nominative.star": "звездочка",
|
||||||
|
"jetkarmabot.awardtypes.nominative.pie": "с полки пирожок",
|
||||||
|
"jetkarmabot.awardtypes.nominative.dream": "мечта",
|
||||||
|
"jetkarmabot.awardtypes.nominative.banana": "стикер-бананчик",
|
||||||
|
"jetkarmabot.awardtypes.nominative.determination": "РЕШИТЕЛЬНОСТЬ",
|
||||||
|
"jetkarmabot.awardtypes.nominative.raisin": "изюм",
|
||||||
|
"jetkarmabot.awardtypes.accusative.star": "звездочку",
|
||||||
|
"jetkarmabot.awardtypes.accusative.pie": "с полки пирожок",
|
||||||
|
"jetkarmabot.awardtypes.accusative.dream": "мечту",
|
||||||
|
"jetkarmabot.awardtypes.accusative.banana": "стикер-бананчик",
|
||||||
|
"jetkarmabot.awardtypes.accusative.determination": "РЕШИТЕЛЬНОСТЬ",
|
||||||
|
"jetkarmabot.awardtypes.accusative.raisin": "изюм"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
perfusion
Submodule
1
perfusion
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 40e8f8871f42f1f32c29480505cf5ac6ba61c393
|
||||||
Loading…
Reference in New Issue
Block a user