Merge pull request #33 from Bubuni-Team/feature/core/chat-options

[core] New fancy option system
This commit is contained in:
Andriy
2023-07-21 19:29:54 +03:00
committed by GitHub
45 changed files with 705 additions and 93 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
+36
View File
@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore;
using System.Text.Json.Serialization;
using System.Text.Json;
using System;
namespace Kruzya.TelegramBot.Core.Data;
[PrimaryKey(nameof(ChatId), nameof(OptionId))]
public class ChatOptionValue
{
public long ChatId { get; set; }
public string OptionId { get; set; }
public byte[] Value { get; set; }
public T GetValue<T>(T defVal = default)
{
try
{
return JsonSerializer.Deserialize<T>(new ReadOnlySpan<byte>(Value));
}
catch (Exception)
{
return defVal;
}
}
public void SetValue<T>(T value)
{
Value = JsonSerializer.SerializeToUtf8Bytes(value, new JsonSerializerOptions
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
});
}
}
+2
View File
@@ -15,6 +15,8 @@ namespace Kruzya.TelegramBot.Core.Data
/// </summary> /// </summary>
public DbSet<BotUserValue> UserValues { get; set; } public DbSet<BotUserValue> UserValues { get; set; }
public DbSet<ChatOptionValue> ChatOptionValues { get; set; }
public CoreContext(DbContextOptions<CoreContext> ctx) : base(ctx) public CoreContext(DbContextOptions<CoreContext> ctx) : base(ctx)
{ {
Database.Migrate(); Database.Migrate();
+3 -3
View File
@@ -22,14 +22,14 @@ namespace Kruzya.TelegramBot.Core.Extensions
public static async Task<bool> IsUserAdminAsync(this ITelegramBotClient bot, Chat chat, User user) public static async Task<bool> IsUserAdminAsync(this ITelegramBotClient bot, Chat chat, User user)
{ {
var callerMember = await bot.GetChatMemberAsync(chat, user.Id); var callerMember = await bot.GetChatMemberAsync(chat, user.Id);
var canUse = callerMember is ChatMemberOwner; var isAdmin = callerMember is ChatMemberOwner;
if (callerMember is ChatMemberAdministrator callerAdmin) if (callerMember is ChatMemberAdministrator callerAdmin)
{ {
canUse = callerAdmin.CanRestrictMembers; isAdmin = callerAdmin.CanRestrictMembers;
} }
return canUse; return isAdmin;
} }
public static async Task<bool> CanDeleteMessagesAsync(this ITelegramBotClient bot, Chat chat) public static async Task<bool> CanDeleteMessagesAsync(this ITelegramBotClient bot, Chat chat)
@@ -0,0 +1,26 @@
using System.Threading.Tasks;
using Kruzya.TelegramBot.Core.Data;
using Microsoft.EntityFrameworkCore;
namespace Kruzya.TelegramBot.Core.Extensions;
public partial class RepositoryExtension
{
public static async Task<ChatOptionValue> FindOption(this DbSet<ChatOptionValue> repository, long chatId,
string optionId)
{
return await repository.SingleOrDefaultAsync(x => x.ChatId == chatId && x.OptionId == optionId);
}
public static async Task<ChatOptionValue> FindOrCreateOption(this DbSet<ChatOptionValue> repository, long chatId,
string optionId)
{
var opt = await repository.FindOption(chatId, optionId) ?? new ChatOptionValue
{
ChatId = chatId,
OptionId = optionId
};
return opt;
}
}
+9 -4
View File
@@ -1,15 +1,20 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Kruzya.TelegramBot.Core.Cache; using Kruzya.TelegramBot.Core.Cache;
using Kruzya.TelegramBot.Core.Options;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Kruzya.TelegramBot.Core.Extensions namespace Kruzya.TelegramBot.Core.Extensions;
public static class StartupExtension
{ {
public static class StartupExtension
{
public static IServiceCollection AddQueue<T>(this IServiceCollection collection) public static IServiceCollection AddQueue<T>(this IServiceCollection collection)
=> collection.AddSingleton<ConcurrentQueue<T>>(); => collection.AddSingleton<ConcurrentQueue<T>>();
public static IServiceCollection AddMemoryCache<TKey, TValue>(this IServiceCollection collection) public static IServiceCollection AddMemoryCache<TKey, TValue>(this IServiceCollection collection)
=> collection.AddSingleton<MemoryCache<TKey, TValue>>(); => collection.AddSingleton<MemoryCache<TKey, TValue>>();
}
public static IServiceCollection AddOption<T>(this IServiceCollection collection) where T : class, IOption
=> collection.AddScoped<T>()
.AddScoped<IOption, T>(s => s.GetService<T>());
} }
@@ -9,7 +9,7 @@ using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
namespace Kruzya.TelegramBot.Core namespace Kruzya.TelegramBot.Core.Handler
{ {
public abstract class CommonHandler : BotEventHandler public abstract class CommonHandler : BotEventHandler
{ {
@@ -53,5 +53,10 @@ namespace Kruzya.TelegramBot.Core
{ {
return await Db.UserValues.FindOrCreateOption(Chat.Id, user.Id, "warnCount"); return await Db.UserValues.FindOrCreateOption(Chat.Id, user.Id, "warnCount");
} }
protected async Task AnswerQuery(string id, string? message = null)
{
await Bot.AnswerCallbackQueryAsync(id, message);
}
} }
} }
@@ -1,14 +1,12 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BotFramework; using BotFramework;
using BotFramework.Attributes; using BotFramework.Attributes;
using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace Kruzya.TelegramBot.Core namespace Kruzya.TelegramBot.Core.Handler
{ {
public class GeneralHandler : BotEventHandler public class GeneralHandler : BotEventHandler
{ {
@@ -32,7 +30,7 @@ namespace Kruzya.TelegramBot.Core
[Priority(short.MaxValue - 10)] [Priority(short.MaxValue - 10)]
public async Task<bool> Listener() public async Task<bool> Listener()
{ {
foreach (var message in new Message[] {RawUpdate.Message, RawUpdate.EditedMessage, RawUpdate.ChannelPost, RawUpdate.EditedChannelPost}) foreach (var message in new Message[] { RawUpdate.Message, RawUpdate.EditedMessage, RawUpdate.ChannelPost, RawUpdate.EditedChannelPost })
{ {
if (message == null) if (message == null)
{ {
+184
View File
@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BotFramework.Attributes;
using BotFramework.Enums;
using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions;
using Kruzya.TelegramBot.Core.Options;
using Microsoft.Extensions.DependencyInjection;
using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
namespace Kruzya.TelegramBot.Core.Handler;
public class SettingsHandler : CommonHandler
{
private readonly IEnumerable<IOption> _options;
public SettingsHandler(CoreContext db, IServiceProvider serviceProvider) : base(db)
{
_options = serviceProvider.GetServices<IOption>();
}
[Command(InChat.Public, "settings", CommandParseMode.WithUsername)]
public async Task HandleSettings()
{
if (!await Bot.IsUserAdminAsync(Chat, From))
{
return;
}
await Bot.SendTextMessageAsync(
Chat.Id,
"What do you want to change?",
replyMarkup: GetSettingsMarkup()
);
}
[Update(InChat.Public, UpdateFlag.CallbackQuery)]
public async Task HandleCallback()
{
var query = CallbackQuery;
var queryData = query.Data;
if (queryData == null || !queryData.StartsWith("option") || !await Bot.IsUserAdminAsync(Chat, From))
{
return;
}
var paramList = queryData.Split('|');
if (!long.TryParse(paramList[1], out var senderId))
{
return;
}
if (senderId != From.Id)
{
await AnswerQuery(query.Id, "Keyboard called by another user");
return;
}
if (paramList[2] == "main_menu")
{
await AnswerQuery(query.Id);
await Bot.EditMessageTextAsync(Chat.Id, query.Message!.MessageId, "What do you want to change?",
replyMarkup: GetSettingsMarkup());
return;
}
if (paramList[2] == "edit" || paramList[2] == "select")
{
var option = GetOptionById(paramList[3]);
if (option == null)
{
await AnswerQuery(query.Id, "Invalid option ID");
return;
}
switch (paramList[2])
{
case "edit":
await HandleOptionEdit(option, paramList, query);
break;
case "select":
await HandleOptionSelect(option, paramList, query);
break;
}
}
}
private async Task HandleOptionEdit(IOption option, string[] paramList, CallbackQuery query)
{
var chatId = Chat.Id;
var messageId = query.Message!.MessageId;
switch (option.OptionType)
{
case OptionType.OnOff:
var onOffOpt = (IOption<bool>) option;
await onOffOpt.SetValueAsync(chatId, !await onOffOpt.GetValueAsync(chatId));
await AnswerQuery(query.Id);
await Bot.EditMessageReplyMarkupAsync(chatId, messageId, GetSettingsMarkup());
break;
case OptionType.Select:
var selectOpt = (IOption<string>) option;
await AnswerQuery(query.Id);
await Bot.EditMessageTextAsync(Chat.Id, messageId, "Here's what you can choose:",
replyMarkup: await GetChoiceSelectKeyboard(selectOpt));
break;
default:
await AnswerQuery(query.Id, "Not implemented");
break;
}
}
private async Task HandleOptionSelect(IOption option, string[] paramList, CallbackQuery query)
{
var choice = option.ChoiceList.SingleOrDefault(x => x.Id == paramList[4]);
if (choice == null)
{
await AnswerQuery(query.Id, "Invalid option choice");
return;
}
var selectOption = (IOption<string>) option;
await selectOption.SetValueAsync(Chat.Id, choice.Id);
await AnswerQuery(query.Id);
await Bot.EditMessageReplyMarkupAsync(Chat.Id, query.Message!.MessageId, await GetChoiceSelectKeyboard(selectOption));
}
private async Task<InlineKeyboardButton> GetKeyboardButton(IOption option)
{
var button = new InlineKeyboardButton(option.OptionName)
{
CallbackData = $"option|{From.Id}|edit|{option.OptionId}"
};
if (option.OptionType == OptionType.OnOff)
{
var isEnabled = await ((IOption<bool>) option).GetValueAsync(Chat.Id);
button.Text += isEnabled ? " 🟢" : " 🔴";
}
return button;
}
private async Task<InlineKeyboardMarkup> GetChoiceSelectKeyboard(IOption<string> option)
{
var value = await option.GetValueAsync(Chat.Id);
var choices = option.ChoiceList.Select(x => x.ToButton(From.Id, option.OptionId, x.Id == value))
.ToList();
choices.Add(new []
{
new InlineKeyboardButton("Main menu")
{
CallbackData = $"option|{From.Id}|main_menu"
}
});
return new InlineKeyboardMarkup(choices);
}
private InlineKeyboardMarkup GetSettingsMarkup()
{
var keyboardLayout = _options.ToAsyncEnumerable()
.SelectAwait(async x => await GetKeyboardButton(x))
.ToEnumerable()
.Chunk(3);
return new InlineKeyboardMarkup(keyboardLayout);
}
private IOption GetOptionById(string optionId)
{
return _options.SingleOrDefault(x => x.OptionId == optionId);
}
}
@@ -0,0 +1,95 @@
// <auto-generated />
using System;
using Kruzya.TelegramBot.Core.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Kruzya.TelegramBot.Core.Migrations
{
[DbContext(typeof(CoreContext))]
[Migration("20230720171645_AddChatOptions")]
partial class AddChatOptions
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUser", b =>
{
b.Property<Guid>("BotUserId")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<long>("ChatId")
.HasColumnType("bigint");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("BotUserId");
b.ToTable("Users");
});
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b =>
{
b.Property<Guid>("BotUserValueId")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<Guid?>("BotUserId")
.HasColumnType("char(36)");
b.Property<string>("Name")
.HasColumnType("longtext");
b.Property<byte[]>("Value")
.HasColumnType("longblob");
b.Property<string>("ValueType")
.HasColumnType("longtext");
b.HasKey("BotUserValueId");
b.HasIndex("BotUserId");
b.ToTable("UserValues");
});
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.ChatOptionValue", b =>
{
b.Property<long>("ChatId")
.HasColumnType("bigint");
b.Property<string>("OptionId")
.HasColumnType("varchar(255)");
b.Property<byte[]>("Value")
.IsRequired()
.HasColumnType("longblob");
b.HasKey("ChatId", "OptionId");
b.ToTable("ChatOptionValues");
});
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b =>
{
b.HasOne("Kruzya.TelegramBot.Core.Data.BotUser", "BotUser")
.WithMany()
.HasForeignKey("BotUserId");
b.Navigation("BotUser");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Kruzya.TelegramBot.Core.Migrations
{
/// <inheritdoc />
public partial class AddChatOptions : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ChatOptionValues",
columns: table => new
{
ChatId = table.Column<long>(type: "bigint", nullable: false),
OptionId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Value = table.Column<byte[]>(type: "longblob", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChatOptionValues", x => new { x.ChatId, x.OptionId });
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ChatOptionValues");
migrationBuilder.AlterColumn<string>(
name: "ValueType",
table: "UserValues",
type: "longtext CHARACTER SET utf8mb4",
nullable: true,
oldClrType: typeof(string),
oldType: "longtext",
oldNullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "UserValues",
type: "longtext CHARACTER SET utf8mb4",
nullable: true,
oldClrType: typeof(string),
oldType: "longtext",
oldNullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
}
}
+24 -3
View File
@@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Kruzya.TelegramBot.Core.Migrations namespace Kruzya.TelegramBot.Core.Migrations
{ {
[DbContext(typeof(CoreContext))] [DbContext(typeof(CoreContext))]
@@ -14,7 +16,7 @@ namespace Kruzya.TelegramBot.Core.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.1.2") .HasAnnotation("ProductVersion", "7.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 64); .HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUser", b => modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUser", b =>
@@ -44,13 +46,13 @@ namespace Kruzya.TelegramBot.Core.Migrations
.HasColumnType("char(36)"); .HasColumnType("char(36)");
b.Property<string>("Name") b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4"); .HasColumnType("longtext");
b.Property<byte[]>("Value") b.Property<byte[]>("Value")
.HasColumnType("longblob"); .HasColumnType("longblob");
b.Property<string>("ValueType") b.Property<string>("ValueType")
.HasColumnType("longtext CHARACTER SET utf8mb4"); .HasColumnType("longtext");
b.HasKey("BotUserValueId"); b.HasKey("BotUserValueId");
@@ -59,11 +61,30 @@ namespace Kruzya.TelegramBot.Core.Migrations
b.ToTable("UserValues"); b.ToTable("UserValues");
}); });
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.ChatOptionValue", b =>
{
b.Property<long>("ChatId")
.HasColumnType("bigint");
b.Property<string>("OptionId")
.HasColumnType("varchar(255)");
b.Property<byte[]>("Value")
.IsRequired()
.HasColumnType("longblob");
b.HasKey("ChatId", "OptionId");
b.ToTable("ChatOptionValues");
});
modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b => modelBuilder.Entity("Kruzya.TelegramBot.Core.Data.BotUserValue", b =>
{ {
b.HasOne("Kruzya.TelegramBot.Core.Data.BotUser", "BotUser") b.HasOne("Kruzya.TelegramBot.Core.Data.BotUser", "BotUser")
.WithMany() .WithMany()
.HasForeignKey("BotUserId"); .HasForeignKey("BotUserId");
b.Navigation("BotUser");
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
+31
View File
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Kruzya.TelegramBot.Core.Options;
public abstract class AbstractOption<TOption, TValue> : IOption<TValue>
where TOption: AbstractOption<TOption, TValue>
{
public abstract string OptionId { get; }
public abstract string OptionName { get; }
public abstract OptionType OptionType { get; }
public abstract TValue DefaultValue { get; }
public List<SelectChoice> ChoiceList { get; } = new();
private readonly IOptionProvider _optionProvider;
protected AbstractOption(IOptionProvider optionProvider)
{
_optionProvider = optionProvider;
}
public async Task<TValue> GetValueAsync(long chatId)
{
return await _optionProvider.GetValueAsync(chatId, OptionId, DefaultValue);
}
public async Task SetValueAsync(long chatId, TValue value)
{
await _optionProvider.SetValueAsync(chatId, OptionId, value);
}
}
+27
View File
@@ -0,0 +1,27 @@
using System.Threading.Tasks;
using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions;
namespace Kruzya.TelegramBot.Core.Options;
public class DbOptionProvider : IOptionProvider
{
private readonly CoreContext _db;
public DbOptionProvider(CoreContext db)
{
_db = db;
}
public async Task<TValue> GetValueAsync<TValue>(long chatId, string optionId, TValue defVal)
{
return (await _db.ChatOptionValues.FindOrCreateOption(chatId, optionId)).GetValue(defVal);
}
public async Task SetValueAsync<TValue>(long chatId, string optionId, TValue value)
{
var chatOption = await _db.ChatOptionValues.FindOrCreateOption(chatId, optionId);
chatOption.SetValue(value);
_db.AddOrUpdate(chatOption);
}
}
+20
View File
@@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Kruzya.TelegramBot.Core.Options;
public interface IOption
{
public string OptionId { get; }
public string OptionName { get; }
public OptionType OptionType { get; }
public List<SelectChoice> ChoiceList { get; }
}
public interface IOption<TValue> : IOption
{
public TValue DefaultValue { get; }
public Task<TValue> GetValueAsync(long chatId);
public Task SetValueAsync(long chatId, TValue value);
}
+9
View File
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace Kruzya.TelegramBot.Core.Options;
public interface IOptionProvider
{
public Task<TValue> GetValueAsync<TValue>(long chatId, string optionId, TValue defVal);
public Task SetValueAsync<TValue>(long chatId, string optionId, TValue value);
}
+7
View File
@@ -0,0 +1,7 @@
namespace Kruzya.TelegramBot.Core.Options;
public enum OptionType
{
OnOff,
Select
}
+29
View File
@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Telegram.Bot.Types.ReplyMarkups;
namespace Kruzya.TelegramBot.Core.Options;
public class SelectChoice
{
public string Id { get; }
public string FullName { get; }
public string ShortName { get; }
public SelectChoice(string id, string fullName, string shortName)
{
Id = id;
FullName = fullName;
ShortName = shortName;
}
public IEnumerable<InlineKeyboardButton> ToButton(long userId, string optionId, bool isActive = false)
{
return new[]
{
new InlineKeyboardButton((isActive ? "🟢 " : "🔴 ") + FullName)
{
CallbackData = $"option|{userId}|select|{optionId}|{Id}"
}
};
}
}
+3
View File
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using Kruzya.TelegramBot.Core.Options;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace Kruzya.TelegramBot.Core namespace Kruzya.TelegramBot.Core
@@ -53,6 +54,8 @@ namespace Kruzya.TelegramBot.Core
services.AddSingleton<ConcurrentBag<DeleteRequest>>(); services.AddSingleton<ConcurrentBag<DeleteRequest>>();
services.AddHostedService<DeleteService>(); services.AddHostedService<DeleteService>();
services.AddScoped<IOptionProvider, DbOptionProvider>();
// Trigger module handlers. // Trigger module handlers.
_core.Modules.ConfigureServices(services); _core.Modules.ConfigureServices(services);
} }
+1 -1
View File
@@ -4,9 +4,9 @@ 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 Kruzya.TelegramBot.Core.Handler;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
@@ -1,5 +1,5 @@
using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Handler;
namespace West.TelegramBot.ChatManagement.Handler; namespace West.TelegramBot.ChatManagement.Handler;
+1 -1
View File
@@ -1,9 +1,9 @@
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 Kruzya.TelegramBot.Core.Handler;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Exceptions; using Telegram.Bot.Exceptions;
using West.TelegramBot.ChatManagement.Service; using West.TelegramBot.ChatManagement.Service;
+1 -1
View File
@@ -3,8 +3,8 @@
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.Handler;
using West.TelegramBot.ChatManagement.Service; using West.TelegramBot.ChatManagement.Service;
namespace West.TelegramBot.ChatManagement.Handler; namespace West.TelegramBot.ChatManagement.Handler;
+1 -1
View File
@@ -3,9 +3,9 @@ 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 Kruzya.TelegramBot.Core.Handler;
using Kruzya.TelegramBot.Core.Service; using Kruzya.TelegramBot.Core.Service;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Telegram.Bot; using Telegram.Bot;
+5
View File
@@ -1,5 +1,8 @@
using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core;
using Kruzya.TelegramBot.Core.Extensions;
using Kruzya.TelegramBot.Core.Options;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using West.TelegramBot.ContentStore.Option;
using West.TelegramBot.ContentStore.Service; using West.TelegramBot.ContentStore.Service;
namespace West.TelegramBot.ContentStore; namespace West.TelegramBot.ContentStore;
@@ -22,6 +25,8 @@ public class ContentStore : Module
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddOption<AllowNsfw>();
foreach (var serviceType in MediaServiceTypes) foreach (var serviceType in MediaServiceTypes)
{ {
services.AddScoped(typeof(IRandomMediaService), serviceType); services.AddScoped(typeof(IRandomMediaService), serviceType);
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
+5 -10
View File
@@ -1,26 +1,26 @@
#nullable enable #nullable enable
using BotFramework;
using BotFramework.Attributes; using BotFramework.Attributes;
using BotFramework.Enums; using BotFramework.Enums;
using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.Core.Extensions;
using Kruzya.TelegramBot.Core.Handler;
using Kruzya.TelegramBot.Core.Service; using Kruzya.TelegramBot.Core.Service;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups; using Telegram.Bot.Types.ReplyMarkups;
using West.TelegramBot.ContentStore.Service; using West.TelegramBot.ContentStore.Service;
namespace West.TelegramBot.ContentStore.Handler; namespace West.TelegramBot.ContentStore.Handler;
public class Store : BotEventHandler public class Store : CommonHandler
{ {
private readonly IReputation? _reputation; private readonly IReputation? _reputation;
private readonly IServiceProvider _provider; private readonly IServiceProvider _provider;
private readonly ContentStore _store; private readonly ContentStore _store;
public Store(IServiceProvider provider, ContentStore store) public Store(CoreContext db, IServiceProvider provider, ContentStore store) : base(db)
{ {
_reputation = provider.GetService<IReputation>(); _reputation = provider.GetService<IReputation>();
_provider = provider; _provider = provider;
@@ -110,7 +110,7 @@ public class Store : BotEventHandler
{ {
await PerformPurchase(mediaService, messageId); await PerformPurchase(mediaService, messageId);
} }
catch (Exception e) catch (Exception)
{ {
answer = "Товар недоступен"; answer = "Товар недоступен";
await Bot.EditMessageTextAsync(Chat.Id, await Bot.EditMessageTextAsync(Chat.Id,
@@ -164,9 +164,4 @@ public class Store : BotEventHandler
return mediaService != null; return mediaService != null;
} }
private async Task AnswerQuery(string id, string? message = null)
{
await Bot.AnswerCallbackQueryAsync(id, message);
}
} }
+15
View File
@@ -0,0 +1,15 @@
using Kruzya.TelegramBot.Core.Options;
namespace West.TelegramBot.ContentStore.Option;
public class AllowNsfw : AbstractOption<AllowNsfw, bool>
{
public override string OptionId => "allowNSFW";
public override string OptionName => "NSFW";
public override OptionType OptionType => OptionType.OnOff;
public override bool DefaultValue => false;
public AllowNsfw(IOptionProvider optionProvider) : base(optionProvider)
{
}
}
@@ -1,25 +1,23 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using BotFramework.Utils; using BotFramework.Utils;
using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using West.TelegramBot.ContentStore.Model; using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Service;
public abstract class AbstractKittiesService : IRandomMediaService public abstract class AbstractKittiesService : IRandomMediaService
{ {
private readonly CoreContext _db; private readonly AllowNsfw _allowNsfw;
protected abstract string VideoType { get; } protected abstract string VideoType { get; }
public abstract StoreItem StoreItem { get; } public abstract StoreItem StoreItem { get; }
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
protected AbstractKittiesService(IConfiguration configuration, CoreContext db) protected AbstractKittiesService(IConfiguration configuration, AllowNsfw allowNsfw)
{ {
_db = db;
var section = configuration.GetSection("KittiesService"); var section = configuration.GetSection("KittiesService");
var uri = section.GetValue<Uri>("Uri"); var uri = section.GetValue<Uri>("Uri");
@@ -33,6 +31,7 @@ public abstract class AbstractKittiesService : IRandomMediaService
Authorization = new AuthenticationHeaderValue("Bearer", token) Authorization = new AuthenticationHeaderValue("Bearer", token)
} }
}; };
_allowNsfw = allowNsfw;
} }
public async Task<RandomMedia> GetRandomMediaAsync() public async Task<RandomMedia> GetRandomMediaAsync()
@@ -52,8 +51,6 @@ public abstract class AbstractKittiesService : IRandomMediaService
public async Task<bool> CanView(Chat chat, User user) public async Task<bool> CanView(Chat chat, User user)
{ {
var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW"); return await _allowNsfw.GetValueAsync(chat.Id);
return option.GetValue(false);
} }
} }
@@ -0,0 +1,19 @@
using Telegram.Bot.Types;
using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service;
public abstract class AbstractNsfwService : IRandomMediaService
{
private readonly AllowNsfw _allowNsfw;
public abstract StoreItem StoreItem { get; }
public abstract Task<RandomMedia> GetRandomMediaAsync();
protected AbstractNsfwService(AllowNsfw allowNsfw)
{
_allowNsfw = allowNsfw;
}
public async Task<bool> CanView(Chat chat, User user) => await _allowNsfw.GetValueAsync(chat.Id);
}
@@ -1,27 +1,24 @@
using Kruzya.TelegramBot.Core.Data; using Telegram.Bot.Types;
using Kruzya.TelegramBot.Core.Extensions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using West.TelegramBot.ContentStore.Model; using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Service;
public class AnimBoobsService : IRandomMediaService public class AnimBoobsService : AbstractNsfwService
{ {
private readonly CoreContext _db;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public StoreItem StoreItem { get; } = new("anim_boobs", "Сиськи движущиеся", 120, 40); public override StoreItem StoreItem { get; } = new("anim_boobs", "Сиськи движущиеся", 120, 40);
public AnimBoobsService(CoreContext db) public AnimBoobsService(AllowNsfw allowNsfw) : base(allowNsfw)
{ {
_db = db; _httpClient = new HttpClient
_httpClient = new HttpClient()
{ {
BaseAddress = new Uri("https://westdev.me/_boobs/") BaseAddress = new Uri("https://westdev.me/_boobs/")
}; };
} }
public async Task<RandomMedia> GetRandomMediaAsync() public override async Task<RandomMedia> GetRandomMediaAsync()
{ {
var fileName = await _httpClient.GetStringAsync("index.php"); var fileName = await _httpClient.GetStringAsync("index.php");
// https://bugs.telegram.org/c/23674 workaround // https://bugs.telegram.org/c/23674 workaround
@@ -33,11 +30,4 @@ public class AnimBoobsService : IRandomMediaService
Nsfw = true Nsfw = true
}; };
} }
public async Task<bool> CanView(Chat chat, User user)
{
var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW");
return option.GetValue(false);
}
} }
@@ -6,8 +6,8 @@ namespace West.TelegramBot.ContentStore.Service;
public class CapyApiService : IRandomMediaService public class CapyApiService : IRandomMediaService
{ {
public StoreItem StoreItem { get; } = new("capybara", "Капибара", 60, 25); public StoreItem StoreItem { get; } = new("capybara", "Капибара", 60, 25);
public async Task<RandomMedia> GetRandomMediaAsync() public Task<RandomMedia> GetRandomMediaAsync()
{ {
return new RandomMedia(new InputFileUrl($"https://api.capy.lol/v1/capybara?{new Random().Next(0, 10000)}")); return Task.FromResult(new RandomMedia(new InputFileUrl($"https://api.capy.lol/v1/capybara?{new Random().Next(0, 10000)}")));
} }
} }
@@ -1,12 +1,13 @@
using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Data;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using West.TelegramBot.ContentStore.Model; using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Service;
public class GayKittiesService : AbstractKittiesService public class GayKittiesService : AbstractKittiesService
{ {
public GayKittiesService(IConfiguration configuration, CoreContext db) : base(configuration, db) public GayKittiesService(IConfiguration configuration, AllowNsfw allowNsfw) : base(configuration, allowNsfw)
{ {
} }
@@ -8,5 +8,5 @@ public interface IRandomMediaService
public StoreItem StoreItem { get; } public StoreItem StoreItem { get; }
public Task<RandomMedia> GetRandomMediaAsync(); public Task<RandomMedia> GetRandomMediaAsync();
public async Task<bool> CanView(Chat chat, User user) => true; public Task<bool> CanView(Chat chat, User user) => Task.FromResult(true);
} }
+5 -13
View File
@@ -3,25 +3,24 @@ using Kruzya.TelegramBot.Core.Extensions;
using Newtonsoft.Json; using Newtonsoft.Json;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using West.TelegramBot.ContentStore.Model; using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Service;
public class OBoobsService : IRandomMediaService public class OBoobsService : AbstractNsfwService
{ {
private readonly CoreContext _db;
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
public StoreItem StoreItem { get; } = new("boobs", "Сиськи", 60, 30); public override StoreItem StoreItem { get; } = new("boobs", "Сиськи", 60, 30);
public OBoobsService(CoreContext db) public OBoobsService(AllowNsfw allowNsfw) : base(allowNsfw)
{ {
_db = db;
_httpClient = new HttpClient _httpClient = new HttpClient
{ {
BaseAddress = new Uri("http://api.oboobs.ru") BaseAddress = new Uri("http://api.oboobs.ru")
}; };
} }
public async Task<RandomMedia> GetRandomMediaAsync() public override async Task<RandomMedia> GetRandomMediaAsync()
{ {
// "/boobs/{start=0; sql offset}/{count=1; sql limit}/{order=-id;[id,rank,-rank,interest,-interest,random]}/ // "/boobs/{start=0; sql offset}/{count=1; sql limit}/{order=-id;[id,rank,-rank,interest,-interest,random]}/
var httpResp = await _httpClient.GetAsync("boobs/1/1/random"); var httpResp = await _httpClient.GetAsync("boobs/1/1/random");
@@ -31,11 +30,4 @@ public class OBoobsService : IRandomMediaService
return new RandomMedia(new InputFileUrl($"https://media.oboobs.ru/boobs/{boobsId}.jpg"), boobsItem.Model, true); return new RandomMedia(new InputFileUrl($"https://media.oboobs.ru/boobs/{boobsId}.jpg"), boobsItem.Model, true);
} }
public async Task<bool> CanView(Chat chat, User user)
{
var option = await _db.UserValues.FindOrCreateOption(chat.Id, "AllowNSFW");
return option.GetValue(false);
}
} }
@@ -22,7 +22,7 @@ public abstract class SomeRandomApiAbstractService : IRandomMediaService
public async Task<RandomMedia> GetRandomMediaAsync() public async Task<RandomMedia> GetRandomMediaAsync()
{ {
var response = await _httpClient.GetStringAsync($"https://some-random-api.com/animal/{ApiId}"); var response = await _httpClient.GetStringAsync($"https://some-random-api.com/animal/{ApiId}");
var image = JsonConvert.DeserializeObject<SomeRandomApiAnimalResponse>(response); var image = JsonConvert.DeserializeObject<SomeRandomApiAnimalResponse>(response)!;
return new RandomMedia(new InputFileUrl(image.Image), image.Fact); return new RandomMedia(new InputFileUrl(image.Image), image.Fact);
} }
@@ -1,6 +1,7 @@
using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Data;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using West.TelegramBot.ContentStore.Model; using West.TelegramBot.ContentStore.Model;
using West.TelegramBot.ContentStore.Option;
namespace West.TelegramBot.ContentStore.Service; namespace West.TelegramBot.ContentStore.Service;
@@ -9,7 +10,7 @@ public class StraightKittiesService : AbstractKittiesService
protected override string VideoType => "straight"; protected override string VideoType => "straight";
public override StoreItem StoreItem { get; } = new("kitties_straight", "Прон", 40, 100); public override StoreItem StoreItem { get; } = new("kitties_straight", "Прон", 40, 100);
public StraightKittiesService(IConfiguration configuration, CoreContext db) : base(configuration, db) public StraightKittiesService(IConfiguration configuration, AllowNsfw allowNsfw) : base(configuration, allowNsfw)
{ {
} }
} }
+1
View File
@@ -3,6 +3,7 @@ global using GuessCache = Kruzya.TelegramBot.Core.Cache.MemoryCache<Telegram.Bot
using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core;
using Kruzya.TelegramBot.Core.Options;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
+1
View File
@@ -9,6 +9,7 @@ using Kruzya.TelegramBot.Core;
using Kruzya.TelegramBot.Core.AutoDelete; using Kruzya.TelegramBot.Core.AutoDelete;
using Kruzya.TelegramBot.Core.Data; using Kruzya.TelegramBot.Core.Data;
using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.Core.Extensions;
using Kruzya.TelegramBot.Core.Handler;
using Kruzya.TelegramBot.Core.Service; using Kruzya.TelegramBot.Core.Service;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types; using Telegram.Bot.Types;
+1
View File
@@ -15,6 +15,7 @@ using System.Collections.Concurrent;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Microsoft.AspNetCore.Components.Forms; using Microsoft.AspNetCore.Components.Forms;
using Kruzya.TelegramBot.Core.Handler;
namespace West.Entertainment.Handler; namespace West.Entertainment.Handler;
+1 -1
View File
@@ -3,9 +3,9 @@
using BotFramework.Attributes; using BotFramework.Attributes;
using BotFramework.Enums; using BotFramework.Enums;
using BotFramework.Utils; using BotFramework.Utils;
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 Kruzya.TelegramBot.Core.Handler;
using Kruzya.TelegramBot.Core.Service; using Kruzya.TelegramBot.Core.Service;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Telegram.Bot; using Telegram.Bot;
+21
View File
@@ -0,0 +1,21 @@
using Kruzya.TelegramBot.Core.Options;
namespace West.TelegramBot.Reputation.Options;
public class KarmaMode : AbstractOption<KarmaMode, string>
{
public override string OptionId => "karmaMode";
public override string OptionName => "Karma Mode";
public override OptionType OptionType => OptionType.Select;
public override string DefaultValue => "geometric";
public KarmaMode(IOptionProvider optionProvider) : base(optionProvider)
{
ChoiceList.AddRange(new []
{
new SelectChoice("geometric", "Geometric", "G"),
new SelectChoice("linear", "Linear", "L")
});
}
}
+3
View File
@@ -1,9 +1,11 @@
using Kruzya.TelegramBot.Core; using Kruzya.TelegramBot.Core;
using Kruzya.TelegramBot.Core.Extensions;
using Kruzya.TelegramBot.Core.Service; using Kruzya.TelegramBot.Core.Service;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using West.TelegramBot.Reputation.Data; using West.TelegramBot.Reputation.Data;
using West.TelegramBot.Reputation.Options;
using West.TelegramBot.Reputation.Service; using West.TelegramBot.Reputation.Service;
namespace West.TelegramBot.Reputation; namespace West.TelegramBot.Reputation;
@@ -19,6 +21,7 @@ public class Reputation : Module
var dsn = Configuration.GetConnectionString("Reputation"); var dsn = Configuration.GetConnectionString("Reputation");
services.AddDbContext<ReputationContext>(options => options.UseMySql(dsn, ServerVersion.AutoDetect(dsn))); services.AddDbContext<ReputationContext>(options => options.UseMySql(dsn, ServerVersion.AutoDetect(dsn)));
services.AddOption<KarmaMode>();
services.AddScoped<IReputation, ReputationService>(); services.AddScoped<IReputation, ReputationService>();
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
@@ -4,16 +4,19 @@ using Kruzya.TelegramBot.Core.Service;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using West.TelegramBot.Reputation.Data; using West.TelegramBot.Reputation.Data;
using West.TelegramBot.Reputation.Options;
namespace West.TelegramBot.Reputation.Service; namespace West.TelegramBot.Reputation.Service;
public class ReputationService : IReputation public class ReputationService : IReputation
{ {
private readonly ReputationContext _reputationContext; private readonly ReputationContext _reputationContext;
private readonly KarmaMode _karmaMode;
public ReputationService(ReputationContext reputationContext) public ReputationService(ReputationContext reputationContext, KarmaMode karmaMode)
{ {
_reputationContext = reputationContext; _reputationContext = reputationContext;
_karmaMode = karmaMode;
} }
public async Task<double> GetUserReputationAsync(Chat chat, User user) public async Task<double> GetUserReputationAsync(Chat chat, User user)
@@ -45,14 +48,25 @@ public class ReputationService : IReputation
public async Task<double> GetReputationDiffAsync(Chat chat, User sender, User receiver) public async Task<double> GetReputationDiffAsync(Chat chat, User sender, User receiver)
{ {
var senderRep = await FindUserReputation(chat, sender); var senderRep = await FindUserReputation(chat, sender);
if (senderRep == null) if (senderRep == null)
{ {
return 1; return 1;
} }
var karmaMode = await _karmaMode.GetValueAsync(chat.Id);
var senderValue = senderRep.Value; var senderValue = senderRep.Value;
return Math.Round(senderValue == 0 ? 1 : Math.Sqrt(senderValue), 2);
double diff;
if (senderValue == 0 || karmaMode == "linear")
{
diff = 1;
}
else
{
diff = Math.Round(Math.Sqrt(senderValue), 2);
}
return diff;
} }
public async Task<IEnumerable<IReputationEntity>> GetChatRatingAsync(Chat chat, int limit = 10, int offset = 0) public async Task<IEnumerable<IReputationEntity>> GetChatRatingAsync(Chat chat, int limit = 10, int offset = 0)