mirror of
https://github.com/Bubuni-Team/telegram-bot.git
synced 2026-07-31 00:29:24 +03:00
[reputation] Move reputation to separate module.
This commit is contained in:
@@ -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 @@
|
||||
namespace West.TelegramBot.Reputation.Data;
|
||||
|
||||
public class UserReputation
|
||||
{
|
||||
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,175 @@
|
||||
#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;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
[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 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()
|
||||
);
|
||||
}
|
||||
|
||||
private async Task<double> GetUserReputation(User user)
|
||||
{
|
||||
return await _reputationService.GetUserReputationAsync(Chat, user);
|
||||
}
|
||||
|
||||
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,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,40 @@
|
||||
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,40 @@
|
||||
// <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,25 @@
|
||||
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,62 @@
|
||||
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);
|
||||
}
|
||||
|
||||
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