mirror of
https://github.com/Bubuni-Team/telegram-bot.git
synced 2026-07-31 00:29:24 +03:00
Initial KV API concept
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Threading.Tasks;
|
|||||||
using Kruzya.TelegramBot.Core.Data;
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Kruzya.TelegramBot.Core.Extensions
|
namespace Kruzya.TelegramBot.Core.Extensions
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,9 @@ namespace Kruzya.TelegramBot.Core.Extensions
|
|||||||
public static async Task<BotUserValue> FindOrCreateOption(this DbSet<BotUserValue> repository, long chatId,
|
public static async Task<BotUserValue> FindOrCreateOption(this DbSet<BotUserValue> repository, long chatId,
|
||||||
long userId, string option)
|
long userId, string option)
|
||||||
{
|
{
|
||||||
|
repository.GetService<ILogger<Core>>().LogCritical("FindOrCreateOption() is deprecated. " +
|
||||||
|
"Please, use the new API OptionManagerService.");
|
||||||
|
|
||||||
var opt = await repository.FindOption(chatId, userId, option);
|
var opt = await repository.FindOption(chatId, userId, option);
|
||||||
if (opt == null)
|
if (opt == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kruzya.TelegramBot.Core.Data;
|
||||||
|
using Kruzya.TelegramBot.Core.Extensions;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
public class CoreContextStorageService : IOptionStorageAdapter
|
||||||
|
{
|
||||||
|
private CoreContext _db;
|
||||||
|
public CoreContextStorageService(CoreContext db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<byte[]?> LoadValueAsync(IOptionKey key)
|
||||||
|
{
|
||||||
|
var entity = await BuildQueryForKey(key).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
return entity?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveValueAsync(IOptionKey key, byte[] value)
|
||||||
|
{
|
||||||
|
var entity = await BuildQueryForKey(key).FirstOrDefaultAsync();
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
entity = await CreateEntity(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.Value = value;
|
||||||
|
_db.AddOrUpdate(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<BotUserValue> BuildQueryForKey(IOptionKey key)
|
||||||
|
{
|
||||||
|
var legacyUserId = key.UserId ?? 0;
|
||||||
|
|
||||||
|
return _db.UserValues.Where(e => e.BotUser.ChatId == key.ChatId && e.BotUser.UserId == legacyUserId
|
||||||
|
&& e.Name == key.Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<BotUserValue> CreateEntity(IOptionKey key)
|
||||||
|
{
|
||||||
|
var entity = _db.UserValues.Create()!;
|
||||||
|
entity.BotUser = await _db.Users.FindOrCreate(key.ChatId, key.UserId ?? 0);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows interact with option related with some chat.
|
||||||
|
/// </summary>
|
||||||
|
public interface IOptionItem<T>
|
||||||
|
{
|
||||||
|
public IOptionKey Key { get; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
public bool IsChanged { get; }
|
||||||
|
|
||||||
|
public Task LoadAsync(IOptionStorageAdapter adapter);
|
||||||
|
public Task SaveAsync(IOptionStorageAdapter adapter);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Explains what
|
||||||
|
/// </summary>
|
||||||
|
public interface IOptionKey
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The chat identifier where this key is related.
|
||||||
|
/// </summary>
|
||||||
|
public long ChatId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Telegram user identifier. May be NULL if this is a simple option for all chat members.
|
||||||
|
/// </summary>
|
||||||
|
public long? UserId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The unique option identifier.
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describes what API should be implemented in Option Manager Service.
|
||||||
|
/// </summary>
|
||||||
|
public interface IOptionManagerService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements an interaction with storage for options.
|
||||||
|
/// </summary>
|
||||||
|
public interface IOptionStorageAdapter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a saved value (if exists) from storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task<byte[]?> LoadValueAsync(IOptionKey key);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a passed value to storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The unique chat key</param>
|
||||||
|
/// <param name="value">The serialized value for saving</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Task SaveValueAsync(IOptionKey key, byte[] value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
public class OptionItem<T> : IOptionItem<T>
|
||||||
|
{
|
||||||
|
public IOptionKey Key { get; }
|
||||||
|
|
||||||
|
public T Value
|
||||||
|
{
|
||||||
|
get => _value;
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_isChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsChanged => _isChanged;
|
||||||
|
|
||||||
|
private T _value;
|
||||||
|
private bool _isChanged;
|
||||||
|
|
||||||
|
public OptionItem(IOptionKey key, T value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadAsync(IOptionStorageAdapter adapter)
|
||||||
|
{
|
||||||
|
var savedValue = await adapter.LoadValueAsync(Key);
|
||||||
|
if (savedValue == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This property changed when we're loaded default value. Reset here.
|
||||||
|
_isChanged = false;
|
||||||
|
_value = JsonSerializer.Deserialize<T>(new ReadOnlySpan<byte>(savedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveAsync(IOptionStorageAdapter adapter)
|
||||||
|
{
|
||||||
|
var data = JsonSerializer.SerializeToUtf8Bytes(Value, new JsonSerializerOptions { WriteIndented = false,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull });
|
||||||
|
await adapter.SaveValueAsync(Key, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
|
namespace Kruzya.TelegramBot.Core.Option;
|
||||||
|
|
||||||
|
public class OptionKey : IOptionKey
|
||||||
|
{
|
||||||
|
public long ChatId { get; }
|
||||||
|
public long? UserId { get; } = null;
|
||||||
|
public string Identifier { get; }
|
||||||
|
|
||||||
|
public OptionKey(Chat chat, User user, string identifier) : this(chat.Id, user.Id, identifier)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public OptionKey(Chat chat, string identifier) : this(chat.Id, null, identifier)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public OptionKey(long chat, string identifier) : this(chat, null, identifier)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public OptionKey(long chat, long? user, string identifier)
|
||||||
|
{
|
||||||
|
ChatId = chat;
|
||||||
|
UserId = user;
|
||||||
|
Identifier = identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user