mirror of
https://github.com/Bubuni-Team/telegram-bot.git
synced 2026-07-31 00:29:24 +03:00
Merge pull request #6 from CrazyHackGUT/refactor/reputation
WIP: Move reputation to separate module.
This commit is contained in:
@@ -9,9 +9,9 @@ using Telegram.Bot;
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement.Handler
|
namespace Kruzya.TelegramBot.Core
|
||||||
{
|
{
|
||||||
public abstract class ManagementHandler : BotEventHandler
|
public abstract class CommonHandler : BotEventHandler
|
||||||
{
|
{
|
||||||
protected readonly CoreContext Db;
|
protected readonly CoreContext Db;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ namespace West.TelegramBot.ChatManagement.Handler
|
|||||||
|
|
||||||
protected User? RepliedUser => Message?.ReplyToMessage?.From;
|
protected User? RepliedUser => Message?.ReplyToMessage?.From;
|
||||||
|
|
||||||
protected ManagementHandler(CoreContext db)
|
protected CommonHandler(CoreContext db)
|
||||||
{
|
{
|
||||||
Db = db;
|
Db = db;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Kruzya.TelegramBot.Core.Data;
|
||||||
|
|
||||||
|
public interface IReputationEntity
|
||||||
|
{
|
||||||
|
public long UserId { get; set; }
|
||||||
|
public long ChatId { get; set; }
|
||||||
|
public double Value { get; set; }
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ public static class DbContextExtension
|
|||||||
dbContext.Update(entity);
|
dbContext.Update(entity);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
}
|
}
|
||||||
catch (DbUpdateConcurrencyException e)
|
catch (DbUpdateConcurrencyException)
|
||||||
{
|
{
|
||||||
dbContext.Add(entity);
|
dbContext.Add(entity);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Service
|
||||||
|
{
|
||||||
|
public interface IReputation
|
||||||
|
{
|
||||||
|
public Task<double> GetUserReputationAsync(Chat chat, User user);
|
||||||
|
public Task<double> SetUserReputationAsync(Chat chat, User user, double value);
|
||||||
|
public Task<double> GetReputationDiffAsync(Chat chat, User sender, User receiver);
|
||||||
|
public Task IncrementReputationAsync(Chat chat, User user, double diff);
|
||||||
|
public Task<IEnumerable<IReputationEntity>> GetChatRatingAsync(Chat chat, int limit = 10, int offset = 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BotFramework.Abstractions;
|
||||||
|
using Kruzya.TelegramBot.Core.Cache;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
namespace Kruzya.TelegramBot.Core.Service
|
namespace Kruzya.TelegramBot.Core.Service
|
||||||
@@ -11,10 +16,17 @@ namespace Kruzya.TelegramBot.Core.Service
|
|||||||
public class UserService
|
public class UserService
|
||||||
{
|
{
|
||||||
private readonly List<long> _superUsers;
|
private readonly List<long> _superUsers;
|
||||||
|
|
||||||
private readonly Dictionary<long, string> _statusMap;
|
private readonly Dictionary<long, string> _statusMap;
|
||||||
|
private readonly ICacheStorage<long, User> _userCache;
|
||||||
|
private readonly ITelegramBotClient _bot;
|
||||||
|
private readonly ILogger<UserService> _logger;
|
||||||
|
|
||||||
public UserService(IConfiguration configuration)
|
|
||||||
|
public UserService(
|
||||||
|
IConfiguration configuration,
|
||||||
|
IBotInstance bot,
|
||||||
|
ICacheStorage<long, User> userCache,
|
||||||
|
ILogger<UserService> logger)
|
||||||
{
|
{
|
||||||
_superUsers = configuration.GetSection("SuperUsers")
|
_superUsers = configuration.GetSection("SuperUsers")
|
||||||
.GetChildren()
|
.GetChildren()
|
||||||
@@ -24,6 +36,41 @@ namespace Kruzya.TelegramBot.Core.Service
|
|||||||
_statusMap = configuration.GetSection("CustomMemberStatus")
|
_statusMap = configuration.GetSection("CustomMemberStatus")
|
||||||
.GetChildren()
|
.GetChildren()
|
||||||
.ToDictionary(x => Convert.ToInt64(x.Key), x => x.Value);
|
.ToDictionary(x => Convert.ToInt64(x.Key), x => x.Value);
|
||||||
|
|
||||||
|
_userCache = userCache;
|
||||||
|
_bot = bot.BotClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> GetUser(long userId, long chatId, bool bypassCache = false)
|
||||||
|
{
|
||||||
|
if (bypassCache || !_userCache.TryGetValue(userId, out var user))
|
||||||
|
{
|
||||||
|
if (bypassCache)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Bypassing cache for {UserId}.", userId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogDebug("User {UserId} is not found in cache. Fetching from Telegram API.", userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = await GetUserByChat(chatId, userId);
|
||||||
|
CacheUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheUser(User user)
|
||||||
|
{
|
||||||
|
// TODO: config entry for TTL
|
||||||
|
_userCache.SetValue(user.Id, user, Convert.ToInt32(TimeSpan.FromHours(1).TotalSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<User> GetUserByChat(long chatId, long userId)
|
||||||
|
{
|
||||||
|
return (await _bot.GetChatMemberAsync(chatId, userId)).User;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsUserSuper(User user)
|
public bool IsUserSuper(User user)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ COPY ./modules/ChatManagement/*.csproj ./modules/ChatManagement/
|
|||||||
COPY ./modules/Entertainment/*.csproj ./modules/Entertainment/
|
COPY ./modules/Entertainment/*.csproj ./modules/Entertainment/
|
||||||
COPY ./modules/UserGroupTag/*.csproj ./modules/UserGroupTag/
|
COPY ./modules/UserGroupTag/*.csproj ./modules/UserGroupTag/
|
||||||
COPY ./modules/ChatQuotes/*.csproj ./modules/ChatQuotes/
|
COPY ./modules/ChatQuotes/*.csproj ./modules/ChatQuotes/
|
||||||
|
COPY ./modules/Reputation/*.csproj ./modules/Reputation/
|
||||||
RUN dotnet restore Kruzya.TelegramBot.sln
|
RUN dotnet restore Kruzya.TelegramBot.sln
|
||||||
|
|
||||||
# Copy all project files
|
# Copy all project files
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UserGroupTag", "modules\Use
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entertainment", "modules\Entertainment\Entertainment.csproj", "{1D62101B-C2B1-4E79-8F7A-690E44E3EE81}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Entertainment", "modules\Entertainment\Entertainment.csproj", "{1D62101B-C2B1-4E79-8F7A-690E44E3EE81}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatQuotes", "modules\ChatQuotes\ChatQuotes.csproj", "{04F16325-8F44-4250-9B2E-0F77E8F65CBF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatQuotes", "modules\ChatQuotes\ChatQuotes.csproj", "{04F16325-8F44-4250-9B2E-0F77E8F65CBF}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reputation", "modules\Reputation\Reputation.csproj", "{03F3C7D5-6FEF-4A83-9191-501091BFC1AC}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -71,6 +73,10 @@ Global
|
|||||||
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.Build.0 = Release|Any CPU
|
{04F16325-8F44-4250-9B2E-0F77E8F65CBF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{03F3C7D5-6FEF-4A83-9191-501091BFC1AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{03F3C7D5-6FEF-4A83-9191-501091BFC1AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{03F3C7D5-6FEF-4A83-9191-501091BFC1AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{03F3C7D5-6FEF-4A83-9191-501091BFC1AC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -84,6 +90,7 @@ Global
|
|||||||
{5B89E22A-78CF-4037-BE0D-5F88EF47022B} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
{5B89E22A-78CF-4037-BE0D-5F88EF47022B} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
||||||
{1D62101B-C2B1-4E79-8F7A-690E44E3EE81} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
{1D62101B-C2B1-4E79-8F7A-690E44E3EE81} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
||||||
{04F16325-8F44-4250-9B2E-0F77E8F65CBF} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
{04F16325-8F44-4250-9B2E-0F77E8F65CBF} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
||||||
|
{03F3C7D5-6FEF-4A83-9191-501091BFC1AC} = {C7821F15-DEDD-474F-A575-A296D4B58F10}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {5BA73C3B-D5FC-4942-9091-504325CDC308}
|
SolutionGuid = {5BA73C3B-D5FC-4942-9091-504325CDC308}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using Kruzya.TelegramBot.Core;
|
using Kruzya.TelegramBot.Core;
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement
|
namespace West.TelegramBot.ChatManagement;
|
||||||
|
|
||||||
|
public class ChatManagement : Module
|
||||||
{
|
{
|
||||||
public class ChatManagement : Module
|
public ChatManagement(Core core) : base(core)
|
||||||
{
|
{
|
||||||
public ChatManagement(Core core) : base(core)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,41 +3,41 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BotFramework.Attributes;
|
using BotFramework.Attributes;
|
||||||
using BotFramework.Enums;
|
using BotFramework.Enums;
|
||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
using Kruzya.TelegramBot.Core.Data;
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement.Handler
|
namespace West.TelegramBot.ChatManagement.Handler;
|
||||||
|
|
||||||
|
public class Ban : CommonHandler
|
||||||
{
|
{
|
||||||
public class Ban : ManagementHandler
|
public Ban(CoreContext db) : base(db) {}
|
||||||
|
|
||||||
|
[Command("ban", CommandParseMode.Both)]
|
||||||
|
public async Task HandleBan()
|
||||||
{
|
{
|
||||||
public Ban(CoreContext db) : base(db) {}
|
if (!await CanPunish()) return;
|
||||||
|
|
||||||
[Command("ban", CommandParseMode.Both)]
|
await BanMember(RepliedUser!);
|
||||||
public async Task HandleBan()
|
}
|
||||||
{
|
|
||||||
if (!await CanPunish()) return;
|
|
||||||
|
|
||||||
await BanMember(RepliedUser!);
|
[Command("unban", CommandParseMode.Both)]
|
||||||
}
|
public async Task HandleUnban()
|
||||||
|
{
|
||||||
|
if (!await CanPunish()) return;
|
||||||
|
|
||||||
[Command("unban", CommandParseMode.Both)]
|
await Bot.RestrictChatMemberAsync(Chat.Id, RepliedUser!.Id,
|
||||||
public async Task HandleUnban()
|
new ChatPermissions
|
||||||
{
|
{
|
||||||
if (!await CanPunish()) return;
|
CanSendMessages = true,
|
||||||
|
CanChangeInfo = true,
|
||||||
await Bot.RestrictChatMemberAsync(Chat.Id, RepliedUser!.Id,
|
CanInviteUsers = true,
|
||||||
new ChatPermissions
|
CanPinMessages = true,
|
||||||
{
|
CanSendPolls = true,
|
||||||
CanSendMessages = true,
|
CanSendMediaMessages = true,
|
||||||
CanChangeInfo = true,
|
CanSendOtherMessages = true,
|
||||||
CanInviteUsers = true,
|
CanAddWebPagePreviews = true
|
||||||
CanPinMessages = true,
|
});
|
||||||
CanSendPolls = true,
|
|
||||||
CanSendMediaMessages = true,
|
|
||||||
CanSendOtherMessages = true,
|
|
||||||
CanAddWebPagePreviews = true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using BotFramework.Attributes;
|
|
||||||
using BotFramework.Enums;
|
|
||||||
using BotFramework.Utils;
|
|
||||||
using Kruzya.TelegramBot.Core.Data;
|
|
||||||
using Kruzya.TelegramBot.Core.Extensions;
|
|
||||||
using Kruzya.TelegramBot.Core.Service;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Telegram.Bot;
|
|
||||||
using Telegram.Bot.Types;
|
|
||||||
using Telegram.Bot.Types.Enums;
|
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement.Handler
|
|
||||||
{
|
|
||||||
public class Reputation : ManagementHandler
|
|
||||||
{
|
|
||||||
private readonly UserService _userService;
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private static readonly List<string> RepUpChars = new()
|
|
||||||
{
|
|
||||||
"+",
|
|
||||||
"👍",
|
|
||||||
"👍🏼",
|
|
||||||
"👍🏽",
|
|
||||||
"👍🏾",
|
|
||||||
"👍🏿",
|
|
||||||
"👍🏻"
|
|
||||||
};
|
|
||||||
private static readonly List<string> RepDownChars = new()
|
|
||||||
{
|
|
||||||
"-",
|
|
||||||
"👎",
|
|
||||||
"👎🏼",
|
|
||||||
"👎🏽",
|
|
||||||
"👎🏾",
|
|
||||||
"👎🏿",
|
|
||||||
"👎🏻"
|
|
||||||
};
|
|
||||||
|
|
||||||
public Reputation(CoreContext db, UserService userService, ILogger<Reputation> logger) : base(db)
|
|
||||||
{
|
|
||||||
_userService = userService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("whois", CommandParseMode.Both)]
|
|
||||||
public async Task HandleWhois()
|
|
||||||
{
|
|
||||||
var user = RepliedUser ?? From;
|
|
||||||
var message = $"{user.ToHtml()}\n";
|
|
||||||
if (user.IsBot)
|
|
||||||
{
|
|
||||||
message += "<code>Бот</code>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message += $"<code>{await GetChatMemberStatusString(user)}</code>\n";
|
|
||||||
message += $"<code>Репутация: {await GetUserReputation(user)}\n";
|
|
||||||
message += $"Предупреждения: {(await GetWarnOption(user)).GetValue<int>()}</code>";
|
|
||||||
}
|
|
||||||
|
|
||||||
await Reply(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Message(InChat.Public, MessageFlag.HasText | MessageFlag.HasSticker)]
|
|
||||||
public async Task HandleReputation()
|
|
||||||
{
|
|
||||||
var text = Message?.Text;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(text))
|
|
||||||
{
|
|
||||||
text = Message?.Sticker?.Emoji;
|
|
||||||
}
|
|
||||||
if (text == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var receiver = RepliedUser;
|
|
||||||
var canChangeRep = receiver is { IsBot: false } && !From.IsBot && receiver.Id != From.Id;
|
|
||||||
|
|
||||||
if (canChangeRep && (RepUpChars.Contains(text) || RepDownChars.Contains(text)))
|
|
||||||
{
|
|
||||||
var receiverRepOption = await GetReputationOption(receiver!);
|
|
||||||
var receiverRepCount = receiverRepOption.GetValue<double>();
|
|
||||||
var senderRepCount = (await GetReputationOption(From)).GetValue<double>();
|
|
||||||
|
|
||||||
if (senderRepCount < 0)
|
|
||||||
{
|
|
||||||
await Reply(new HtmlString()
|
|
||||||
.Code("Ты не можешь голосовать с отрицательной репутацией.")
|
|
||||||
.ToString()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var diff = Math.Round(senderRepCount == 0 ? 1 : Math.Sqrt(senderRepCount), 2);
|
|
||||||
_logger.LogDebug("Calculated diff: {Diff}", diff);
|
|
||||||
|
|
||||||
string action;
|
|
||||||
if (RepUpChars.Contains(text))
|
|
||||||
{
|
|
||||||
receiverRepCount += diff;
|
|
||||||
action = "увеличил";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
receiverRepCount -= diff;
|
|
||||||
action = "уменьшил";
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogDebug("Saving receiver rep with value: {Value}", receiverRepCount);
|
|
||||||
receiverRepOption.SetValue(receiverRepCount);
|
|
||||||
Db.AddOrUpdate(receiverRepOption);
|
|
||||||
_logger.LogDebug("Updated value: {Value}", receiverRepOption.GetValue<double>());
|
|
||||||
|
|
||||||
await Reply($"{From.ToHtml()}<code>({await GetUserReputation(From)}) " +
|
|
||||||
$"{action} репутацию </code>{receiver.ToHtml()}<code>({Math.Round(receiverRepCount, 2)})</code>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ParametrizedCommand(InChat.Public, "setrep", CommandParseMode.Both)]
|
|
||||||
public async Task HandleSetRep(double reputation)
|
|
||||||
{
|
|
||||||
if (!_userService.IsUserSuper(From))
|
|
||||||
{
|
|
||||||
_logger.LogInformation("{User} attempted to call /setrep in {Chat}", From, Chat.Title);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RepliedUser == null || RepliedUser.IsBot)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
reputation = Math.Round(reputation, 2);
|
|
||||||
var opt = await GetReputationOption(RepliedUser);
|
|
||||||
opt.SetValue(reputation);
|
|
||||||
Db.AddOrUpdate(opt);
|
|
||||||
|
|
||||||
await Reply(
|
|
||||||
new HtmlString()
|
|
||||||
.Code("Пользователю ")
|
|
||||||
.UserMention(RepliedUser)
|
|
||||||
.Code($" установлена репутация: {reputation}")
|
|
||||||
.ToString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<double> GetUserReputation(User user)
|
|
||||||
{
|
|
||||||
return Math.Round((await GetReputationOption(user)).GetValue<double>(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<BotUserValue> GetReputationOption(User user)
|
|
||||||
{
|
|
||||||
return await Db.UserValues.FindOrCreateOption(Chat.Id, user.Id, "reputation");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> GetChatMemberStatusString(User user)
|
|
||||||
{
|
|
||||||
var customStatus = _userService.GetUserCustomStatus(user.Id);
|
|
||||||
if (customStatus != null)
|
|
||||||
{
|
|
||||||
return customStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chatMember = await Bot.GetChatMemberAsync(Chat.Id, user.Id);
|
|
||||||
return chatMember.Status switch
|
|
||||||
{
|
|
||||||
ChatMemberStatus.Creator => "Владелец",
|
|
||||||
ChatMemberStatus.Restricted => "Заблокированный",
|
|
||||||
ChatMemberStatus.Administrator => "Администратор",
|
|
||||||
_ => "Пользователь"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +1,47 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BotFramework.Attributes;
|
using BotFramework.Attributes;
|
||||||
using BotFramework.Enums;
|
using BotFramework.Enums;
|
||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
using Kruzya.TelegramBot.Core.Data;
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
using Kruzya.TelegramBot.Core.Extensions;
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Exceptions;
|
using Telegram.Bot.Exceptions;
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement.Handler
|
namespace West.TelegramBot.ChatManagement.Handler;
|
||||||
|
|
||||||
|
class Rules : CommonHandler
|
||||||
{
|
{
|
||||||
class Rules : ManagementHandler
|
[Command(InChat.Public, "setrules", CommandParseMode.Both)]
|
||||||
|
public async Task HandleSetRules()
|
||||||
{
|
{
|
||||||
[Command(InChat.Public, "setrules", CommandParseMode.Both)]
|
var replyToMessage = Message?.ReplyToMessage;
|
||||||
public async Task HandleSetRules()
|
|
||||||
{
|
|
||||||
var replyToMessage = Message?.ReplyToMessage;
|
|
||||||
|
|
||||||
if (replyToMessage == null || !await Bot.IsUserAdminAsync(Chat, From)) return;
|
if (replyToMessage == null || !await Bot.IsUserAdminAsync(Chat, From)) return;
|
||||||
|
|
||||||
var opt = await Db.UserValues.FindOrCreateOption(Chat.Id, "rulesMsgId");
|
var opt = await Db.UserValues.FindOrCreateOption(Chat.Id, "rulesMsgId");
|
||||||
opt.SetValue(replyToMessage.MessageId);
|
opt.SetValue(replyToMessage.MessageId);
|
||||||
Db.AddOrUpdate(opt);
|
Db.AddOrUpdate(opt);
|
||||||
}
|
|
||||||
|
|
||||||
[Command(InChat.Public, "rules", CommandParseMode.Both)]
|
|
||||||
public async Task HandleRules()
|
|
||||||
{
|
|
||||||
var opt = await Db.UserValues.FindOption(Chat.Id, "rulesMsgId");
|
|
||||||
if (opt == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Bot.CopyMessageAsync(Chat.Id, Chat.Id, opt.GetValue<int>());
|
|
||||||
}
|
|
||||||
catch (ApiRequestException)
|
|
||||||
{
|
|
||||||
Db.UserValues.Remove(opt);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rules(CoreContext db) : base(db) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command(InChat.Public, "rules", CommandParseMode.Both)]
|
||||||
|
public async Task HandleRules()
|
||||||
|
{
|
||||||
|
var opt = await Db.UserValues.FindOption(Chat.Id, "rulesMsgId");
|
||||||
|
if (opt == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Bot.CopyMessageAsync(Chat.Id, Chat.Id, opt.GetValue<int>());
|
||||||
|
}
|
||||||
|
catch (ApiRequestException)
|
||||||
|
{
|
||||||
|
Db.UserValues.Remove(opt);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rules(CoreContext db) : base(db) {}
|
||||||
}
|
}
|
||||||
@@ -4,59 +4,59 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BotFramework.Attributes;
|
using BotFramework.Attributes;
|
||||||
using BotFramework.Enums;
|
using BotFramework.Enums;
|
||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
using Kruzya.TelegramBot.Core.Data;
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
using Kruzya.TelegramBot.Core.Extensions;
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
|
|
||||||
namespace West.TelegramBot.ChatManagement.Handler
|
namespace West.TelegramBot.ChatManagement.Handler;
|
||||||
|
|
||||||
|
public class Warn : CommonHandler
|
||||||
{
|
{
|
||||||
public class Warn : ManagementHandler
|
public Warn(CoreContext db) : base(db) {}
|
||||||
|
|
||||||
|
[Command("warn", CommandParseMode.Both)]
|
||||||
|
public async Task HandleWarn()
|
||||||
{
|
{
|
||||||
public Warn(CoreContext db) : base(db) {}
|
if (!await CanPunish()) return;
|
||||||
|
|
||||||
[Command("warn", CommandParseMode.Both)]
|
var warnUser = RepliedUser!;
|
||||||
public async Task HandleWarn()
|
var warnOption = await GetWarnOption(warnUser);
|
||||||
|
var warnCount = warnOption!.GetValue<int>() + 1;
|
||||||
|
warnOption.SetValue(warnCount);
|
||||||
|
Db.AddOrUpdate(warnOption);
|
||||||
|
|
||||||
|
await Bot.SendTextMessageAsync(Chat.Id, $"{warnUser.ToHtml()}<code>, Вам выдано предупреждение! " +
|
||||||
|
$"Текущее кол-во: {warnCount}/3</code>",
|
||||||
|
disableNotification: true,
|
||||||
|
parseMode: ParseMode.Html);
|
||||||
|
|
||||||
|
if (warnCount == 3)
|
||||||
{
|
{
|
||||||
if (!await CanPunish()) return;
|
await BanMember(warnUser);
|
||||||
|
warnOption.SetValue(0);
|
||||||
var warnUser = RepliedUser!;
|
|
||||||
var warnOption = await GetWarnOption(warnUser);
|
|
||||||
var warnCount = warnOption!.GetValue<int>() + 1;
|
|
||||||
warnOption.SetValue(warnCount);
|
|
||||||
Db.AddOrUpdate(warnOption);
|
Db.AddOrUpdate(warnOption);
|
||||||
|
|
||||||
await Bot.SendTextMessageAsync(Chat.Id, $"{warnUser.ToHtml()}<code>, Вам выдано предупреждение! " +
|
|
||||||
$"Текущее кол-во: {warnCount}/3</code>",
|
|
||||||
disableNotification: true,
|
|
||||||
parseMode: ParseMode.Html);
|
|
||||||
|
|
||||||
if (warnCount == 3)
|
|
||||||
{
|
|
||||||
await BanMember(warnUser);
|
|
||||||
warnOption.SetValue(0);
|
|
||||||
Db.AddOrUpdate(warnOption);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("unwarn", CommandParseMode.Both)]
|
|
||||||
public async Task HandleUnwarn()
|
|
||||||
{
|
|
||||||
if (!await CanPunish()) return;
|
|
||||||
|
|
||||||
var warnOption = await GetWarnOption(RepliedUser!);
|
|
||||||
var warnCount = warnOption.GetValue<int>();
|
|
||||||
if (warnCount == 0) return;
|
|
||||||
|
|
||||||
warnCount = Math.Max(warnCount - 1, 0);
|
|
||||||
warnOption.SetValue(warnCount);
|
|
||||||
Db.AddOrUpdate(warnOption);
|
|
||||||
|
|
||||||
await Bot.SendTextMessageAsync(Chat.Id,
|
|
||||||
$"{RepliedUser.ToHtml()}<code>, с Вас снято предупреждение! " +
|
|
||||||
$"Текущее кол-во: {warnCount}/3</code>",
|
|
||||||
disableNotification: true,
|
|
||||||
parseMode: ParseMode.Html);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command("unwarn", CommandParseMode.Both)]
|
||||||
|
public async Task HandleUnwarn()
|
||||||
|
{
|
||||||
|
if (!await CanPunish()) return;
|
||||||
|
|
||||||
|
var warnOption = await GetWarnOption(RepliedUser!);
|
||||||
|
var warnCount = warnOption.GetValue<int>();
|
||||||
|
if (warnCount == 0) return;
|
||||||
|
|
||||||
|
warnCount = Math.Max(warnCount - 1, 0);
|
||||||
|
warnOption.SetValue(warnCount);
|
||||||
|
Db.AddOrUpdate(warnOption);
|
||||||
|
|
||||||
|
await Bot.SendTextMessageAsync(Chat.Id,
|
||||||
|
$"{RepliedUser.ToHtml()}<code>, с Вас снято предупреждение! " +
|
||||||
|
$"Текущее кол-во: {warnCount}/3</code>",
|
||||||
|
disableNotification: true,
|
||||||
|
parseMode: ParseMode.Html);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BotFramework.Attributes;
|
||||||
|
using BotFramework.Enums;
|
||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
|
using Kruzya.TelegramBot.Core.Service;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.ChatManagement.Handler;
|
||||||
|
|
||||||
|
public class WhoIs : CommonHandler
|
||||||
|
{
|
||||||
|
private readonly IReputation? _reputation;
|
||||||
|
private readonly UserService _userService;
|
||||||
|
|
||||||
|
public WhoIs(CoreContext db, UserService userService, IServiceProvider provider) : base(db)
|
||||||
|
{
|
||||||
|
_reputation = provider.GetService<IReputation>();
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("whois", CommandParseMode.Both)]
|
||||||
|
public async Task HandleWhoIs()
|
||||||
|
{
|
||||||
|
var user = RepliedUser ?? From;
|
||||||
|
var message = $"{user.ToHtml()}\n";
|
||||||
|
if (user.IsBot)
|
||||||
|
{
|
||||||
|
message += "<code>Бот</code>";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message += $"<code>{await GetChatMemberStatusString(user)}</code>\n";
|
||||||
|
if (_reputation != null)
|
||||||
|
{
|
||||||
|
message += $"<code>Репутация: {await _reputation.GetUserReputationAsync(Chat, user)}\n</code>";
|
||||||
|
}
|
||||||
|
message += $"<code>Предупреждения: {(await GetWarnOption(user)).GetValue<int>()}</code>";
|
||||||
|
}
|
||||||
|
|
||||||
|
await Reply(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> GetChatMemberStatusString(User user)
|
||||||
|
{
|
||||||
|
var customStatus = _userService.GetUserCustomStatus(user.Id);
|
||||||
|
if (customStatus != null)
|
||||||
|
{
|
||||||
|
return customStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chatMember = await Bot.GetChatMemberAsync(Chat.Id, user.Id);
|
||||||
|
return chatMember.Status switch
|
||||||
|
{
|
||||||
|
ChatMemberStatus.Creator => "Владелец",
|
||||||
|
ChatMemberStatus.Restricted => "Заблокированный",
|
||||||
|
ChatMemberStatus.Administrator => "Администратор",
|
||||||
|
_ => "Пользователь"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Kruzya.TelegramBot.Core.EF;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
public class ReputationContext : AutoSaveDbContext
|
||||||
|
{
|
||||||
|
public DbSet<UserReputation> UserReputation { get; set; } = null!;
|
||||||
|
|
||||||
|
public ReputationContext(DbContextOptions<ReputationContext> options) : base(options)
|
||||||
|
{
|
||||||
|
Database.EnsureCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
modelBuilder.Entity<UserReputation>().HasKey(e => new {e.ChatId, e.UserId});
|
||||||
|
modelBuilder.Entity<UserReputation>().HasIndex(e => e.ChatId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
public class UserReputation : IReputationEntity
|
||||||
|
{
|
||||||
|
public long UserId { get; set; }
|
||||||
|
public long ChatId { get; set; }
|
||||||
|
public double Value { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#nullable enable
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
public class ReputationContextFactory : IDesignTimeDbContextFactory<ReputationContext>
|
||||||
|
{
|
||||||
|
public ReputationContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<ReputationContext>();
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("You should pass DSN as CLI argument in order to use this.");
|
||||||
|
}
|
||||||
|
var dsn = args[0];
|
||||||
|
optionsBuilder.UseMySql(dsn, ServerVersion.AutoDetect(dsn));
|
||||||
|
|
||||||
|
return new ReputationContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using BotFramework;
|
||||||
|
using BotFramework.Attributes;
|
||||||
|
using BotFramework.Enums;
|
||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Kruzya.TelegramBot.Core.Service;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Handler;
|
||||||
|
|
||||||
|
public class Debug : BotEventHandler
|
||||||
|
{
|
||||||
|
private readonly CoreContext _coreContext;
|
||||||
|
private readonly ReputationContext _reputationContext;
|
||||||
|
private readonly UserService _userService;
|
||||||
|
|
||||||
|
|
||||||
|
public Debug(CoreContext coreContext, ReputationContext reputationContext, UserService userService)
|
||||||
|
{
|
||||||
|
_coreContext = coreContext;
|
||||||
|
_reputationContext = reputationContext;
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(InChat.Public, "dbg_migrate", CommandParseMode.Both)]
|
||||||
|
public async Task HandleMigrate()
|
||||||
|
{
|
||||||
|
if (!_userService.IsUserSuper(From))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reputationValues = _coreContext.UserValues.AsNoTracking()
|
||||||
|
.Where(value => value.Name == "reputation").Include(v => v.BotUser);
|
||||||
|
|
||||||
|
foreach (var reputationValue in reputationValues)
|
||||||
|
{
|
||||||
|
var value = reputationValue.GetValue<double>();
|
||||||
|
if (value == 0D)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userReputation = new UserReputation
|
||||||
|
{
|
||||||
|
ChatId = reputationValue.BotUser.ChatId,
|
||||||
|
UserId = reputationValue.BotUser.UserId,
|
||||||
|
Value = value
|
||||||
|
};
|
||||||
|
|
||||||
|
var repList = _reputationContext.UserReputation;
|
||||||
|
if (repList.Contains(userReputation))
|
||||||
|
{
|
||||||
|
repList.Update(userReputation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
repList.Add(userReputation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _reputationContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using BotFramework.Attributes;
|
||||||
|
using BotFramework.Enums;
|
||||||
|
using BotFramework.Utils;
|
||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
|
using Kruzya.TelegramBot.Core.Service;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Handler;
|
||||||
|
|
||||||
|
public class Reputation : CommonHandler
|
||||||
|
{
|
||||||
|
private readonly UserService _userService;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly IReputation _reputationService;
|
||||||
|
|
||||||
|
private static readonly List<string> RepUpChars = new()
|
||||||
|
{
|
||||||
|
"+",
|
||||||
|
"👍",
|
||||||
|
"👍🏼",
|
||||||
|
"👍🏽",
|
||||||
|
"👍🏾",
|
||||||
|
"👍🏿",
|
||||||
|
"👍🏻"
|
||||||
|
};
|
||||||
|
private static readonly List<string> RepDownChars = new()
|
||||||
|
{
|
||||||
|
"-",
|
||||||
|
"👎",
|
||||||
|
"👎🏼",
|
||||||
|
"👎🏽",
|
||||||
|
"👎🏾",
|
||||||
|
"👎🏿",
|
||||||
|
"👎🏻"
|
||||||
|
};
|
||||||
|
|
||||||
|
public Reputation(
|
||||||
|
CoreContext db,
|
||||||
|
UserService userService,
|
||||||
|
IReputation reputationService,
|
||||||
|
ILogger<Reputation> logger) : base(db)
|
||||||
|
{
|
||||||
|
_reputationService = reputationService;
|
||||||
|
_userService = userService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Message(InChat.Public, MessageFlag.HasText | MessageFlag.HasSticker)]
|
||||||
|
public async Task HandleReputation()
|
||||||
|
{
|
||||||
|
var text = Message?.Text;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
{
|
||||||
|
text = Message?.Sticker?.Emoji;
|
||||||
|
}
|
||||||
|
if (text == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var receiver = RepliedUser;
|
||||||
|
var canChangeRep = receiver is { IsBot: false } && !From.IsBot && receiver.Id != From.Id;
|
||||||
|
|
||||||
|
if (canChangeRep && (RepUpChars.Contains(text) || RepDownChars.Contains(text)))
|
||||||
|
{
|
||||||
|
var senderRepCount = await GetUserReputation(From);
|
||||||
|
|
||||||
|
if (senderRepCount < 0)
|
||||||
|
{
|
||||||
|
await Reply(new HtmlString()
|
||||||
|
.Code("Ты не можешь голосовать с отрицательной репутацией.")
|
||||||
|
.ToString()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var diff = await _reputationService.GetReputationDiffAsync(Chat, From, receiver);
|
||||||
|
_logger.LogDebug("Calculated diff: {Diff}", diff);
|
||||||
|
|
||||||
|
string action;
|
||||||
|
if (RepUpChars.Contains(text))
|
||||||
|
{
|
||||||
|
action = "увеличил";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff *= -1;
|
||||||
|
action = "уменьшил";
|
||||||
|
}
|
||||||
|
|
||||||
|
await _reputationService.IncrementReputationAsync(Chat, receiver, diff);
|
||||||
|
|
||||||
|
var receiverRepCount = await GetUserReputation(receiver!);
|
||||||
|
_logger.LogDebug("Updated value: {Value}", receiverRepCount);
|
||||||
|
|
||||||
|
await Reply($"{From.ToHtml()}<code>({await GetUserReputation(From)}) " +
|
||||||
|
$"{action} репутацию </code>{receiver.ToHtml()}<code>({Math.Round(receiverRepCount, 2)})</code>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ParametrizedCommand(InChat.Public, "setrep", CommandParseMode.Both)]
|
||||||
|
public async Task HandleSetRep(double reputation)
|
||||||
|
{
|
||||||
|
if (!_userService.IsUserSuper(From))
|
||||||
|
{
|
||||||
|
_logger.LogInformation("{User} attempted to call /setrep in {Chat}", From, Chat.Title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RepliedUser == null || RepliedUser.IsBot)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reputation = await _reputationService.SetUserReputationAsync(Chat, RepliedUser, reputation);
|
||||||
|
await Reply(
|
||||||
|
new HtmlString()
|
||||||
|
.Code("Пользователю ")
|
||||||
|
.UserMention(RepliedUser)
|
||||||
|
.Code($" установлена репутация: {reputation}")
|
||||||
|
.ToString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(InChat.Public, "top", CommandParseMode.Both)]
|
||||||
|
public async Task HandleTop()
|
||||||
|
{
|
||||||
|
var msg = new HtmlString();
|
||||||
|
msg.TextBr("Топ репутации чата: ");
|
||||||
|
var i = 1;
|
||||||
|
foreach (var rep in await _reputationService.GetChatRatingAsync(Chat))
|
||||||
|
{
|
||||||
|
var user = await _userService.GetUser(rep.UserId, rep.ChatId);
|
||||||
|
msg.Text($"{i}. ").UserMention(user).Text($" ({rep.Value})").Br();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Bot.SendHtmlStringAsync(Chat, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<double> GetUserReputation(User user)
|
||||||
|
{
|
||||||
|
return await _reputationService.GetUserReputationAsync(Chat, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ReputationContext))]
|
||||||
|
[Migration("20220213182703_Initial")]
|
||||||
|
partial class Initial
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "6.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("West.TelegramBot.ChatManagement.Data.UserReputation", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("ChatId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<double>("Value")
|
||||||
|
.HasColumnType("double");
|
||||||
|
|
||||||
|
b.HasKey("ChatId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("ChatId");
|
||||||
|
|
||||||
|
b.ToTable("UserReputation");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Migrations;
|
||||||
|
|
||||||
|
public partial class Initial : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterDatabase()
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserReputation",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ChatId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
Value = table.Column<double>(type: "double", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserReputation", x => new { x.ChatId, x.UserId });
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserReputation_ChatId",
|
||||||
|
table: "UserReputation",
|
||||||
|
column: "ChatId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserReputation");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Migrations;
|
||||||
|
|
||||||
|
[DbContext(typeof(ReputationContext))]
|
||||||
|
partial class ReputationContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "6.0.2")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("West.TelegramBot.ChatManagement.Data.UserReputation", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("ChatId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("UserId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<double>("Value")
|
||||||
|
.HasColumnType("double");
|
||||||
|
|
||||||
|
b.HasKey("ChatId", "UserId");
|
||||||
|
|
||||||
|
b.HasIndex("ChatId");
|
||||||
|
|
||||||
|
b.ToTable("UserReputation");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Kruzya.TelegramBot.Core;
|
||||||
|
using Kruzya.TelegramBot.Core.Service;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using West.TelegramBot.Reputation.Data;
|
||||||
|
using West.TelegramBot.Reputation.Service;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation;
|
||||||
|
|
||||||
|
public class Reputation : Module
|
||||||
|
{
|
||||||
|
public Reputation(Core core) : base(core)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
var dsn = Configuration.GetConnectionString("Reputation");
|
||||||
|
services.AddDbContext<ReputationContext>(options => options.UseMySql(dsn, ServerVersion.AutoDetect(dsn)));
|
||||||
|
|
||||||
|
services.AddScoped<IReputation, ReputationService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AssemblyName>TelegramBot.Reputation</AssemblyName>
|
||||||
|
<RootNamespace>West.TelegramBot.Reputation</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\Core\Core.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Service\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<Delete Files="$(OutDir)\TelegramBot.dll" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
|
using Kruzya.TelegramBot.Core.Service;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
using West.TelegramBot.Reputation.Data;
|
||||||
|
|
||||||
|
namespace West.TelegramBot.Reputation.Service;
|
||||||
|
|
||||||
|
public class ReputationService : IReputation
|
||||||
|
{
|
||||||
|
private readonly ReputationContext _reputationContext;
|
||||||
|
|
||||||
|
public ReputationService(ReputationContext reputationContext)
|
||||||
|
{
|
||||||
|
_reputationContext = reputationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<double> GetUserReputationAsync(Chat chat, User user)
|
||||||
|
{
|
||||||
|
var rep = await FindUserReputation(chat, user);
|
||||||
|
|
||||||
|
return rep?.Value ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<double> SetUserReputationAsync(Chat chat, User user, double value)
|
||||||
|
{
|
||||||
|
var rep = await FindUserReputation(chat, user) ?? new UserReputation
|
||||||
|
{
|
||||||
|
ChatId = chat.Id,
|
||||||
|
UserId = user.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
rep.Value = Math.Round(value, 2);
|
||||||
|
_reputationContext.AddOrUpdate(rep);
|
||||||
|
|
||||||
|
return rep.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task IncrementReputationAsync(Chat chat, User user, double diff)
|
||||||
|
{
|
||||||
|
await SetUserReputationAsync(chat, user, await GetUserReputationAsync(chat, user) + diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<double> GetReputationDiffAsync(Chat chat, User sender, User receiver)
|
||||||
|
{
|
||||||
|
var senderRep = await FindUserReputation(chat, sender);
|
||||||
|
|
||||||
|
if (senderRep == null)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var senderValue = senderRep.Value;
|
||||||
|
return Math.Round(senderValue == 0 ? 1 : Math.Sqrt(senderValue), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<IReputationEntity>> GetChatRatingAsync(Chat chat, int limit = 10, int offset = 0)
|
||||||
|
{
|
||||||
|
return await _reputationContext.UserReputation
|
||||||
|
.Where(r => r.ChatId == chat.Id && r.Value > 0D)
|
||||||
|
.OrderByDescending(r => r.Value)
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(limit)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<UserReputation?> FindUserReputation(Chat chat, User user)
|
||||||
|
{
|
||||||
|
return await _reputationContext.UserReputation.SingleOrDefaultAsync(
|
||||||
|
e => e.ChatId == chat.Id && e.UserId == user.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user