diff --git a/.github/workflows/package.navigator.yml b/.github/workflows/package.navigator.yml
new file mode 100644
index 0000000..96b549e
--- /dev/null
+++ b/.github/workflows/package.navigator.yml
@@ -0,0 +1,80 @@
+name: Build & Publish - Navigator
+on:
+ push:
+ pull_request:
+ release:
+ types:
+ - published
+env:
+ # Stop wasting time caching packages
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
+ # Disable sending usage data to Microsoft
+ DOTNET_CLI_TELEMETRY_OPTOUT: true
+ # Project name to pack and publish
+ PROJECT_NAME: Navigator
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ ubuntu-latest ]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '6.0.x'
+ - name: Restore
+ working-directory: ./src
+ run: dotnet restore
+ - name: Build
+ working-directory: ./src
+ run: dotnet build -c Release --no-restore
+ - name: Test
+ working-directory: ./src
+ run: dotnet test -c Release
+ - name: Pack
+ if: matrix.os == 'ubuntu-latest'
+ run: dotnet pack -v normal -c Release --no-restore --include-symbols --include-source -p:PackageVersion=$GITHUB_RUN_ID src/$PROJECT_NAME.sln
+ # - name: Upload Artifact
+ # if: matrix.os == 'ubuntu-latest'
+ # uses: actions/upload-artifact@v2
+ # with:
+ # name: nupkg
+ # path: ./src/${{ env.PROJECT_NAME }}/bin/Release/*.nupkg
+ publish:
+ needs: build
+ if: github.event_name == 'release'
+ env:
+ # GitHub Packages Feed settings
+ GITHUB_FEED: https://nuget.pkg.github.com/navigatorframework/
+ GITHUB_USER: navigatorframeworkbot
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # Official NuGet Feed settings
+ NUGET_FEED: https://api.nuget.org/v3/index.json
+ NUGET_KEY: ${{ secrets.NUGET_KEY }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: '6.0.x'
+ - name: Create Release NuGet package
+ run: |
+ arrTag=(${GITHUB_REF//\// })
+ VERSION="${arrTag[2]}"
+ echo Version: $VERSION
+ VERSION="${VERSION//v}"
+ echo Clean Version: $VERSION
+ dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=$VERSION -o nupkg src/$PROJECT_NAME.sln
+ - name: Push to GitHub Feed
+ run: |
+ for f in ./nupkg/*.nupkg
+ do
+ curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED
+ done
+ - name: Push to NuGet Feed
+ run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY
diff --git a/src/Navigator.Abstractions.Extensions/INavigatorContextExtensions.cs b/src/Navigator.Abstractions.Extensions/INavigatorContextExtensions.cs
deleted file mode 100644
index 9d1c902..0000000
--- a/src/Navigator.Abstractions.Extensions/INavigatorContextExtensions.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using System;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.Enums;
-using Telegram.Bot.Types.Payments;
-
-namespace Navigator.Abstractions.Extensions
-{
- ///
- /// Useful extensions for Navigator Context.
- ///
- public static class INavigatorContextExtensions
- {
- #region User
-
- ///
- /// Get a Telegram user.
- ///
- ///
- /// When user is not found.
- ///
- public static User GetTelegramUser(this INavigatorContext ctx)
- {
- var user = ctx.GetTelegramUserOrDefault();
-
- return user ?? throw new Exception("User not found in update.");
- }
-
- ///
- /// Get a Telegram user or default if not found.
- ///
- ///
- ///
- public static User? GetTelegramUserOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type switch
- {
- UpdateType.Message => ctx.Update.Message.From,
- UpdateType.InlineQuery => ctx.Update.InlineQuery.From,
- UpdateType.ChosenInlineResult => ctx.Update.ChosenInlineResult.From,
- UpdateType.CallbackQuery => ctx.Update.CallbackQuery.From,
- UpdateType.EditedMessage => ctx.Update.EditedMessage.From,
- UpdateType.ChannelPost => ctx.Update.ChannelPost.From,
- UpdateType.EditedChannelPost => ctx.Update.EditedChannelPost.From,
- UpdateType.ShippingQuery => ctx.Update.ShippingQuery.From,
- UpdateType.PreCheckoutQuery => ctx.Update.PreCheckoutQuery.From,
- _ => default
- };
- }
-
- #endregion
-
- #region Chat
-
- ///
- /// Get a Telegram chat.
- ///
- ///
- /// When chat is not found.
- ///
- public static Chat GetTelegramChat(this INavigatorContext ctx)
- {
- var chat = ctx.GetTelegramChatOrDefault();
-
- return chat ?? throw new Exception("Chat not found in update.");
- }
-
- ///
- /// Get a Telegram chat or default if not found.
- ///
- ///
- /// Telegram Chat
- public static Chat? GetTelegramChatOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type switch
- {
- UpdateType.CallbackQuery => ctx.Update.CallbackQuery.Message.Chat,
- UpdateType.Message => ctx.Update.Message.Chat,
- UpdateType.EditedMessage => ctx.Update.EditedMessage.Chat,
- UpdateType.ChannelPost => ctx.Update.ChannelPost.Chat,
- UpdateType.EditedChannelPost => ctx.Update.EditedChannelPost.Chat,
- _ => default
- };
- }
-
- #endregion
-
- #region Message
-
- ///
- /// Get a Telegram message.
- ///
- ///
- /// When message is not found.
- ///
- public static Message GetMessage(this INavigatorContext ctx)
- {
- return ctx.GetMessageOrDefault() ?? throw new Exception("Message not found in update.");
- }
-
- ///
- /// Get a Telegram message or default if not found.
- ///
- ///
- ///
- public static Message? GetMessageOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.Message ? ctx.Update.Message : default;
- }
-
- #endregion
-
- #region InlineQuery
-
- ///
- /// Get a Telegram inline query
- ///
- ///
- /// When inline query is not found.
- ///
- public static InlineQuery GetInlineQuery(this INavigatorContext ctx)
- {
- return ctx.GetInlineQueryOrDefault() ?? throw new Exception("InlineQuery not found in update.");
- }
-
- ///
- /// Get a Telegram inline query or default if not found.
- ///
- ///
- ///
- public static InlineQuery? GetInlineQueryOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.InlineQuery ? ctx.Update.InlineQuery : default;
- }
-
- #endregion
-
- #region ChosenInlineResult
-
- public static ChosenInlineResult GetChosenInlineResult(this INavigatorContext ctx)
- {
- return ctx.GetChosenInlineResultOrDefault() ?? throw new Exception("ChosenInlineResult not found in update.");
- }
-
- public static ChosenInlineResult? GetChosenInlineResultOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.ChosenInlineResult ? ctx.Update.ChosenInlineResult : default;
- }
-
- #endregion
-
- #region CallbackQuery
-
- public static CallbackQuery GetCallbackQuery(this INavigatorContext ctx)
- {
- return ctx.GetCallbackQueryOrDefault() ?? throw new Exception("CallbackQuery not found in update.");
- }
-
- public static CallbackQuery? GetCallbackQueryOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.CallbackQuery ? ctx.Update.CallbackQuery : default;
- }
-
- #endregion
-
- #region EditedMessage
-
- public static Message GetEditedMessage(this INavigatorContext ctx)
- {
- return ctx.GetEditedMessageOrDefault() ?? throw new Exception("EditedMessage not found in update.");
- }
-
- public static Message? GetEditedMessageOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.EditedMessage ? ctx.Update.EditedMessage : default;
- }
-
- #endregion
-
- #region ChannelPost
-
- public static Message GetChannelPost(this INavigatorContext ctx)
- {
- return ctx.GetChannelPostOrDefault() ?? throw new Exception("ChannelPost not found in update.");
- }
-
- public static Message? GetChannelPostOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.ChannelPost ? ctx.Update.ChannelPost : default;
- }
-
- #endregion
-
- #region EditedChannelPost
-
- public static Message GetEditedChannelPost(this INavigatorContext ctx)
- {
- return ctx.GetEditedChannelPostOrDefault() ?? throw new Exception("EditedChannelPost not found in update.");
- }
-
- public static Message? GetEditedChannelPostOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.EditedChannelPost ? ctx.Update.EditedChannelPost : default;
- }
-
- #endregion
-
- #region ShippingQuery
-
- public static ShippingQuery GetShippingQuery(this INavigatorContext ctx)
- {
- return ctx.GetShippingQueryOrDefault() ?? throw new Exception("ShippingQuery not found in update.");
- }
-
- public static ShippingQuery? GetShippingQueryOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.ShippingQuery ? ctx.Update.ShippingQuery : default;
- }
-
- #endregion
-
- #region PreCheckoutQuery
-
- public static PreCheckoutQuery GetPreCheckoutQuery(this INavigatorContext ctx)
- {
- return ctx.GetPreCheckoutQueryOrDefault() ?? throw new Exception("PreCheckoutQuery not found in update.");
- }
-
- public static PreCheckoutQuery? GetPreCheckoutQueryOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.PreCheckoutQuery ? ctx.Update.PreCheckoutQuery : default;
- }
-
- #endregion
-
- #region Poll
-
- public static Poll GetPoll(this INavigatorContext ctx)
- {
- return ctx.GetPollOrDefault() ?? throw new Exception("Poll not found in update.");
- }
-
- public static Poll? GetPollOrDefault(this INavigatorContext ctx)
- {
- return ctx.Update.Type == UpdateType.Poll ? ctx.Update.Poll : default;
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions.Extensions/Navigator.Abstractions.Extensions.csproj b/src/Navigator.Abstractions.Extensions/Navigator.Abstractions.Extensions.csproj
deleted file mode 100644
index 4c53843..0000000
--- a/src/Navigator.Abstractions.Extensions/Navigator.Abstractions.Extensions.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net5.0
- enable
- true
- true
- 0.9.99-beta
- Navigator.Abstractions.Extensions
- Navigator Framework Abstractions Extensions
- Lucas Maximiliano Marino
- A highly opinionated telegram bot framework, mainly based on Telegram.Bot.
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator
- true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Abstractions/IAction.cs b/src/Navigator.Abstractions/IAction.cs
deleted file mode 100644
index 52ea586..0000000
--- a/src/Navigator.Abstractions/IAction.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using MediatR;
-
-namespace Navigator.Abstractions
-{
- ///
- /// Base action.
- ///
- public interface IAction : IRequest
- {
- ///
- /// Order at which the action must be evaluated, default is 1000.
- ///
- int Order { get; }
- ///
- /// Defines the type of action it can handle. See
- ///
- string Type { get; }
-
- ///
- /// Timestamp at which the action was launched, in UTC.
- ///
- DateTime Timestamp { get; }
-
- ///
- /// Can be used to populate the action before triggering "CanHandle".
- ///
- ///
- ///
- IAction Init(INavigatorContext ctx);
-
- ///
- /// This function must return true when the incoming update can be handled by this action.
- ///
- ///
- ///
- bool CanHandle(INavigatorContext ctx);
- }
-
- public static class ActionType
- {
- public static readonly string Command = "_navigator_command";
- public static readonly string Message = "_navigator_message";
- public static readonly string CallbackQuery = "_navigator_callbackQuery";
- public static readonly string InlineQuery = "_navigator_inlineQuery";
- public static readonly string InlineResultChosen = "_navigator_inlineResultChosen";
- public static readonly string Poll = "_navigator_poll";
- public static readonly string EditedMessage = "_navigator_editedMessage";
- public static readonly string ChannelPost = "_navigator_channelPost";
- public static readonly string EditedChannelPost = "_navigator_editedChannelPost";
- public static readonly string ShippingQuery = "_navigator_shippingQuery";
- public static readonly string PreCheckoutQuery = "_navigator_preCheckoutQuery";
- public static readonly string ChatMembersAdded = "_navigator_message_chatMembersAdded";
- public static readonly string ChatMemberLeft = "_navigator_message_ChatMemberLeft";
- public static readonly string ChatTitleChanged = "_navigator_message_chatTitleChanged";
- public static readonly string ChatPhotoChanged = "_navigator_message_chatPhotoChanged";
- public static readonly string MessagePinned = "_navigator_message_messagePinned";
- public static readonly string ChatPhotoDeleted = "_navigator_message_chatPhotoDeleted";
- public static readonly string GroupCreated = "_navigator_message_groupCreated";
- public static readonly string SupergroupCreated = "_navigator_message_supergroupCreated";
- public static readonly string ChannelCreated = "_navigator_message_channelCreated";
- public static readonly string MigratedToSupergroup = "_navigator_message_migratedToSupergroup";
- public static readonly string MigratedFromGroup = "_navigator_message_migratedFromGroup";
- public static readonly string Unknown = "_navigator_unknown";
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/IActionLauncher.cs b/src/Navigator.Abstractions/IActionLauncher.cs
deleted file mode 100644
index 0d141ba..0000000
--- a/src/Navigator.Abstractions/IActionLauncher.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Telegram.Bot.Types;
-
-namespace Navigator.Abstractions
-{
- public interface IActionLauncher
- {
- Task Launch();
- IEnumerable GetActions(Update update);
- public string? GetActionType(Update update);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/IBotClient.cs b/src/Navigator.Abstractions/IBotClient.cs
deleted file mode 100644
index bdd0817..0000000
--- a/src/Navigator.Abstractions/IBotClient.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Telegram.Bot;
-
-namespace Navigator.Abstractions
-{
- public interface IBotClient : ITelegramBotClient
- {
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorContext.cs b/src/Navigator.Abstractions/INavigatorContext.cs
deleted file mode 100644
index 44f1898..0000000
--- a/src/Navigator.Abstractions/INavigatorContext.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Telegram.Bot.Types;
-
-namespace Navigator.Abstractions
-{
- public interface INavigatorContext
- {
- Dictionary Items { get; }
- public IBotClient Client { get; }
- public User BotProfile { get; }
- public Update Update { get; }
-
- Task Init(Update update, Dictionary extensions);
- TExtension? Get(string extensionKey, bool throwIfNotFound = false);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorContextBuilder.cs b/src/Navigator.Abstractions/INavigatorContextBuilder.cs
deleted file mode 100644
index 6bf1369..0000000
--- a/src/Navigator.Abstractions/INavigatorContextBuilder.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Threading.Tasks;
-using Telegram.Bot.Types;
-
-namespace Navigator.Abstractions
-{
- public interface INavigatorContextBuilder
- {
- Task Build(Update update);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorContextExtensionProvider.cs b/src/Navigator.Abstractions/INavigatorContextExtensionProvider.cs
deleted file mode 100644
index 78d09d9..0000000
--- a/src/Navigator.Abstractions/INavigatorContextExtensionProvider.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Threading.Tasks;
-using Telegram.Bot.Types;
-
-namespace Navigator.Abstractions
-{
- public interface INavigatorContextExtensionProvider
- {
- int Order => 1000;
- Task<(string?, object?)> Process(Update update);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorMiddleware.cs b/src/Navigator.Abstractions/INavigatorMiddleware.cs
deleted file mode 100644
index 0920ae8..0000000
--- a/src/Navigator.Abstractions/INavigatorMiddleware.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
-
-namespace Navigator.Abstractions
-{
- ///
- /// Middleware for handling telegram updates incoming as webhooks.
- ///
- public interface INavigatorMiddleware
- {
- ///
- /// Handles an incoming update from telegram.
- ///
- ///
- ///
- Task Handle(HttpRequest httpRequest);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorNotification.cs b/src/Navigator.Abstractions/INavigatorNotification.cs
deleted file mode 100644
index 0571d65..0000000
--- a/src/Navigator.Abstractions/INavigatorNotification.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-using MediatR;
-
-namespace Navigator.Abstractions
-{
- public interface INavigatorNotification : INotification
- {
- DateTime Timestamp { get; }
- int UpdateId { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INavigatorOptions.cs b/src/Navigator.Abstractions/INavigatorOptions.cs
deleted file mode 100644
index 12da1af..0000000
--- a/src/Navigator.Abstractions/INavigatorOptions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Navigator.Abstractions
-{
- ///
- ///
- ///
- public interface INavigatorOptions
- {
- bool TryRegisterOption(string key, object option);
- bool ForceRegisterOption(string key, object option);
- TType? RetrieveOption(string key);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/INotificationLauncher.cs b/src/Navigator.Abstractions/INotificationLauncher.cs
deleted file mode 100644
index ad88376..0000000
--- a/src/Navigator.Abstractions/INotificationLauncher.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Threading.Tasks;
-
-namespace Navigator.Abstractions
-{
- public interface INotificationLauncher
- {
- Task Launch();
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/Action.cs b/src/Navigator.Extensions.Actions/Action.cs
deleted file mode 100644
index c8a15ec..0000000
--- a/src/Navigator.Extensions.Actions/Action.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- ///
- public abstract class Action : IAction
- {
- ///
- public virtual int Order => 1000;
-
- ///
- public abstract string Type { get; }
-
- ///
- public DateTime Timestamp { get; } = DateTime.UtcNow;
-
- ///
- public abstract IAction Init(INavigatorContext ctx);
-
- ///
- public abstract bool CanHandle(INavigatorContext ctx);
-
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ActionHandler.cs b/src/Navigator.Extensions.Actions/ActionHandler.cs
deleted file mode 100644
index 287ec68..0000000
--- a/src/Navigator.Extensions.Actions/ActionHandler.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using MediatR;
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ActionHandler : IRequestHandler where TAction : IAction
- {
- public readonly INavigatorContext Ctx;
-
- public ActionHandler(INavigatorContext ctx)
- {
- Ctx = ctx;
- }
-
- public abstract Task Handle(TAction request, CancellationToken cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/CallbackQueryAction.cs b/src/Navigator.Extensions.Actions/CallbackQueryAction.cs
deleted file mode 100644
index c9016b0..0000000
--- a/src/Navigator.Extensions.Actions/CallbackQueryAction.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class CallbackQueryAction : Action
- {
- public override string Type => ActionType.CallbackQuery;
-
- public string Data { get; set; } = string.Empty;
-
- public bool IsGameQuery { get; set; }
-
- public override IAction Init(INavigatorContext ctx)
- {
- Data = ctx.Update.CallbackQuery.Data;
- IsGameQuery = ctx.Update.CallbackQuery.IsGameQuery;
-
- return this;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChannelCreatedAction.cs b/src/Navigator.Extensions.Actions/ChannelCreatedAction.cs
deleted file mode 100644
index 63df302..0000000
--- a/src/Navigator.Extensions.Actions/ChannelCreatedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChannelCreatedAction : Action
- {
- public override string Type => ActionType.ChannelCreated;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChannelPostAction.cs b/src/Navigator.Extensions.Actions/ChannelPostAction.cs
deleted file mode 100644
index facd2da..0000000
--- a/src/Navigator.Extensions.Actions/ChannelPostAction.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChannelPostAction : Action
- {
- public override string Type => ActionType.ChannelPost;
- public DateTime PostTimestamp { get; protected set; }
- public int PostId { get; protected set; }
-
- public override IAction Init(INavigatorContext ctx)
- {
- PostTimestamp = ctx.Update.ChannelPost.Date;
- PostId = ctx.Update.ChannelPost.MessageId;
-
- return this;
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChatMemberLeftAction.cs b/src/Navigator.Extensions.Actions/ChatMemberLeftAction.cs
deleted file mode 100644
index fc64e7b..0000000
--- a/src/Navigator.Extensions.Actions/ChatMemberLeftAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChatMemberLeftAction : Action
- {
- public override string Type => ActionType.ChatMemberLeft;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChatMembersAddedAction.cs b/src/Navigator.Extensions.Actions/ChatMembersAddedAction.cs
deleted file mode 100644
index 0d94879..0000000
--- a/src/Navigator.Extensions.Actions/ChatMembersAddedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChatMembersAddedAction : Action
- {
- public override string Type => ActionType.ChatMembersAdded;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChatPhotoChangedAction.cs b/src/Navigator.Extensions.Actions/ChatPhotoChangedAction.cs
deleted file mode 100644
index 4553806..0000000
--- a/src/Navigator.Extensions.Actions/ChatPhotoChangedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChatPhotoChangedAction : Action
- {
- public override string Type => ActionType.ChatPhotoChanged;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChatPhotoDeletedAction.cs b/src/Navigator.Extensions.Actions/ChatPhotoDeletedAction.cs
deleted file mode 100644
index 16f1aa2..0000000
--- a/src/Navigator.Extensions.Actions/ChatPhotoDeletedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChatPhotoDeletedAction : Action
- {
- public override string Type => ActionType.ChatPhotoDeleted;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ChatTitleChangedAction.cs b/src/Navigator.Extensions.Actions/ChatTitleChangedAction.cs
deleted file mode 100644
index 68dc234..0000000
--- a/src/Navigator.Extensions.Actions/ChatTitleChangedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ChatTitleChangedAction : Action
- {
- public override string Type => ActionType.ChatTitleChanged;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/CommandAction.cs b/src/Navigator.Extensions.Actions/CommandAction.cs
deleted file mode 100644
index 9332a46..0000000
--- a/src/Navigator.Extensions.Actions/CommandAction.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class CommandAction : Action
- {
- public override string Type => ActionType.Command;
- public DateTime MessageTimestamp { get; protected set; }
- public int MessageId { get; protected set; }
- public int? ReplyToMessageId { get; protected set; }
- public string Command { get; set; } = string.Empty;
-
- public override IAction Init(INavigatorContext ctx)
- {
- MessageTimestamp = ctx.Update.Message.Date;
- MessageId = ctx.Update.Message.MessageId;
- ReplyToMessageId = ctx.Update.Message.ReplyToMessage?.MessageId;
-
- Command = ctx.Update.Message.ExtractCommand(ctx.BotProfile.Username) ?? string.Empty;
-
- return this;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/EditedChannelPostAction.cs b/src/Navigator.Extensions.Actions/EditedChannelPostAction.cs
deleted file mode 100644
index eaca5be..0000000
--- a/src/Navigator.Extensions.Actions/EditedChannelPostAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class EditedChannelPostAction : ChannelPostAction
- {
- public override string Type => ActionType.EditedChannelPost;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/EditedMessageAction.cs b/src/Navigator.Extensions.Actions/EditedMessageAction.cs
deleted file mode 100644
index c938cb8..0000000
--- a/src/Navigator.Extensions.Actions/EditedMessageAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class EditedMessageAction : MessageAction
- {
- public override string Type => ActionType.EditedMessage;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/GroupCreatedAction.cs b/src/Navigator.Extensions.Actions/GroupCreatedAction.cs
deleted file mode 100644
index 9fe4135..0000000
--- a/src/Navigator.Extensions.Actions/GroupCreatedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class GroupCreatedAction : Action
- {
- public override string Type => ActionType.GroupCreated;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/InlineQueryAction.cs b/src/Navigator.Extensions.Actions/InlineQueryAction.cs
deleted file mode 100644
index 709f5f8..0000000
--- a/src/Navigator.Extensions.Actions/InlineQueryAction.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class InlineQueryAction : Action
- {
- public override string Type => ActionType.InlineQuery;
- public string InlineQueryId { get; protected set; } = string.Empty;
- public string Query { get; protected set; } = string.Empty;
- public string Offset { get; protected set; } = string.Empty;
-
- public override IAction Init(INavigatorContext ctx)
- {
- InlineQueryId = ctx.Update.InlineQuery.Id;
- Query = ctx.Update.InlineQuery.Query;
- Offset = ctx.Update.InlineQuery.Offset;
-
- return this;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/InlineResultChosenAction.cs b/src/Navigator.Extensions.Actions/InlineResultChosenAction.cs
deleted file mode 100644
index 703a6fa..0000000
--- a/src/Navigator.Extensions.Actions/InlineResultChosenAction.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class InlineResultChosenAction : Action
- {
- public override string Type => ActionType.InlineResultChosen;
- public string ChosenResultId { get; protected set; } = string.Empty;
- public string Query { get; protected set; } = string.Empty;
-
- public override IAction Init(INavigatorContext ctx)
- {
- ChosenResultId = ctx.Update.ChosenInlineResult.ResultId;
- Query = ctx.Update.ChosenInlineResult.Query;
-
- return this;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/MessageAction.cs b/src/Navigator.Extensions.Actions/MessageAction.cs
deleted file mode 100644
index 6141292..0000000
--- a/src/Navigator.Extensions.Actions/MessageAction.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class MessageAction : Action
- {
- public override string Type => ActionType.Message;
- public DateTime MessageTimestamp { get; protected set; }
- public int MessageId { get; protected set; }
- public int? ReplyToMessageId { get; protected set; }
-
- public override IAction Init(INavigatorContext ctx)
- {
- MessageTimestamp = ctx.Update.Message.Date;
- MessageId = ctx.Update.Message.MessageId;
- ReplyToMessageId = ctx.Update.Message.ReplyToMessage?.MessageId;
-
- return this;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/MessageExtension.cs b/src/Navigator.Extensions.Actions/MessageExtension.cs
deleted file mode 100644
index 9fdde01..0000000
--- a/src/Navigator.Extensions.Actions/MessageExtension.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Linq;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.Enums;
-
-namespace Navigator.Extensions.Actions
-{
- public static class MessageExtension
- {
- public static string? ExtractCommand(this Message message, string botName)
- {
- if (message.Entities?.First()?.Type != MessageEntityType.BotCommand) return default;
-
- var command = message.EntityValues.First();
-
- if (!command.Contains('@')) return command;
-
- if (!command.Contains(botName)) return default;
-
- command = command.Substring(0, command.IndexOf('@'));
-
- return command;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/MessagePinnedAction.cs b/src/Navigator.Extensions.Actions/MessagePinnedAction.cs
deleted file mode 100644
index fd0d834..0000000
--- a/src/Navigator.Extensions.Actions/MessagePinnedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class MessagePinnedAction : Action
- {
- public override string Type => ActionType.MessagePinned;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/MigratedFromGroupAction.cs b/src/Navigator.Extensions.Actions/MigratedFromGroupAction.cs
deleted file mode 100644
index 3c7e16a..0000000
--- a/src/Navigator.Extensions.Actions/MigratedFromGroupAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class MigratedFromGroupAction : Action
- {
- public override string Type => ActionType.MigratedFromGroup;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/MigratedToSupergroupAction.cs b/src/Navigator.Extensions.Actions/MigratedToSupergroupAction.cs
deleted file mode 100644
index e8734a5..0000000
--- a/src/Navigator.Extensions.Actions/MigratedToSupergroupAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class MigratedToSupergroupAction : Action
- {
- public override string Type => ActionType.MigratedToSupergroup;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/Navigator.Extensions.Actions.csproj b/src/Navigator.Extensions.Actions/Navigator.Extensions.Actions.csproj
deleted file mode 100644
index 17e60b6..0000000
--- a/src/Navigator.Extensions.Actions/Navigator.Extensions.Actions.csproj
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- net5.0
- 9
- true
- True
- 0.9.100-beta
- Navigator.Extensions.Actions
- Navigator Extensions Actions
- Lucas Maximiliano Marino
- Common actions for Navigator Framework
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator, Actions, Extension
- true
- Copyright © Lucas Maximiliano Marino 2020
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
- enable
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Actions/PollAction.cs b/src/Navigator.Extensions.Actions/PollAction.cs
deleted file mode 100644
index 64a2e8b..0000000
--- a/src/Navigator.Extensions.Actions/PollAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class PollAction : Action
- {
- public override string Type => ActionType.Poll;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/PreCheckoutQueryAction.cs b/src/Navigator.Extensions.Actions/PreCheckoutQueryAction.cs
deleted file mode 100644
index 5845f26..0000000
--- a/src/Navigator.Extensions.Actions/PreCheckoutQueryAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class PreCheckoutQueryAction : Action
- {
- public override string Type => ActionType.PreCheckoutQuery;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/ShippingQueryAction.cs b/src/Navigator.Extensions.Actions/ShippingQueryAction.cs
deleted file mode 100644
index 70f3b96..0000000
--- a/src/Navigator.Extensions.Actions/ShippingQueryAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class ShippingQueryAction : Action
- {
- public override string Type => ActionType.ShippingQuery;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/SupergroupCreatedAction.cs b/src/Navigator.Extensions.Actions/SupergroupCreatedAction.cs
deleted file mode 100644
index ab8e3e0..0000000
--- a/src/Navigator.Extensions.Actions/SupergroupCreatedAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class SupergroupCreatedAction : Action
- {
- public override string Type => ActionType.SupergroupCreated;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Actions/UnknownAction.cs b/src/Navigator.Extensions.Actions/UnknownAction.cs
deleted file mode 100644
index 970666a..0000000
--- a/src/Navigator.Extensions.Actions/UnknownAction.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Navigator.Abstractions;
-
-namespace Navigator.Extensions.Actions
-{
- public abstract class UnknownAction : Action
- {
- public override string Type => ActionType.Unknown;
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard.Abstractions/IBotManagementService.cs b/src/Navigator.Extensions.Shipyard.Abstractions/IBotManagementService.cs
deleted file mode 100644
index 61607d2..0000000
--- a/src/Navigator.Extensions.Shipyard.Abstractions/IBotManagementService.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Threading;
-using System.Threading.Tasks;
-using Navigator.Extensions.Shipyard.Abstractions.Model;
-
-namespace Navigator.Extensions.Shipyard.Abstractions
-{
- public interface IBotManagementService
- {
- Task GetBotInfo(CancellationToken cancellationToken = default);
- Task GetBotPic(CancellationToken cancellationToken = default);
- Task GetBotCommands(CancellationToken cancellationToken = default);
- Task UpdateBotCommands(BotCommands botCommands, CancellationToken cancellationToken = default);
-
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotCommands.cs b/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotCommands.cs
deleted file mode 100644
index dea35e7..0000000
--- a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotCommands.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-
-namespace Navigator.Extensions.Shipyard.Abstractions.Model
-{
- ///
- /// Bot commands.
- ///
- public class BotCommands
- {
- ///
- /// List of commands.
- ///
- public Dictionary Commands { get; init; }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotInfo.cs b/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotInfo.cs
deleted file mode 100644
index b79320f..0000000
--- a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotInfo.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-namespace Navigator.Extensions.Shipyard.Abstractions.Model
-{
- ///
- /// Bot information.
- ///
- public class BotInfo
- {
- ///
- /// Bot id.
- ///
- public long Id { get; init; }
-
- ///
- /// Bot username.
- ///
- public string Username { get; init; }
-
- ///
- /// Bot name.
- ///
- public string Name { get; init; }
-
- ///
- /// Bot permissions.
- ///
- public BotPermissions Permissions { get; init; }
-
- ///
- /// Bot permissions.
- ///
- public class BotPermissions
- {
- ///
- /// CanJoinGroups
- ///
- public bool CanJoinGroups { get; init; }
-
- ///
- /// CanReadAllGroupMessages
- ///
- public bool CanReadAllGroupMessages { get; init; }
-
- ///
- /// SupportsInlineQueries
- ///
- public bool SupportsInlineQueries { get; init; }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotPic.cs b/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotPic.cs
deleted file mode 100644
index 310a420..0000000
--- a/src/Navigator.Extensions.Shipyard.Abstractions/Model/BotPic.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.IO;
-
-namespace Navigator.Extensions.Shipyard.Abstractions.Model
-{
- ///
- /// Bot Picture.
- ///
- public class BotPic
- {
- ///
- /// Picture in stream format.
- ///
- public MemoryStream File { get; init; }
-
- ///
- /// Mime type of the picture.
- ///
- public string MimeType { get; init; } = "image/png";
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard.Abstractions/Navigator.Extensions.Shipyard.Abstractions.csproj b/src/Navigator.Extensions.Shipyard.Abstractions/Navigator.Extensions.Shipyard.Abstractions.csproj
deleted file mode 100644
index 864fe98..0000000
--- a/src/Navigator.Extensions.Shipyard.Abstractions/Navigator.Extensions.Shipyard.Abstractions.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net5.0
- enable
- true
- true
- 0.9.100-beta
- Navigator.Extensions.Shipyard.Abstractions
- Navigator Extensions Shipyard Abstractions
- Lucas Maximiliano Marino
- A highly opinionated telegram bot framework, mainly based on Telegram.Bot.
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator
- true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Shipyard/BotManagementService.cs b/src/Navigator.Extensions.Shipyard/BotManagementService.cs
deleted file mode 100644
index a45ffc4..0000000
--- a/src/Navigator.Extensions.Shipyard/BotManagementService.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Navigator.Abstractions;
-using Navigator.Extensions.Shipyard.Abstractions;
-using Navigator.Extensions.Shipyard.Abstractions.Model;
-
-namespace Navigator.Extensions.Shipyard
-{
- ///
- public class BotManagementService : IBotManagementService
- {
- private readonly ILogger _logger;
- private readonly IBotClient _botClient;
-
- ///
- /// Default constructor.
- ///
- ///
- ///
- public BotManagementService(ILogger logger, IBotClient botClient)
- {
- _logger = logger;
- _botClient = botClient;
- }
-
- ///
- public async Task GetBotInfo(CancellationToken cancellationToken = default)
- {
- try
- {
- var info = await _botClient.GetMeAsync(cancellationToken);
-
- return new BotInfo
- {
- Id = info.Id,
- Username = info.Username,
- Name = info.FirstName,
- Permissions = new BotInfo.BotPermissions
- {
- CanJoinGroups = info.CanJoinGroups ?? false,
- CanReadAllGroupMessages = info.CanReadAllGroupMessages ?? false,
- SupportsInlineQueries = info.SupportsInlineQueries ?? false
- }
- };
- }
- catch (Exception e)
- {
- _logger.LogError(e, "Unhandled exception requesting bot information.");
-
- return default;
- }
- }
-
- ///
- public async Task GetBotPic(CancellationToken cancellationToken = default)
- {
- try
- {
- var photos = await _botClient.GetUserProfilePhotosAsync(_botClient.BotId, cancellationToken: cancellationToken);
-
- if (photos.TotalCount > 0)
- {
- var botPicId = photos.Photos.FirstOrDefault()?
- .OrderByDescending(x => x.FileSize).FirstOrDefault()
- ?.FileId;
-
- if (!string.IsNullOrWhiteSpace(botPicId))
- {
- var pictureStream = new MemoryStream();
- await _botClient.GetInfoAndDownloadFileAsync(botPicId, pictureStream, cancellationToken);
-
- return new BotPic
- {
- File = pictureStream
- };
- }
- }
- }
- catch (Exception e)
- {
- _logger.LogError(e, "Unhandled exception requesting bot pic.");
- }
-
- return default;
- }
-
- ///
- public async Task GetBotCommands(CancellationToken cancellationToken = default)
- {
- var commands = await _botClient.GetMyCommandsAsync(cancellationToken);
-
- return new BotCommands
- {
- Commands = commands.ToDictionary(command => command.Command, command => command.Description)
- };
- }
-
- ///
- public Task UpdateBotCommands(BotCommands botCommands, CancellationToken cancellationToken = default)
- {
- throw new System.NotImplementedException();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/Controllers/BotController.cs b/src/Navigator.Extensions.Shipyard/Controllers/BotController.cs
deleted file mode 100644
index 389cdd6..0000000
--- a/src/Navigator.Extensions.Shipyard/Controllers/BotController.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Navigator.Extensions.Shipyard.Abstractions;
-using Navigator.Extensions.Shipyard.Middleware;
-
-namespace Navigator.Extensions.Shipyard.Controllers
-{
- ///
- /// Bot controller.
- ///
- [Authorize(AuthenticationSchemes = nameof(ShipyardApiKeyAuthenticationHandler))]
- [ApiController]
- [ApiVersion("1")]
- [Route("shipyard/api/v{v:apiVersion}/[controller]")]
- public class BotController : ControllerBase
- {
- private readonly IBotManagementService _botManagementService;
-
- ///
- /// Default constructor.
- ///
- ///
- public BotController(IBotManagementService botManagementService)
- {
- _botManagementService = botManagementService;
- }
-
- [HttpGet("information")]
- public async Task GetBotInformation(CancellationToken cancellationToken)
- {
- var information = await _botManagementService.GetBotInfo(cancellationToken);
-
- return Ok(information);
- }
-
- [HttpGet("pic")]
- public async Task GetBotPic(CancellationToken cancellationToken)
- {
- var botPic = await _botManagementService.GetBotPic(cancellationToken);
-
- if (botPic is not null)
- {
- return File(botPic.File, botPic.MimeType);
- }
- return NotFound("BotPic not found");
- }
-
- [HttpGet("commands")]
- public async Task GetBotCommands(CancellationToken cancellationToken)
- {
- var botCommands = await _botManagementService.GetBotCommands(cancellationToken);
-
- return Ok(botCommands);
- }
-
- [HttpPut("commands")]
- public async Task UpdateBotCommands()
- {
- throw new NotImplementedException();
- }
-
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/Controllers/ConversationController.cs b/src/Navigator.Extensions.Shipyard/Controllers/ConversationController.cs
deleted file mode 100644
index 0e0090f..0000000
--- a/src/Navigator.Extensions.Shipyard/Controllers/ConversationController.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Mvc;
-using Navigator.Abstractions;
-using Navigator.Extensions.Shipyard.Middleware;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-using Telegram.Bot.Types.Enums;
-
-namespace Navigator.Extensions.Shipyard.Controllers
-{
- ///
- /// Conversation controller.
- ///
- ///
- ///
- [Authorize(AuthenticationSchemes = nameof(ShipyardApiKeyAuthenticationHandler))]
- [ApiController]
- [ApiVersion("1")]
- [Route("shipyard/api/v{v:apiVersion}/conversation")]
- public class ConversationController : ControllerBase where TUser : User where TChat : Chat
- {
- private readonly IEntityManager _entityManager;
- private readonly IBotClient _botClient;
-
- ///
- /// Default constructor.
- ///
- ///
- ///
- public ConversationController(IEntityManager entityManager, IBotClient botClient)
- {
- _entityManager = entityManager;
- _botClient = botClient;
- }
-
- ///
- /// Gets all the users.
- ///
- ///
- ///
- [HttpGet("user")]
- public IActionResult GetUsers(CancellationToken cancellationToken)
- {
- try
- {
- var users = _entityManager.FindAllUsersAsync(cancellationToken);
-
- if (users is not null)
- {
- return Ok(users);
- }
- }
- catch (Exception)
- {
- // ignored
- }
-
- return NotFound();
- }
-
- ///
- /// Gets all the chats.
- ///
- ///
- ///
- [HttpGet("chat")]
- public IActionResult GetChats(CancellationToken cancellationToken)
- {
- try
- {
- var chats = _entityManager.FindAllChatsAsync(cancellationToken);
-
- if (chats is not null)
- {
- return Ok(chats);
- }
- }
- catch (Exception)
- {
- // ignored
- }
-
- return NotFound();
- }
-
- [HttpPost("chat/{chatId}")]
- public async Task SendMessage([FromRoute] long chatId, [FromBody] string message, CancellationToken cancellationToken)
- {
- if (await _entityManager.FindChatAsync(chatId, cancellationToken) is null)
- {
- return NotFound();
- }
-
- await _botClient.SendTextMessageAsync(chatId, message, ParseMode.MarkdownV2, cancellationToken: cancellationToken);
-
- return Ok();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/GenericControllerFeatureProvider.cs b/src/Navigator.Extensions.Shipyard/GenericControllerFeatureProvider.cs
deleted file mode 100644
index 9efcac3..0000000
--- a/src/Navigator.Extensions.Shipyard/GenericControllerFeatureProvider.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using Microsoft.AspNetCore.Mvc.ApplicationParts;
-using Microsoft.AspNetCore.Mvc.Controllers;
-using Navigator.Extensions.Shipyard.Controllers;
-
-namespace Navigator.Extensions.Shipyard
-{
- public class GenericControllerFeatureProvider : IApplicationFeatureProvider
- {
- private Type UserType { get; }
- private Type ChatType { get; }
-
- public GenericControllerFeatureProvider(Type userType, Type chatType)
- {
- UserType = userType;
- ChatType = chatType;
- }
-
- public void PopulateFeature(IEnumerable parts, ControllerFeature feature)
- {
- // Check to see if there is a "real" controller for this class
- if (!feature.Controllers.Any(t => t.GenericTypeParameters.Any(gp => gp == UserType) && t.GenericTypeParameters.Any(gp => gp == ChatType)))
- {
- // Create a generic controller for this type
- var controllerType = typeof(ConversationController<,>).MakeGenericType(UserType, ChatType).GetTypeInfo();
- feature.Controllers.Add(controllerType);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/Middleware/ShipyardApiKeyAuthenticationHandler.cs b/src/Navigator.Extensions.Shipyard/Middleware/ShipyardApiKeyAuthenticationHandler.cs
deleted file mode 100644
index 39784db..0000000
--- a/src/Navigator.Extensions.Shipyard/Middleware/ShipyardApiKeyAuthenticationHandler.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System.Collections.Generic;
-using System.Security.Claims;
-using System.Text.Encodings.Web;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-
-namespace Navigator.Extensions.Shipyard.Middleware
-{
- internal class ShipyardApiKeyAuthenticationHandler : AuthenticationHandler
- {
- private readonly NavigatorOptions _navigatorOptions;
-
- public ShipyardApiKeyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, NavigatorOptions navigatorOptions) : base(options, logger, encoder, clock)
- {
- _navigatorOptions = navigatorOptions;
- }
-
- ///
- protected override Task HandleAuthenticateAsync()
- {
- if (_navigatorOptions.GetShipyardApiKey() is null)
- {
- return Task.FromResult(AuthenticateResult.Fail("Navigator Shipyard is not configured. Please configure an API KEY."));
- }
-
- if (Context.Request.Headers.TryGetValue("SHIPYARD-API-KEY", out var headerApiKey) && headerApiKey == _navigatorOptions.GetShipyardApiKey())
- {
- const string username = "manager";
- var claims = new[]
- {
- new Claim(ClaimTypes.NameIdentifier, username, ClaimValueTypes.String, Options.ClaimsIssuer),
- new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Options.ClaimsIssuer)
- };
-
- ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
-
- AuthenticationTicket ticket = new(principal, Scheme.Name);
- return Task.FromResult(AuthenticateResult.Success(ticket));
- }
-
- return Task.FromResult(AuthenticateResult.Fail("Unauthorized"));
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/Navigator.Extensions.Shipyard.csproj b/src/Navigator.Extensions.Shipyard/Navigator.Extensions.Shipyard.csproj
deleted file mode 100644
index 1669592..0000000
--- a/src/Navigator.Extensions.Shipyard/Navigator.Extensions.Shipyard.csproj
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- net5.0
- enable
- true
- true
- 0.9.100-beta
- Navigator.Extensions.Shipyard
- Navigator Extensions Shipyard
- Lucas Maximiliano Marino
- A highly opinionated telegram bot framework, mainly based on Telegram.Bot.
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator
- true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Shipyard/NavigatorOptionsCollectionExtensions.cs b/src/Navigator.Extensions.Shipyard/NavigatorOptionsCollectionExtensions.cs
deleted file mode 100644
index 66a39fb..0000000
--- a/src/Navigator.Extensions.Shipyard/NavigatorOptionsCollectionExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Shipyard
-{
- public static class NavigatorOptionsCollectionExtensions
- {
- #region ShipyardApiKey
-
- private const string ShipyardApiKeyKey = "_navigator.extensions.shipyard.options.api_key";
-
- public static void SetShipyardApiKey(this INavigatorOptions navigatorOptions, string apiKey)
- {
- navigatorOptions.TryRegisterOption(ShipyardApiKeyKey, apiKey);
- }
-
- public static string? GetShipyardApiKey(this INavigatorOptions navigatorOptions)
- {
- return navigatorOptions.RetrieveOption(ShipyardApiKeyKey);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Shipyard/NavigatorServiceCollectionExtensions.cs b/src/Navigator.Extensions.Shipyard/NavigatorServiceCollectionExtensions.cs
deleted file mode 100644
index 3c9b797..0000000
--- a/src/Navigator.Extensions.Shipyard/NavigatorServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Linq;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.DependencyInjection;
-using Navigator.Extensions.Shipyard.Abstractions;
-using Navigator.Extensions.Shipyard.Middleware;
-using Navigator.Extensions.Store;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Shipyard
-{
- ///
- /// Navigator Shipyard Services
- ///
- public static class NavigatorServiceCollectionExtensions
- {
- ///
- /// Registers all the necessary navigatorBuilder for shipyard to work.
- ///
- ///
- ///
- ///
- public static NavigatorBuilder AddShipyard(this NavigatorBuilder navigatorBuilder, Action? options = default)
- {
- if (options is null)
- {
- throw new ArgumentException("Please configure shipyard options.");
- }
-
- options.Invoke(navigatorBuilder.Options);
-
- navigatorBuilder.RegisterOrReplaceOptions();
-
- navigatorBuilder.Services.AddControllers().ConfigureApplicationPartManager(m =>
- m.FeatureProviders.Add(
- new GenericControllerFeatureProvider(navigatorBuilder.Options.GetUserType()!, navigatorBuilder.Options.GetChatType()!)
- ));
- navigatorBuilder.Services.AddAuthentication().AddScheme(
- nameof(ShipyardApiKeyAuthenticationHandler), o => { });
-
- navigatorBuilder.Services.AddScoped();
-
- return navigatorBuilder;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions.Extensions/INavigatorContextExtensions.cs b/src/Navigator.Extensions.Store.Abstractions.Extensions/INavigatorContextExtensions.cs
deleted file mode 100644
index 56431c3..0000000
--- a/src/Navigator.Extensions.Store.Abstractions.Extensions/INavigatorContextExtensions.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using System;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Store.Abstractions.Extensions
-{
- public static class INavigatorContextExtensions
- {
- public static string DefaultUserKey => "navigator.extensions.store.user";
- public static string DefaultChatKey => "navigator.extensions.store.chat";
-
- #region User
-
- public static TUser GetUser(this INavigatorContext ctx) where TUser : User
- {
- return ctx.GetUserOrDefault() ?? throw new Exception("User not found");
- }
-
- public static TUser? GetUserOrDefault(this INavigatorContext ctx, TUser defaultValue = default) where TUser : User
- {
- return ctx.Get(DefaultUserKey) ?? defaultValue;
- }
-
- #endregion
-
- #region Chat
-
- public static TChat GetChat(this INavigatorContext ctx) where TChat : Chat
- {
- return ctx.GetChatOrDefault() ?? throw new Exception("User not found");
- }
-
- public static TChat? GetChatOrDefault(this INavigatorContext ctx, TChat defaultValue = default) where TChat : Chat
- {
- return ctx.Get(DefaultChatKey) ?? defaultValue;
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions.Extensions/Navigator.Extensions.Store.Abstractions.Extensions.csproj b/src/Navigator.Extensions.Store.Abstractions.Extensions/Navigator.Extensions.Store.Abstractions.Extensions.csproj
deleted file mode 100644
index f5b247d..0000000
--- a/src/Navigator.Extensions.Store.Abstractions.Extensions/Navigator.Extensions.Store.Abstractions.Extensions.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net5.0
- enable
- true
- true
- 0.9.99-beta
- Navigator.Extensions.Store.Abstractions.Extensions
- Navigator Extensions Store Abstractions Extensions
- Lucas Maximiliano Marino
- Store extension for Navigator Framework.
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator, EF Core, Store, Database, Extension
- true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Store.Abstractions/Entity/Chat.cs b/src/Navigator.Extensions.Store.Abstractions/Entity/Chat.cs
deleted file mode 100644
index e6dc1f4..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/Entity/Chat.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Navigator.Extensions.Store.Abstractions.Entity
-{
- public class Chat
- {
- public Chat()
- {
- Conversations = new List();
- CreatedAt = DateTime.UtcNow;
- }
-
- ///
- /// Id of the chat.
- ///
- public long Id { get; set; }
- ///
- /// Optional, available when the type of the chat is private.
- ///
- public string? Username { get; set; }
- ///
- /// Optional, available when the type of the chat is a group, supergroup or channel.
- ///
- public string? Title { get; set; }
- ///
- /// Type of the chat.
- ///
- public ChatType Type { get; set; }
- ///
- /// List of conversations that this chat has.
- ///
- public List Conversations { get; set; }
- ///
- /// Date of first interaction with the chat.
- ///
- public DateTime CreatedAt { get; set; }
-
- public enum ChatType
- {
- Private,
- Group,
- Channel,
- Supergroup,
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/Entity/Conversation.cs b/src/Navigator.Extensions.Store.Abstractions/Entity/Conversation.cs
deleted file mode 100644
index ff0efd4..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/Entity/Conversation.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-
-namespace Navigator.Extensions.Store.Abstractions.Entity
-{
- public class Conversation
- {
- public Conversation()
- {
- CreatedAt = DateTime.UtcNow;
- }
-
- public long ChatId { get; set; }
- public Chat Chat { get; set; }
- public int UserId { get; set; }
- public User User { get; set; }
- public DateTime CreatedAt { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/Entity/User.cs b/src/Navigator.Extensions.Store.Abstractions/Entity/User.cs
deleted file mode 100644
index 5f3c025..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/Entity/User.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Navigator.Extensions.Store.Abstractions.Entity
-{
- public class User
- {
- public User()
- {
- Conversations = new List();
- CreatedAt = DateTime.UtcNow;
- }
-
- ///
- /// Id of the user.
- ///
- public int Id { get; set; }
- ///
- /// Specifies if the user is a bot or not.
- ///
- public bool IsBot { get; set; }
- ///
- /// Language code of the user client.
- ///
- public string? LanguageCode { get; set; }
- ///
- /// Username of the user.
- ///
- public string? Username { get; set; }
- ///
- /// List of conversations that this user has.
- ///
- public List Conversations { get; set; }
- ///
- /// Date of first interaction with the user.
- ///
- public DateTime CreatedAt { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/IChatMapper.cs b/src/Navigator.Extensions.Store.Abstractions/IChatMapper.cs
deleted file mode 100644
index 4c7091a..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/IChatMapper.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Telegram.Bot.Types;
-
-namespace Navigator.Extensions.Store.Abstractions
-{
- public interface IChatMapper
- {
- TChat Parse(Chat chat);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/IEntityManager.cs b/src/Navigator.Extensions.Store.Abstractions/IEntityManager.cs
deleted file mode 100644
index 772eac3..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/IEntityManager.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Store.Abstractions
-{
- public interface IEntityManager
- where TUser : User
- where TChat : Chat
- {
- Task Handle(Telegram.Bot.Types.User telegramUser, CancellationToken cancellationToken = default);
- Task Handle(Telegram.Bot.Types.User telegramUser, Telegram.Bot.Types.Chat telegramChat, CancellationToken cancellationToken = default);
- TUser? FindUser(int id);
- Task FindUserAsync(int id, CancellationToken cancellationToken = default);
- IEnumerable FindAllUsersAsync(CancellationToken cancellationToken = default);
- TChat? FindChat(long id);
- Task FindChatAsync(long id, CancellationToken cancellationToken = default);
- IEnumerable FindAllChatsAsync(CancellationToken cancellationToken = default);
- Task MigrateFromGroup(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default);
- Task MigrateToSupergroup(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default);
- Task ChatTitleChanged(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default);
- Task ChatMemberLeft(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/IUserMapper.cs b/src/Navigator.Extensions.Store.Abstractions/IUserMapper.cs
deleted file mode 100644
index dd11aef..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/IUserMapper.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Telegram.Bot.Types;
-
-namespace Navigator.Extensions.Store.Abstractions
-{
- public interface IUserMapper
- {
- TUser Parse(User user);
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Abstractions/Navigator.Extensions.Store.Abstractions.csproj b/src/Navigator.Extensions.Store.Abstractions/Navigator.Extensions.Store.Abstractions.csproj
deleted file mode 100644
index 215aa22..0000000
--- a/src/Navigator.Extensions.Store.Abstractions/Navigator.Extensions.Store.Abstractions.csproj
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- net5.0
- enable
- true
- true
- 0.9.100-beta
- Navigator.Extensions.Store.Abstractions
- Navigator Extensions Store Abstractions
- Lucas Maximiliano Marino
- Store extension for Navigator Framework.
- https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
- https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator, EF Core, Store, Database, Extension
- true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramChatDataExtractor.cs b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramChatDataExtractor.cs
new file mode 100644
index 0000000..da60ac3
--- /dev/null
+++ b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramChatDataExtractor.cs
@@ -0,0 +1,30 @@
+using Navigator.Entities;
+using Navigator.Extensions.Store.Extractors;
+using Navigator.Providers.Telegram.Entities;
+
+namespace Navigator.Extensions.Store.Telegram.Extractors;
+
+public class TelegramChatDataExtractor : IDataExtractor
+{
+ public Dictionary From(Conversation source)
+ {
+ var dict = new Dictionary();
+
+ if (source.Chat is TelegramChat chat)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramChat.ExternalIdentifier)}", chat.ExternalIdentifier.ToString());
+
+ if (chat.Title is not null)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramChat.Title)}", chat.Title);
+ }
+ }
+
+ return dict;
+ }
+
+ public bool Maps(Type type)
+ {
+ return type == typeof(TelegramChat);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramConversationDataExtractor.cs b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramConversationDataExtractor.cs
new file mode 100644
index 0000000..cc301c1
--- /dev/null
+++ b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramConversationDataExtractor.cs
@@ -0,0 +1,20 @@
+using Navigator.Entities;
+using Navigator.Extensions.Store.Extractors;
+using Navigator.Providers.Telegram.Entities;
+
+namespace Navigator.Extensions.Store.Telegram.Extractors;
+
+public class TelegramConversationDataExtractor : IDataExtractor
+{
+ public Dictionary From(Conversation source)
+ {
+ var dict = new Dictionary();
+
+ return dict;
+ }
+
+ public bool Maps(Type type)
+ {
+ return type == typeof(TelegramConversation);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramUserDataExtractor.cs b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramUserDataExtractor.cs
new file mode 100644
index 0000000..b1ad08b
--- /dev/null
+++ b/src/Navigator.Extensions.Store.Telegram/Extractors/TelegramUserDataExtractor.cs
@@ -0,0 +1,42 @@
+using Navigator.Entities;
+using Navigator.Extensions.Store.Extractors;
+using Navigator.Providers.Telegram.Entities;
+
+namespace Navigator.Extensions.Store.Telegram.Extractors;
+
+public class TelegramUserDataExtractor : IDataExtractor
+{
+ public Dictionary From(Conversation source)
+ {
+ var dict = new Dictionary();
+
+ if (source.User is TelegramUser user)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramUser.ExternalIdentifier)}", user.ExternalIdentifier.ToString());
+
+ if (user.Username is not null)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramUser.Username)}", user.Username);
+ }
+
+ dict.Add($"navigator.telegram.{nameof(TelegramUser.FirstName)}", user.FirstName);
+
+ if (user.LastName is not null)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramUser.LastName)}", user.LastName);
+ }
+
+ if (user.LanguageCode is not null)
+ {
+ dict.Add($"navigator.telegram.{nameof(TelegramUser.LanguageCode)}", user.LanguageCode);
+ }
+ }
+
+ return dict;
+ }
+
+ public bool Maps(Type type)
+ {
+ return type == typeof(TelegramUser);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store.Telegram/Navigator.Extensions.Store.Telegram.csproj b/src/Navigator.Extensions.Store.Telegram/Navigator.Extensions.Store.Telegram.csproj
new file mode 100644
index 0000000..df0cdb6
--- /dev/null
+++ b/src/Navigator.Extensions.Store.Telegram/Navigator.Extensions.Store.Telegram.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net6.0
+ true
+ enable
+ true
+ Navigator.Extensions.Store
+ Lucas Maximiliano Marino
+ Telegram provider persistence implementation for Navigator Store Extension.
+ https://github.com/navigatorframework/navigator
+ https://github.com/navigatorframework/navigator
+ https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
+ Store, Bot, Telegram, Navigator
+ true
+ AGPL-3.0-only
+ Copyright © Lucas Maximiliano Marino 2022
+
+
+
+
+
+
+
+
diff --git a/src/Navigator.Extensions.Store.Telegram/NavigatorExtensionConfigurationExtensions.cs b/src/Navigator.Extensions.Store.Telegram/NavigatorExtensionConfigurationExtensions.cs
new file mode 100644
index 0000000..6b6445b
--- /dev/null
+++ b/src/Navigator.Extensions.Store.Telegram/NavigatorExtensionConfigurationExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Navigator.Configuration;
+using Navigator.Configuration.Extension;
+using Navigator.Extensions.Store.Extractors;
+using Navigator.Extensions.Store.Telegram.Extractors;
+
+namespace Navigator.Extensions.Store.Telegram;
+
+public static class NavigatorExtensionConfigurationExtensions
+{
+ public static NavigatorConfiguration StoreForTelegram(this NavigatorExtensionConfiguration extensionConfiguration, Action? dbContextOptions = default)
+ {
+ return extensionConfiguration.Extension(configuration =>
+ {
+ configuration.Services.AddSingleton();
+ configuration.Services.AddSingleton();
+ configuration.Services.AddSingleton();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Bundled/Extensions/NavigatorContextExtensions.cs b/src/Navigator.Extensions.Store/Bundled/Extensions/NavigatorContextExtensions.cs
new file mode 100644
index 0000000..30bc9d1
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Bundled/Extensions/NavigatorContextExtensions.cs
@@ -0,0 +1,25 @@
+using Navigator.Context;
+
+namespace Navigator.Extensions.Store.Bundled.Extensions;
+
+public static class NavigatorContextExtensions
+{
+ public static INavigatorStore GetStore(this INavigatorContext navigatorContext)
+ {
+ var store = navigatorContext.GetStoreOrDefault();
+
+ return store ?? throw new NavigatorException("Store was not found.");
+ }
+
+ public static INavigatorStore? GetStoreOrDefault(this INavigatorContext navigatorContext)
+ {
+ var @store = navigatorContext.Extensions.GetValueOrDefault(StoreContextExtension.NavigatorStoreKey);
+
+ if (store is INavigatorStore navigatorStore)
+ {
+ return navigatorStore;
+ }
+
+ return default;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Bundled/StoreContextExtensions.cs b/src/Navigator.Extensions.Store/Bundled/StoreContextExtensions.cs
new file mode 100644
index 0000000..41bf192
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Bundled/StoreContextExtensions.cs
@@ -0,0 +1,23 @@
+using Navigator.Context;
+using Navigator.Context.Extensions;
+
+namespace Navigator.Extensions.Store.Bundled;
+
+internal class StoreContextExtension : INavigatorContextExtension
+{
+ public const string NavigatorStoreKey = "_navigator.extensions.store.navigator_store";
+
+ private readonly INavigatorStore _navigatorStore;
+
+ public StoreContextExtension(INavigatorStore navigatorStore)
+ {
+ _navigatorStore = navigatorStore;
+ }
+
+ public Task Extend(INavigatorContext navigatorContext, INavigatorContextBuilderOptions builderOptions)
+ {
+ navigatorContext.Extensions.TryAdd(NavigatorStoreKey, _navigatorStore);
+
+ return Task.FromResult(navigatorContext);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Bundled/StoreConversationContextExtension.cs b/src/Navigator.Extensions.Store/Bundled/StoreConversationContextExtension.cs
new file mode 100644
index 0000000..2f5dde7
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Bundled/StoreConversationContextExtension.cs
@@ -0,0 +1,119 @@
+using Microsoft.EntityFrameworkCore;
+using Navigator.Context;
+using Navigator.Context.Extensions;
+using Navigator.Extensions.Store.Context;
+using Navigator.Extensions.Store.Entities;
+using Navigator.Extensions.Store.Extractors;
+
+namespace Navigator.Extensions.Store.Bundled;
+
+internal class StoreConversationContextExtension : INavigatorContextExtension
+{
+ public const string UniversalConversation = "_navigator.extensions.store.source";
+
+ private readonly NavigatorDbContext _dbContext;
+ private readonly IEnumerable _dataExtractors;
+
+ public StoreConversationContextExtension(NavigatorDbContext dbContext, IEnumerable dataExtractors)
+ {
+ _dbContext = dbContext;
+ _dataExtractors = dataExtractors;
+ }
+
+ public async Task Extend(INavigatorContext navigatorContext, INavigatorContextBuilderOptions builderOptions)
+ {
+ if (await _dbContext.Conversations.AnyAsync(e => e.Id == navigatorContext.Conversation.Id))
+ {
+ return navigatorContext;
+ }
+
+ var user = await TryStoreUserAsync(navigatorContext.Conversation);
+ var chat = await TryStoreChatAsync(navigatorContext.Conversation);
+ await TryStoreConversationAsync(navigatorContext.Conversation, user, chat);
+
+ await _dbContext.SaveChangesAsync();
+
+ return navigatorContext;
+ }
+
+ private async Task TryStoreUserAsync(Navigator.Entities.Conversation source)
+ {
+ if (await _dbContext.Users.AnyAsync(user => user.Id == source.User.Id)) return default;
+
+ var user = new User
+ {
+ Id = source.User.Id
+ };
+
+ var data = _dataExtractors
+ .FirstOrDefault(extractor => extractor.Maps(source.User.GetType()))?
+ .From(source);
+
+ if (data is not null)
+ {
+ foreach (var pair in data)
+ {
+ user.Data.Add(pair);
+ }
+ }
+
+ await _dbContext.Users.AddAsync(user);
+
+ return user;
+ }
+
+ private async Task TryStoreChatAsync(Navigator.Entities.Conversation source)
+ {
+ if (source.Chat is null) return default;
+
+ if (await _dbContext.Chats.AnyAsync(chat => chat.Id == source.Chat.Id)) return default;
+
+ var chat = new Chat
+ {
+ Id = source.Chat.Id
+ };
+
+ var data = _dataExtractors
+ .FirstOrDefault(extractor => extractor.Maps(source.Chat.GetType()))?
+ .From(source);
+
+ if (data is not null)
+ {
+ foreach (var pair in data)
+ {
+ chat.Data.Add(pair);
+ }
+ }
+
+ await _dbContext.Chats.AddAsync(chat);
+
+ return chat;
+
+ }
+
+ private async Task TryStoreConversationAsync(Navigator.Entities.Conversation source, User? user, Chat? chat)
+ {
+ if (user is null || chat is null) return;
+
+ var conversation = new Conversation(user, chat)
+ {
+ Id = source.Id,
+ User = user,
+ Chat = chat
+ };
+
+ var data = _dataExtractors
+ .FirstOrDefault(extractor => extractor.Maps(source.GetType()))?
+ .From(source);
+
+ if (data is not null)
+ {
+ foreach (var pair in data)
+ {
+ chat.Data.Add(pair);
+ }
+ }
+
+ await _dbContext.Conversations.AddAsync(conversation);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Configuration/ChatEntityTypeConfiguration.cs b/src/Navigator.Extensions.Store/Context/Configuration/ChatEntityTypeConfiguration.cs
new file mode 100644
index 0000000..696e9df
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Context/Configuration/ChatEntityTypeConfiguration.cs
@@ -0,0 +1,33 @@
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Navigator.Extensions.Store.Entities;
+
+namespace Navigator.Extensions.Store.Context.Configuration;
+
+public class ChatEntityTypeConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(e => e.Id);
+
+ builder.HasMany(e => e.Users)
+ .WithMany(e => e.Chats)
+ .UsingEntity(
+ e => e.HasOne(e => e.User)
+ .WithMany(e => e.Conversations),
+ e=> e.HasOne(e => e.Chat)
+ .WithMany(e => e.Conversations));
+
+ builder.Property(e => e.Data)
+ .HasConversion(
+ dictionary => JsonSerializer.Serialize(dictionary, default(JsonSerializerOptions)),
+ json => JsonSerializer.Deserialize>(json, default(JsonSerializerOptions))
+ ?? new Dictionary(),
+ ValueComparer.CreateDefault(typeof(IDictionary), false));
+
+ builder.Property(e => e.FirstInteractionAt)
+ .IsRequired();
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Configuration/ConversationEntityTypeConfiguration.cs b/src/Navigator.Extensions.Store/Context/Configuration/ConversationEntityTypeConfiguration.cs
index 0008eed..2193bf9 100644
--- a/src/Navigator.Extensions.Store/Context/Configuration/ConversationEntityTypeConfiguration.cs
+++ b/src/Navigator.Extensions.Store/Context/Configuration/ConversationEntityTypeConfiguration.cs
@@ -1,24 +1,23 @@
-using Microsoft.EntityFrameworkCore;
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using Navigator.Extensions.Store.Abstractions.Entity;
+using Navigator.Extensions.Store.Entities;
-namespace Navigator.Extensions.Store.Context.Configuration
+namespace Navigator.Extensions.Store.Context.Configuration;
+
+public class ConversationEntityTypeConfiguration : IEntityTypeConfiguration
{
- ///
- public class ConversationEntityTypeConfiguration : IEntityTypeConfiguration
+ public void Configure(EntityTypeBuilder builder)
{
- ///
- public void Configure(EntityTypeBuilder builder)
- {
- builder.HasKey(e => new {e.ChatId, e.UserId});
-
- builder.HasOne(e => e.User)
- .WithMany(e => e.Conversations)
- .HasForeignKey(e => e.UserId);
-
- builder.HasOne(e => e.Chat)
- .WithMany(e => e.Conversations)
- .HasForeignKey(e => e.ChatId);
- }
+ builder.Property(e => e.Data)
+ .HasConversion(
+ dictionary => JsonSerializer.Serialize(dictionary, default(JsonSerializerOptions)),
+ json => JsonSerializer.Deserialize>(json, default(JsonSerializerOptions))
+ ?? new Dictionary(),
+ ValueComparer.CreateDefault(typeof(IDictionary), false));
+
+ builder.Property(e => e.FirstInteractionAt)
+ .IsRequired();
}
}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Configuration/UserEntityTypeConfiguration.cs b/src/Navigator.Extensions.Store/Context/Configuration/UserEntityTypeConfiguration.cs
new file mode 100644
index 0000000..ddb07c8
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Context/Configuration/UserEntityTypeConfiguration.cs
@@ -0,0 +1,33 @@
+using System.Text.Json;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Navigator.Extensions.Store.Entities;
+
+namespace Navigator.Extensions.Store.Context.Configuration;
+
+public class UserEntityTypeConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(e => e.Id);
+
+ builder.HasMany(e => e.Chats)
+ .WithMany(e => e.Users)
+ .UsingEntity(
+ e => e.HasOne(e => e.Chat)
+ .WithMany(e => e.Conversations),
+ e=> e.HasOne(e => e.User)
+ .WithMany(e => e.Conversations));
+
+ builder.Property(e => e.Data)
+ .HasConversion(
+ dictionary => JsonSerializer.Serialize(dictionary, default(JsonSerializerOptions)),
+ json => JsonSerializer.Deserialize>(json, default(JsonSerializerOptions))
+ ?? new Dictionary(),
+ ValueComparer.CreateDefault(typeof(IDictionary), false));
+
+ builder.Property(e => e.FirstInteractionAt)
+ .IsRequired();
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Extension/DbContextOptionsBuilderExtensions.cs b/src/Navigator.Extensions.Store/Context/Extension/DbContextOptionsBuilderExtensions.cs
new file mode 100644
index 0000000..0bccdb5
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Context/Extension/DbContextOptionsBuilderExtensions.cs
@@ -0,0 +1,13 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace Navigator.Extensions.Store.Context.Extension;
+
+public static class DbContextOptionsBuilderExtensions
+{
+ public static void UsingStoreExtension(this DbContextOptionsBuilder builder)
+ where TExtension : class, IDbContextOptionsExtension, new()
+ {
+ ((IDbContextOptionsBuilderInfrastructure)builder).AddOrUpdateExtension(new TExtension());
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtension.cs b/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtension.cs
new file mode 100644
index 0000000..b739743
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtension.cs
@@ -0,0 +1,23 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Navigator.Extensions.Store.Context.Extension;
+
+public abstract class NavigatorStoreModelExtension : IDbContextOptionsExtension
+{
+ public Action? Extension { get; protected set; }
+ public Action? ExtensionServices { get; protected set; }
+
+ public void ApplyServices(IServiceCollection services)
+ {
+
+ }
+
+ public void Validate(IDbContextOptions options)
+ {
+
+ }
+
+ public abstract DbContextOptionsExtensionInfo Info { get; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtensionInfo.cs b/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtensionInfo.cs
new file mode 100644
index 0000000..860a17e
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Context/Extension/NavigatorStoreModelExtensionInfo.cs
@@ -0,0 +1,30 @@
+using Microsoft.EntityFrameworkCore.Infrastructure;
+
+namespace Navigator.Extensions.Store.Context.Extension;
+
+public abstract class NavigatorStoreModelExtensionInfo : DbContextOptionsExtensionInfo
+{
+ public NavigatorStoreModelExtensionInfo(IDbContextOptionsExtension extension) : base(extension)
+ {
+ }
+
+ public override int GetServiceProviderHashCode()
+ {
+
+ var hashCode = new HashCode();
+
+ return hashCode.ToHashCode();
+ }
+
+ public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
+ {
+ return other is NavigatorStoreModelExtensionInfo;
+ }
+
+ public override void PopulateDebugInfo(IDictionary debugInfo)
+ {
+ }
+
+ public override bool IsDatabaseProvider { get; }
+ public override string LogFragment { get; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Context/NavigatorDbContext.cs b/src/Navigator.Extensions.Store/Context/NavigatorDbContext.cs
index 92ccfab..e079a3f 100644
--- a/src/Navigator.Extensions.Store/Context/NavigatorDbContext.cs
+++ b/src/Navigator.Extensions.Store/Context/NavigatorDbContext.cs
@@ -1,38 +1,45 @@
-using Microsoft.EntityFrameworkCore;
-using Navigator.Extensions.Store.Abstractions.Entity;
+using Microsoft.EntityFrameworkCore;
using Navigator.Extensions.Store.Context.Configuration;
+using Navigator.Extensions.Store.Context.Extension;
+using Chat = Navigator.Extensions.Store.Entities.Chat;
+using Conversation = Navigator.Extensions.Store.Entities.Conversation;
+using User = Navigator.Extensions.Store.Entities.User;
-namespace Navigator.Extensions.Store.Context
+namespace Navigator.Extensions.Store.Context;
+
+public class NavigatorDbContext : DbContext
{
- public class NavigatorDbContext : NavigatorDbContext
+ public DbSet Users { get; set; }
+ public DbSet Chats { get; set; }
+ public DbSet Conversations { get; set; }
+
+ protected NavigatorDbContext()
{
- public NavigatorDbContext(DbContextOptions options) : base(options)
- {
- }
}
-
- public class NavigatorDbContext : DbContext
- where TUser : User
- where TChat : Chat
- {
- public DbSet Users { get; set; }
- public DbSet Chats { get; set; }
- public DbSet Conversations { get; set; }
- protected NavigatorDbContext()
- {
- }
+ private readonly IList> _entityTypeConfigurations = new List>();
- public NavigatorDbContext(DbContextOptions options) : base(options)
+ public NavigatorDbContext(DbContextOptions options) : base(options)
+ {
+ foreach (var extension in options.Extensions
+ .OfType()
+ .Select(e => e.Extension))
{
+ if (extension is not null) _entityTypeConfigurations.Add(extension);
}
+ }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- base.OnModelCreating(modelBuilder);
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
- modelBuilder.ApplyConfiguration(new ConversationEntityTypeConfiguration());
+ modelBuilder.ApplyConfiguration(new ChatEntityTypeConfiguration());
+ modelBuilder.ApplyConfiguration(new UserEntityTypeConfiguration());
+ modelBuilder.ApplyConfiguration(new ConversationEntityTypeConfiguration());
+ foreach (var entityTypeConfiguration in _entityTypeConfigurations)
+ {
+ entityTypeConfiguration.Invoke(modelBuilder);
}
}
}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/DefaultChatMapper.cs b/src/Navigator.Extensions.Store/DefaultChatMapper.cs
deleted file mode 100644
index 5396606..0000000
--- a/src/Navigator.Extensions.Store/DefaultChatMapper.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Store
-{
- public class DefaultChatMapper : IChatMapper
- {
- public Chat Parse(Telegram.Bot.Types.Chat chat)
- {
- return new Chat
- {
- Id = chat.Id,
- Username = chat.Username,
- Title = chat.Title,
- Type = Enum.Parse(chat.Type.ToString())
- };
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/DefaultEntityManager.cs b/src/Navigator.Extensions.Store/DefaultEntityManager.cs
deleted file mode 100644
index 128ac1a..0000000
--- a/src/Navigator.Extensions.Store/DefaultEntityManager.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.Logging;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-using Navigator.Extensions.Store.Context;
-using Telegram.Bot.Types;
-using Chat = Navigator.Extensions.Store.Abstractions.Entity.Chat;
-using User = Navigator.Extensions.Store.Abstractions.Entity.User;
-
-namespace Navigator.Extensions.Store
-{
- public class DefaultEntityManager : IEntityManager where TContext : NavigatorDbContext
- where TUser : User
- where TChat : Chat
- {
- private readonly ILogger> _logger;
- private readonly TContext _navigatorDbContext;
- private readonly IUserMapper _userMapper;
- private readonly IChatMapper _chatMapper;
-
- public DefaultEntityManager(ILogger> logger, TContext navigatorDbContext,
- IUserMapper userMapper, IChatMapper chatMapper)
- {
- _logger = logger;
- _navigatorDbContext = navigatorDbContext;
- _userMapper = userMapper;
- _chatMapper = chatMapper;
- }
-
- public async Task Handle(Telegram.Bot.Types.User telegramUser, CancellationToken cancellationToken = default)
- {
- _logger.LogDebug("Handling user {@UserId}", telegramUser.Id);
-
- var user = await _navigatorDbContext.Users.Include(u => u.Conversations)
- .FirstOrDefaultAsync(u => u.Id == telegramUser.Id, cancellationToken);
-
- if (user == null)
- {
- user = _userMapper.Parse(telegramUser);
-
- await _navigatorDbContext.AddAsync(user, cancellationToken);
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
- }
-
- public async Task Handle(Telegram.Bot.Types.User telegramUser, Telegram.Bot.Types.Chat telegramChat,
- CancellationToken cancellationToken = default)
- {
- _logger.LogDebug("Handling user {@UserId} and chat {@ChatId}", telegramUser.Id, telegramChat.Id);
-
- var userIsNew = false;
- var chatIsNew = false;
- var conversationIsNew = false;
-
- var user = await _navigatorDbContext.Users.Include(u => u.Conversations)
- .FirstOrDefaultAsync(u => u.Id == telegramUser.Id, cancellationToken);
-
- var chat = await _navigatorDbContext.Chats.Include(u => u.Conversations)
- .FirstOrDefaultAsync(u => u.Id == telegramChat.Id, cancellationToken);
-
- if (user == null)
- {
- userIsNew = true;
- user = _userMapper.Parse(telegramUser);
- await _navigatorDbContext.Users.AddAsync(user, cancellationToken);
- }
-
- if (chat == null)
- {
- chatIsNew = true;
- chat = _chatMapper.Parse(telegramChat);
- await _navigatorDbContext.Chats.AddAsync(chat, cancellationToken);
- }
-
- if (chat.Conversations.FirstOrDefault(cv => cv.UserId == user.Id) == null)
- {
- conversationIsNew = true;
- var conversation = new Conversation
- {
- UserId = user.Id,
- ChatId = chat.Id
- };
- await _navigatorDbContext.Conversations.AddAsync(conversation, cancellationToken);
- }
-
- if (userIsNew || chatIsNew || conversationIsNew)
- {
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
- }
-
- public TUser FindUser(int id)
- {
- return _navigatorDbContext.Users.Find(id);
- }
-
- public async Task FindUserAsync(int id, CancellationToken cancellationToken = default)
- {
- return await _navigatorDbContext.Users.FindAsync(id);
- }
-
- public IEnumerable FindAllUsersAsync(CancellationToken cancellationToken = default)
- {
- return _navigatorDbContext.Users.AsEnumerable();
- }
-
- public TChat FindChat(long id)
- {
- return _navigatorDbContext.Chats.Find(id);
- }
-
- public async Task FindChatAsync(long id, CancellationToken cancellationToken = default)
- {
- return await _navigatorDbContext.Chats.FindAsync(id);
- }
-
- public IEnumerable FindAllChatsAsync(CancellationToken cancellationToken = default)
- {
- return _navigatorDbContext.Chats.AsEnumerable();
- }
-
- public async Task MigrateFromGroup(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default)
- {
- var chat = await FindChatAsync(telegramMessage.Chat.Id, cancellationToken);
-
- chat.Id = telegramMessage.MigrateToChatId;
- chat.Type = Chat.ChatType.Supergroup;
-
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
-
- public async Task MigrateToSupergroup(Telegram.Bot.Types.Message telegramMessage, CancellationToken cancellationToken = default)
- {
- var chat = await FindChatAsync(telegramMessage.MigrateFromChatId, cancellationToken);
-
- chat.Id = telegramMessage.Chat.Id;
- chat.Type = Chat.ChatType.Supergroup;
-
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
-
- public async Task ChatTitleChanged(Message telegramMessage, CancellationToken cancellationToken = default)
- {
- var chat = await FindChatAsync(telegramMessage.MigrateFromChatId, cancellationToken);
-
- chat.Title = telegramMessage.NewChatTitle;
-
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
-
- public async Task ChatMemberLeft(Message telegramMessage, CancellationToken cancellationToken = default)
- {
- var conversation = await _navigatorDbContext.Conversations
- .Where(c => c.ChatId == telegramMessage.Chat.Id && c.UserId == telegramMessage.From.Id)
- .SingleOrDefaultAsync(cancellationToken);
-
- _navigatorDbContext.Remove(conversation);
-
- await _navigatorDbContext.SaveChangesAsync(cancellationToken);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/DefaultUserMapper.cs b/src/Navigator.Extensions.Store/DefaultUserMapper.cs
deleted file mode 100644
index 0d6f893..0000000
--- a/src/Navigator.Extensions.Store/DefaultUserMapper.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Store
-{
- public class DefaultUserMapper : IUserMapper
- {
- public User Parse(Telegram.Bot.Types.User user)
- {
- return new User
- {
- Id = user.Id,
- IsBot = user.IsBot,
- LanguageCode = user.LanguageCode,
- Username = user.Username
- };
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Entities/Chat.cs b/src/Navigator.Extensions.Store/Entities/Chat.cs
new file mode 100644
index 0000000..7c1fd57
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Entities/Chat.cs
@@ -0,0 +1,26 @@
+namespace Navigator.Extensions.Store.Entities;
+
+public class Chat : Navigator.Entities.Chat
+{
+ public Chat()
+ {
+ Users = new List();
+ Conversations = new List();
+ Data = new Dictionary();
+ FirstInteractionAt = DateTime.UtcNow;
+ }
+
+ ///
+ /// Users related to the chat.
+ ///
+ public ICollection Users { get; set; }
+
+ public ICollection Conversations { get; set; }
+
+ public IDictionary Data { get; set; }
+
+ ///
+ /// Date of first interaction for this chat.
+ ///
+ public DateTime FirstInteractionAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Entities/Conversation.cs b/src/Navigator.Extensions.Store/Entities/Conversation.cs
new file mode 100644
index 0000000..c7447b8
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Entities/Conversation.cs
@@ -0,0 +1,28 @@
+namespace Navigator.Extensions.Store.Entities;
+
+public class Conversation : Navigator.Entities.Conversation
+{
+ public Conversation()
+ {
+ Data = new Dictionary();
+ FirstInteractionAt = DateTime.UtcNow;
+ }
+ public Conversation(User user, Chat chat) : base(user, chat)
+ {
+ Data = new Dictionary();
+ FirstInteractionAt = DateTime.UtcNow;
+ }
+
+ // public new Guid Id { get; set; }
+
+ public new Chat Chat { get; set; }
+
+ public new User User { get; set; }
+
+ public IDictionary Data { get; set; }
+
+ ///
+ /// Date of first interaction for this chat.
+ ///
+ public DateTime FirstInteractionAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Entities/User.cs b/src/Navigator.Extensions.Store/Entities/User.cs
new file mode 100644
index 0000000..d61d58d
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Entities/User.cs
@@ -0,0 +1,26 @@
+namespace Navigator.Extensions.Store.Entities;
+
+public class User : Navigator.Entities.User
+{
+ public User()
+ {
+ Chats = new List();
+ Conversations = new List();
+ Data = new Dictionary();
+ FirstInteractionAt = DateTime.UtcNow;
+ }
+
+ ///
+ /// Chats related to the user.
+ ///
+ public IList Chats { get; set; }
+
+ public ICollection Conversations { get; set; }
+
+ public IDictionary Data { get; set; }
+
+ ///
+ /// Date of first interaction for this chat.
+ ///
+ public DateTime FirstInteractionAt { get; set; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Extractors/IDataExtractor.cs b/src/Navigator.Extensions.Store/Extractors/IDataExtractor.cs
new file mode 100644
index 0000000..3b6d346
--- /dev/null
+++ b/src/Navigator.Extensions.Store/Extractors/IDataExtractor.cs
@@ -0,0 +1,9 @@
+using Navigator.Entities;
+
+namespace Navigator.Extensions.Store.Extractors;
+
+public interface IDataExtractor
+{
+ Dictionary From(Conversation source);
+ public bool Maps(Type type);
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/INavigatorStore.cs b/src/Navigator.Extensions.Store/INavigatorStore.cs
new file mode 100644
index 0000000..f6ddc28
--- /dev/null
+++ b/src/Navigator.Extensions.Store/INavigatorStore.cs
@@ -0,0 +1,21 @@
+using Navigator.Entities;
+
+namespace Navigator.Extensions.Store;
+
+public interface INavigatorStore
+{
+ public IDictionary? GetAllData(User user);
+ public IDictionary? GetAllData(Chat chat);
+ public Task?> GetAllDataAsync(User user, CancellationToken cancellationToken = default);
+ public Task?> GetAllDataAsync(Chat chat, CancellationToken cancellationToken = default);
+
+ public string? TryGetData(User user, string key);
+ public string? TryGetData(Chat chat, string key);
+ public Task TryGetDataAsync(User user, string key, CancellationToken cancellationToken = default);
+ public Task TryGetDataAsync(Chat chat, string key, CancellationToken cancellationToken = default);
+
+ public bool TryAddData(User user, string key, object data, bool force = false);
+ public bool TryAddData(Chat chat, string key, object data, bool force = false);
+ public Task TryAddDataAsync(User user, string key, object data, bool force = false, CancellationToken cancellationToken = default);
+ public Task TryAddDataAsync(Chat chat, string key, object data, bool force = false, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Navigator.Extensions.Store.csproj b/src/Navigator.Extensions.Store/Navigator.Extensions.Store.csproj
index b5e6ecc..ddfeffc 100644
--- a/src/Navigator.Extensions.Store/Navigator.Extensions.Store.csproj
+++ b/src/Navigator.Extensions.Store/Navigator.Extensions.Store.csproj
@@ -1,42 +1,32 @@
- net5.0
+ net6.0
+ true
enable
- true
true
- 0.9.100-beta
- Navigator.Extensions.Store
- Navigator Extensions Store
+ Navigator.Extensions.Store
Lucas Maximiliano Marino
- Store extension for Navigator Framework.
+ Persistence extension for Navigator Framework bots, requires provider implementations.
https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
https://github.com/navigatorframework/navigator
- Telegram, Bot, Framework, Navigator, EF Core, Store, Database, Extension
- true
- Copyright © Lucas Maximiliano Marino 2021
https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
+ Store, Bot, Extension, Navigator
+ true
+ AGPL-3.0-only
+ Copyright © Lucas Maximiliano Marino 2022
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
+
+
-
-
-
-
-
diff --git a/src/Navigator.Extensions.Store/NavigatorExtensionConfigurationExtensions.cs b/src/Navigator.Extensions.Store/NavigatorExtensionConfigurationExtensions.cs
new file mode 100644
index 0000000..fb3adfa
--- /dev/null
+++ b/src/Navigator.Extensions.Store/NavigatorExtensionConfigurationExtensions.cs
@@ -0,0 +1,36 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Navigator.Configuration;
+using Navigator.Configuration.Extension;
+using Navigator.Context.Extensions;
+using Navigator.Extensions.Store.Bundled;
+using Navigator.Extensions.Store.Context;
+using Navigator.Extensions.Store.Context.Extension;
+
+namespace Navigator.Extensions.Store;
+
+public static class NavigatorExtensionConfigurationExtensions
+{
+ public static NavigatorConfiguration Store(this NavigatorExtensionConfiguration extensionConfiguration, Action? dbContextOptions = default)
+ {
+ var temporal = new DbContextOptionsBuilder();
+
+ dbContextOptions?.Invoke(temporal);
+
+ return extensionConfiguration.Extension(configuration =>
+ {
+
+ configuration.Services.AddDbContext(dbContextOptions);
+
+ configuration.Services.AddScoped();
+ configuration.Services.AddScoped();
+
+ configuration.Services.AddScoped();
+
+ foreach (var extension in temporal.Options.Extensions.OfType())
+ {
+ extension.ExtensionServices?.Invoke(configuration.Services);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/NavigatorOptionsCollectionExtensions.cs b/src/Navigator.Extensions.Store/NavigatorOptionsCollectionExtensions.cs
deleted file mode 100644
index 84a4e7d..0000000
--- a/src/Navigator.Extensions.Store/NavigatorOptionsCollectionExtensions.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-using System;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-
-namespace Navigator.Extensions.Store
-{
- public static class NavigatorOptionsCollectionExtensions
- {
- #region UserMapper
-
- private const string UserTypeKey = "_navigator.extensions.store.options.user_type";
-
- public static void SetUserType(this INavigatorOptions navigatorOptions) where TUser : User
- {
- navigatorOptions.TryRegisterOption(UserTypeKey, typeof(TUser));
- }
-
- public static Type? GetUserType(this INavigatorOptions navigatorOptions)
- {
- return navigatorOptions.RetrieveOption(UserTypeKey);
- }
-
- #endregion
-
- #region ChatMapper
-
- private const string ChatTypeKey = "_navigator.extensions.store.options.chat_type";
-
- public static void SetChatType(this INavigatorOptions navigatorOptions)
- {
- navigatorOptions.TryRegisterOption(ChatTypeKey, typeof(TChat));
- }
-
- public static Type? GetChatType(this INavigatorOptions navigatorOptions)
- {
- return navigatorOptions.RetrieveOption(ChatTypeKey);
- }
-
- #endregion
-
- #region UserMapper
-
- private const string UserMapperKey = "_navigator.extensions.store.options.user_mapper";
-
- public static void SetUserMapper(this INavigatorOptions navigatorOptions)
- {
- navigatorOptions.ForceRegisterOption(UserMapperKey, typeof(TUserMapper));
- }
-
- public static Type? GetUserMapper(this INavigatorOptions navigatorOptions)
- {
- return navigatorOptions.RetrieveOption(UserMapperKey);
- }
-
- #endregion
-
- #region ChatMapper
-
- private const string ChatMapperKey = "_navigator.extensions.store.options.chat_mapper";
-
- public static void SetChatMapper(this INavigatorOptions navigatorOptions)
- {
- navigatorOptions.ForceRegisterOption(ChatMapperKey, typeof(TChatMapper));
- }
-
- public static Type? GetChatMapper(this INavigatorOptions navigatorOptions)
- {
- return navigatorOptions.RetrieveOption(ChatMapperKey);
- }
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/NavigatorServiceCollectionExtensions.cs b/src/Navigator.Extensions.Store/NavigatorServiceCollectionExtensions.cs
deleted file mode 100644
index 2ae9024..0000000
--- a/src/Navigator.Extensions.Store/NavigatorServiceCollectionExtensions.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.DependencyInjection.Extensions;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Entity;
-using Navigator.Extensions.Store.Context;
-using Navigator.Extensions.Store.Provider;
-
- namespace Navigator.Extensions.Store
-{
- public static class NavigatorServiceCollectionExtensions
- {
- #region Default Implementation
-
- public static NavigatorBuilder AddNavigatorStore(this NavigatorBuilder navigatorBuilder, Action dbContextOptions = default,
- Action options = default)
- {
- navigatorBuilder.Options.SetUserMapper();
- navigatorBuilder.Options.SetChatMapper();
-
- navigatorBuilder.AddNavigatorStore(dbContextOptions, options);
-
- return navigatorBuilder;
- }
-
- #endregion
-
- public static NavigatorBuilder AddNavigatorStore(this NavigatorBuilder navigatorBuilder, Action dbContextOptions = default,
- Action options = default)
- where TContext : NavigatorDbContext
- where TUser : User
- {
- navigatorBuilder.Options.SetChatMapper();
-
- return navigatorBuilder.AddNavigatorStore(dbContextOptions, options);
- }
-
- public static NavigatorBuilder AddNavigatorStore(this NavigatorBuilder navigatorBuilder, Action? dbContextOptions = default,
- Action? options = default)
- where TContext : NavigatorDbContext
- where TUser : User
- where TChat : Chat
- {
- navigatorBuilder.Options.SetUserType();
- navigatorBuilder.Options.SetChatType();
-
- options?.Invoke(navigatorBuilder.Options);
-
- navigatorBuilder.Services.AddDbContext(dbContextOptions);
-
- RegisterUserMapper(navigatorBuilder);
-
- RegisterChatMapper(navigatorBuilder);
-
- navigatorBuilder.Services.AddScoped, DefaultEntityManager>();
- navigatorBuilder.Services.TryAddEnumerable(ServiceDescriptor.Scoped>());
- navigatorBuilder.Services.TryAddEnumerable(ServiceDescriptor.Scoped>());
- navigatorBuilder.Services.TryAddEnumerable(ServiceDescriptor.Scoped>());
-
- navigatorBuilder.RegisterOrReplaceOptions();
-
- return navigatorBuilder;
- }
-
- private static void RegisterUserMapper(NavigatorBuilder navigatorBuilder) where TUser : User
- {
- if (navigatorBuilder.Options.GetUserMapper() is not null && typeof(IUserMapper).IsAssignableFrom(navigatorBuilder.Options.GetUserMapper()))
- {
- navigatorBuilder.Services.AddScoped(typeof(IUserMapper), navigatorBuilder.Options.GetUserMapper()!);
- }
- else
- {
- throw new ArgumentException("TODO");
- }
- }
-
- private static void RegisterChatMapper(NavigatorBuilder navigatorBuilder) where TChat : Chat
- {
- if (navigatorBuilder.Options.GetChatMapper() is not null && typeof(IChatMapper).IsAssignableFrom(navigatorBuilder.Options.GetChatMapper()))
- {
- navigatorBuilder.Services.AddScoped(typeof(IChatMapper), navigatorBuilder.Options.GetChatMapper()!);
- }
- else
- {
- throw new ArgumentException("TODO");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/NavigatorStore.cs b/src/Navigator.Extensions.Store/NavigatorStore.cs
new file mode 100644
index 0000000..76ed7a6
--- /dev/null
+++ b/src/Navigator.Extensions.Store/NavigatorStore.cs
@@ -0,0 +1,240 @@
+using System.Text.Json;
+using Navigator.Entities;
+using Navigator.Extensions.Store.Context;
+
+namespace Navigator.Extensions.Store;
+
+public class NavigatorStore : INavigatorStore
+{
+ private readonly NavigatorDbContext _dbContext;
+
+ ///
+ /// Default constructor.
+ ///
+ ///
+ public NavigatorStore(NavigatorDbContext dbContext)
+ {
+ _dbContext = dbContext;
+ }
+
+ ///
+ public IDictionary? GetAllData(User user)
+ {
+ return _dbContext.Users.Find(user.Id)?.Data;
+ }
+
+ ///
+ public IDictionary? GetAllData(Chat chat)
+ {
+ return _dbContext.Users.Find(chat.Id)?.Data;
+ }
+
+ ///
+ public async Task?> GetAllDataAsync(User user, CancellationToken cancellationToken = default)
+ {
+ return (await _dbContext.Users.FindAsync(new object?[] { user.Id }, cancellationToken))?.Data;
+ }
+
+ ///
+ public async Task?> GetAllDataAsync(Chat chat, CancellationToken cancellationToken = default)
+ {
+ return (await _dbContext.Chats.FindAsync(new object?[] { chat?.Id }, cancellationToken))?.Data;
+ }
+
+ ///
+ public string? TryGetData(User user, string key)
+ {
+ var data = GetAllData(user);
+
+ if (data?.TryGetValue(key, out var value) is not null)
+ {
+ return value;
+ }
+
+ return default;
+ }
+
+ public string? TryGetData(Chat chat, string key)
+ {
+ var data = GetAllData(chat);
+
+ if (data?.TryGetValue(key, out var value) is not null)
+ {
+ return value;
+ }
+
+ return default;
+ }
+
+ ///
+ public async Task TryGetDataAsync(User user, string key, CancellationToken cancellationToken = default)
+ {
+ var data = await GetAllDataAsync(user, cancellationToken);
+
+ if (data?.TryGetValue(key, out var value) is not null)
+ {
+ return value;
+ }
+
+ return default;
+ }
+
+ ///
+ public async Task TryGetDataAsync(Chat chat, string key, CancellationToken cancellationToken = default)
+ {
+ var data = await GetAllDataAsync(chat, cancellationToken);
+
+ if (data?.TryGetValue(key, out var value) is not null)
+ {
+ return value;
+ }
+
+ return default;
+ }
+
+ ///
+ public bool TryAddData(User user, string key, object data, bool force = false)
+ {
+ var storedUser = _dbContext.Users.Find(user.Id);
+
+ if (storedUser is null) return false;
+
+ try
+ {
+ switch (force)
+ {
+ case true:
+ if (storedUser.Data.ContainsKey(key)) storedUser.Data.Remove(key);
+ return TryAddKeyValue();
+ case false:
+ return TryAddKeyValue();
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ bool TryAddKeyValue()
+ {
+ if (!storedUser.Data.TryAdd(key, JsonSerializer.Serialize(data)))
+ {
+ return false;
+ }
+
+ _dbContext.SaveChanges();
+
+ return true;
+ }
+ }
+
+ ///
+ public bool TryAddData(Chat chat, string key, object data, bool force = false)
+ {
+ var storedChat = _dbContext.Chats.Find(chat.Id);
+
+ if (storedChat is null) return false;
+
+ try
+ {
+ switch (force)
+ {
+ case true:
+ if (storedChat.Data.ContainsKey(key)) storedChat.Data.Remove(key);
+ return TryAddKeyValue();
+ case false:
+ return TryAddKeyValue();
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ bool TryAddKeyValue()
+ {
+ if (!storedChat.Data.TryAdd(key, JsonSerializer.Serialize(data)))
+ {
+ return false;
+ }
+
+ _dbContext.SaveChanges();
+
+ return true;
+ }
+ }
+
+ ///
+ public async Task TryAddDataAsync(User user, string key, object data, bool force = false, CancellationToken cancellationToken = default)
+ {
+ var storedUser = await _dbContext.Chats.FindAsync(new object?[] { user.Id }, cancellationToken);
+
+ if (storedUser is null) return false;
+
+ try
+ {
+ switch (force)
+ {
+ case true:
+ if (storedUser.Data.ContainsKey(key)) storedUser.Data.Remove(key);
+ return await TryAddKeyValueAsync();
+ case false:
+ return await TryAddKeyValueAsync();
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+
+ async Task TryAddKeyValueAsync()
+ {
+ if (!storedUser.Data.TryAdd(key, JsonSerializer.Serialize(data)))
+ {
+ return false;
+ }
+
+ await _dbContext.SaveChangesAsync(cancellationToken);
+
+ return true;
+ }
+ }
+
+ ///
+ public async Task TryAddDataAsync(Chat chat, string key, object data, bool force = false, CancellationToken cancellationToken = default)
+ {
+ var storedChat = await _dbContext.Chats.FindAsync(new object?[] { chat.Id }, cancellationToken);
+
+ if (storedChat is null) return false;
+
+ try
+ {
+ switch (force)
+ {
+ case true:
+ if (storedChat.Data.ContainsKey(key)) storedChat.Data.Remove(key);
+ return await TryAddKeyValueAsync();
+ case false:
+ return await TryAddKeyValueAsync();
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+
+ async Task TryAddKeyValueAsync()
+ {
+ if (!storedChat.Data.TryAdd(key, JsonSerializer.Serialize(data)))
+ {
+ return false;
+ }
+
+ await _dbContext.SaveChangesAsync(cancellationToken);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/NavigatorStoreException.cs b/src/Navigator.Extensions.Store/NavigatorStoreException.cs
new file mode 100644
index 0000000..f180adc
--- /dev/null
+++ b/src/Navigator.Extensions.Store/NavigatorStoreException.cs
@@ -0,0 +1,22 @@
+using System.Runtime.Serialization;
+
+namespace Navigator.Extensions.Store;
+
+public class NavigatorStoreException : NavigatorException
+{
+ public NavigatorStoreException()
+ {
+ }
+
+ protected NavigatorStoreException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ public NavigatorStoreException(string? message) : base(message)
+ {
+ }
+
+ public NavigatorStoreException(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Provider/DefaultChatContextProvider.cs b/src/Navigator.Extensions.Store/Provider/DefaultChatContextProvider.cs
deleted file mode 100644
index d62e73a..0000000
--- a/src/Navigator.Extensions.Store/Provider/DefaultChatContextProvider.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Extensions;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.Enums;
-using Chat = Navigator.Extensions.Store.Abstractions.Entity.Chat;
-using User = Navigator.Extensions.Store.Abstractions.Entity.User;
-
-namespace Navigator.Extensions.Store.Provider
-{
- public class DefaultChatContextProvider : INavigatorContextExtensionProvider
- where TUser : User
- where TChat : Chat
- {
- public int Order => 500;
-
- private readonly ILogger> _logger;
- private readonly IEntityManager _entityManager;
-
- public DefaultChatContextProvider(ILogger> logger, IEntityManager entityManager)
- {
- _logger = logger;
- _entityManager = entityManager;
- }
-
- public async Task<(string?, object?)> Process(Update update)
- {
- _logger.LogDebug("Processing update {UpdateId} of type {UpdateType} from {Provider}", update.Id, update.Type.ToString(), nameof(DefaultChatContextProvider));
-
- TChat chat;
-
- switch (update.Type)
- {
- case UpdateType.Message:
- await _entityManager.Handle(update.Message.From, update.Message.Chat);
- chat = await _entityManager.FindChatAsync(update.Message.Chat.Id);
- break;
- case UpdateType.EditedMessage:
- await _entityManager.Handle(update.EditedMessage.From, update.EditedMessage.Chat);
- chat = await _entityManager.FindChatAsync(update.EditedMessage.Chat.Id);
- break;
- case UpdateType.ChannelPost:
- await _entityManager.Handle(update.ChannelPost.From, update.ChannelPost.Chat);
- chat = await _entityManager.FindChatAsync(update.ChannelPost.Chat.Id);
- break;
- case UpdateType.EditedChannelPost:
- await _entityManager.Handle(update.EditedChannelPost.From, update.EditedChannelPost.Chat);
- chat = await _entityManager.FindChatAsync(update.EditedChannelPost.Chat.Id);
- break;
- default:
- return default;
- }
-
- return (INavigatorContextExtensions.DefaultChatKey, chat);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Provider/DefaultUserContextProvider.cs b/src/Navigator.Extensions.Store/Provider/DefaultUserContextProvider.cs
deleted file mode 100644
index 3b41d3a..0000000
--- a/src/Navigator.Extensions.Store/Provider/DefaultUserContextProvider.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions;
-using Navigator.Extensions.Store.Abstractions.Extensions;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.Enums;
-using Chat = Navigator.Extensions.Store.Abstractions.Entity.Chat;
-using User = Navigator.Extensions.Store.Abstractions.Entity.User;
-
-namespace Navigator.Extensions.Store.Provider
-{
- public class DefaultUserContextProvider : INavigatorContextExtensionProvider
- where TUser : User
- where TChat : Chat
- {
- public int Order => 500;
-
- private readonly ILogger> _logger;
- private readonly IEntityManager _entityManager;
-
- public DefaultUserContextProvider(ILogger> logger, IEntityManager entityManager)
- {
- _logger = logger;
- _entityManager = entityManager;
- }
-
- public async Task<(string?, object?)> Process(Update update)
- {
- _logger.LogDebug("Processing update {UpdateId} of type {UpdateType} from {Provider}", update.Id, update.Type.ToString(), nameof(DefaultChatContextProvider));
-
- TUser user;
-
- switch (update.Type)
- {
- case UpdateType.Message:
- await _entityManager.Handle(update.Message.From, update.Message.Chat);
- user = await _entityManager.FindUserAsync(update.Message.From.Id);
- break;
- case UpdateType.InlineQuery:
- await _entityManager.Handle(update.InlineQuery.From);
- user = await _entityManager.FindUserAsync(update.InlineQuery.From.Id);
- break;
- case UpdateType.ChosenInlineResult:
- await _entityManager.Handle(update.ChosenInlineResult.From);
- user = await _entityManager.FindUserAsync(update.ChosenInlineResult.From.Id);
- break;
- case UpdateType.CallbackQuery:
- await _entityManager.Handle(update.CallbackQuery.From);
- user = await _entityManager.FindUserAsync(update.CallbackQuery.From.Id);
- break;
- case UpdateType.EditedMessage:
- await _entityManager.Handle(update.EditedMessage.From, update.EditedMessage.Chat);
- user = await _entityManager.FindUserAsync(update.EditedMessage.From.Id);
- break;
- case UpdateType.ChannelPost:
- await _entityManager.Handle(update.ChannelPost.From, update.ChannelPost.Chat);
- user = await _entityManager.FindUserAsync(update.ChannelPost.From.Id);
- break;
- case UpdateType.EditedChannelPost:
- await _entityManager.Handle(update.EditedChannelPost.From, update.EditedChannelPost.Chat);
- user = await _entityManager.FindUserAsync(update.EditedChannelPost.From.Id);
- break;
- case UpdateType.ShippingQuery:
- await _entityManager.Handle(update.ShippingQuery.From);
- user = await _entityManager.FindUserAsync(update.ShippingQuery.From.Id);
- break;
- case UpdateType.PreCheckoutQuery:
- await _entityManager.Handle(update.PreCheckoutQuery.From);
- user = await _entityManager.FindUserAsync(update.PreCheckoutQuery.From.Id);
- break;
- default:
- return default;
- }
-
- return (INavigatorContextExtensions.DefaultUserKey, user);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Extensions.Store/Provider/UpdateDataContextProvider.cs b/src/Navigator.Extensions.Store/Provider/UpdateDataContextProvider.cs
deleted file mode 100644
index d00af98..0000000
--- a/src/Navigator.Extensions.Store/Provider/UpdateDataContextProvider.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Navigator.Abstractions;
-using Navigator.Extensions.Store.Abstractions;
-using Telegram.Bot.Types;
-using Telegram.Bot.Types.Enums;
-using Chat = Navigator.Extensions.Store.Abstractions.Entity.Chat;
-using User = Navigator.Extensions.Store.Abstractions.Entity.User;
-
-namespace Navigator.Extensions.Store.Provider
-{
- public class UpdateDataContextProvider : INavigatorContextExtensionProvider
- where TUser : User
- where TChat : Chat
- {
- public int Order => 250;
-
- private readonly ILogger> _logger;
- private readonly IEntityManager _entityManager;
-
- public UpdateDataContextProvider(ILogger> logger, IEntityManager entityManager)
- {
- _logger = logger;
- _entityManager = entityManager;
- }
-
- public async Task<(string?, object?)> Process(Update update)
- {
- if (update.Type == UpdateType.Message || update.Type == UpdateType.ChannelPost)
- {
- switch (update.Message.Type)
- {
- case MessageType.ChatMembersAdded:
- await AddMembers(update);
- break;
- case MessageType.ChatMemberLeft:
- await _entityManager.ChatMemberLeft(update.Message);
- break;
- case MessageType.ChatTitleChanged:
- await _entityManager.ChatTitleChanged(update.Message);
- break;
- case MessageType.MigratedToSupergroup:
- await _entityManager.MigrateToSupergroup(update.Message);
- break;
- case MessageType.MigratedFromGroup:
- await _entityManager.MigrateFromGroup(update.Message);
- break;
- }
- }
-
- return default;
- }
-
- private async Task AddMembers(Update update)
- {
- foreach (var newUser in update.Message.NewChatMembers)
- {
- await _entityManager.Handle(newUser, update.Message.Chat);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/ActionHandlerExtensions.cs b/src/Navigator.Providers.Telegram/ActionHandlerExtensions.cs
new file mode 100644
index 0000000..2dcbdf6
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/ActionHandlerExtensions.cs
@@ -0,0 +1,17 @@
+using Navigator.Actions;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram;
+
+public static class ActionHandlerExtensions
+{
+ public static NavigatorTelegramClient GetTelegramClient(this ActionHandler actionHandler) where T : IAction
+ {
+ return actionHandler.NavigatorContext.Provider.GetTelegramClient();
+ }
+
+ public static Chat GetTelegramChat(this ActionHandler actionHandler) where T : IAction
+ {
+ return actionHandler.NavigatorContext.GetTelegramChat();
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Bundled/CommandAction.cs b/src/Navigator.Providers.Telegram/Actions/Bundled/CommandAction.cs
new file mode 100644
index 0000000..f4c0217
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Bundled/CommandAction.cs
@@ -0,0 +1,33 @@
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Providers.Telegram.Actions.Messages;
+using Navigator.Providers.Telegram.Entities;
+using Navigator.Providers.Telegram.Extensions;
+
+namespace Navigator.Providers.Telegram.Actions.Bundled;
+
+///
+/// Command based action.
+///
+[ActionType(nameof(CommandAction))]
+public abstract class CommandAction : MessageAction
+{
+ ///
+ /// Command.
+ ///
+ public readonly string Command;
+
+ ///
+ /// Any arguments passed with the command. If no arguments were passed, it will be null.
+ ///
+ public readonly string? Arguments;
+
+ ///
+ protected CommandAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var botProfile = NavigatorContextAccessor.NavigatorContext.BotProfile;
+
+ Command = Message.ExtractCommand((botProfile as TelegramBot)?.Username) ?? string.Empty;
+ Arguments = Message.ExtractArguments();
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Messages/MessageAction.cs b/src/Navigator.Providers.Telegram/Actions/Messages/MessageAction.cs
new file mode 100644
index 0000000..2480797
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Messages/MessageAction.cs
@@ -0,0 +1,39 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Messages;
+
+///
+/// A message based action.
+///
+[ActionType(nameof(MessageAction))]
+public abstract class MessageAction : BaseAction
+{
+ ///
+ /// The original Message.
+ ///
+ public Message Message { get; protected set; }
+
+ ///
+ /// Determines if this message is a reply to another message.
+ ///
+ public bool IsReply { get; protected set; }
+
+ ///
+ /// Determines if this message is a forwarded message.
+ ///
+ public bool IsForwarded { get; protected set; }
+
+ ///
+ protected MessageAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = navigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ Message = update.Message!;
+ IsReply = update.Message!.ReplyToMessage is not null;
+ IsForwarded = update.Message.ForwardDate is not null;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/UnknownAction.cs b/src/Navigator.Providers.Telegram/Actions/UnknownAction.cs
new file mode 100644
index 0000000..23a568b
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/UnknownAction.cs
@@ -0,0 +1,14 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions;
+
+[ActionType(nameof(UnknownAction))]
+public abstract class UnknownAction : BaseAction
+{
+ ///
+ protected UnknownAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/CallbackQueryAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/CallbackQueryAction.cs
new file mode 100644
index 0000000..dc3c3e4
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/CallbackQueryAction.cs
@@ -0,0 +1,45 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// A callback query based action.
+///
+[ActionType(nameof(CallbackQueryAction))]
+public abstract class CallbackQueryAction : BaseAction
+{
+ ///
+ /// The original
+ ///
+ public CallbackQuery CallbackQuery { get; protected set; }
+
+ ///
+ /// The message that originated the callback query. Iy may be null if the message is too old.
+ ///
+ public Message? OriginalMessage { get; protected set; }
+
+ ///
+ /// Any data present on the callback query.
+ ///
+ public string? Data { get; protected set; }
+
+ ///
+ /// True if the callback query is from a game.
+ ///
+ public bool IsGameQuery { get; protected set; }
+
+ ///
+ protected CallbackQueryAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ CallbackQuery = update.CallbackQuery!;
+ OriginalMessage = CallbackQuery.Message;
+ Data = string.IsNullOrWhiteSpace(CallbackQuery.Data) ? CallbackQuery.Data : default;
+ IsGameQuery = CallbackQuery.IsGameQuery;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ChannelCreatedAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ChannelCreatedAction.cs
new file mode 100644
index 0000000..3a30fbf
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ChannelCreatedAction.cs
@@ -0,0 +1,20 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(ChannelCreatedAction))]
+public abstract class ChannelCreatedAction : BaseAction
+{
+ ///
+ protected ChannelCreatedAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ChannelPostAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ChannelPostAction.cs
new file mode 100644
index 0000000..ee6df97
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ChannelPostAction.cs
@@ -0,0 +1,27 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(ChannelPostAction))]
+public abstract class ChannelPostAction : BaseAction
+{
+ ///
+ /// Channel post message.
+ ///
+ public Message ChannelPost { get; protected set; }
+
+ ///
+ protected ChannelPostAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ ChannelPost = update.ChannelPost!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ChatJoinRequestAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ChatJoinRequestAction.cs
new file mode 100644
index 0000000..0e3f0ed
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ChatJoinRequestAction.cs
@@ -0,0 +1,27 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(ChatJoinRequestAction))]
+public abstract class ChatJoinRequestAction : BaseAction
+{
+ ///
+ /// Chat join request.
+ ///
+ public ChatJoinRequest Request { get; protected set; }
+
+ ///
+ protected ChatJoinRequestAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ Request = update.ChatJoinRequest!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ChatMemberAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ChatMemberAction.cs
new file mode 100644
index 0000000..f8bfb07
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ChatMemberAction.cs
@@ -0,0 +1,27 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(ChatMemberAction))]
+public abstract class ChatMemberAction : BaseAction
+{
+ ///
+ /// Chat member updated.
+ ///
+ public ChatMemberUpdated ChatMemberUpdated { get; set; }
+
+ ///
+ protected ChatMemberAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ ChatMemberUpdated = update.ChatMember!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ChosenInlineResultAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ChosenInlineResultAction.cs
new file mode 100644
index 0000000..ccd0076
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ChosenInlineResultAction.cs
@@ -0,0 +1,39 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// Inline result based action.
+///
+[ActionType(nameof(ChosenInlineResultAction))]
+public abstract class ChosenInlineResultAction : BaseAction
+{
+ ///
+ /// The original chosen inline result.
+ ///
+ public ChosenInlineResult ChosenInlineResult { get; protected set; }
+
+ ///
+ /// The chosen result id.
+ ///
+ public string ResultId { get; protected set; }
+
+ ///
+ /// The original query.
+ ///
+ public string Query { get; protected set; }
+
+ ///
+ protected ChosenInlineResultAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ ChosenInlineResult = update.ChosenInlineResult;
+ ResultId = update.ChosenInlineResult.ResultId;
+ Query = update.ChosenInlineResult.Query;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/DocumentAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/DocumentAction.cs
new file mode 100644
index 0000000..e47be23
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/DocumentAction.cs
@@ -0,0 +1,18 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(DocumentAction))]
+public abstract class DocumentAction : BaseAction
+{
+ ///
+ public DocumentAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ //TODO
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/EditedChannelPostAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/EditedChannelPostAction.cs
new file mode 100644
index 0000000..25e5ca8
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/EditedChannelPostAction.cs
@@ -0,0 +1,21 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(EditedChannelPostAction))]
+public abstract class EditedChannelPostAction : BaseAction
+{
+ ///
+ protected EditedChannelPostAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ //TODO
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/EditedMessageAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/EditedMessageAction.cs
new file mode 100644
index 0000000..0346180
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/EditedMessageAction.cs
@@ -0,0 +1,33 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(EditedMessageAction))]
+public abstract class EditedMessageAction : BaseAction
+{
+ ///
+ /// TODO
+ ///
+ public Message OriginalMessage { get; protected set; }
+
+ ///
+ /// TODO
+ ///
+ public Message EditedMessage { get; protected set; }
+
+ ///
+ protected EditedMessageAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ OriginalMessage = update.Message;
+ EditedMessage = update.EditedMessage;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/InlineQueryAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/InlineQueryAction.cs
new file mode 100644
index 0000000..254c78a
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/InlineQueryAction.cs
@@ -0,0 +1,39 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// Inline query based action.
+///
+[ActionType(nameof(InlineQueryAction))]
+public abstract class InlineQueryAction : BaseAction
+{
+ ///
+ /// The original
+ ///
+ public InlineQuery InlineQuery { get; protected set; }
+
+ ///
+ /// The query from the user.
+ ///
+ public string Query { get; protected set; }
+
+ ///
+ /// The offset.
+ ///
+ public string Offset { get; protected set; }
+
+ ///
+ protected InlineQueryAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ InlineQuery = update.InlineQuery!;
+ Query = update.InlineQuery!.Query;
+ Offset = update.InlineQuery.Offset;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/MyChatMemberAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/MyChatMemberAction.cs
new file mode 100644
index 0000000..30b1395
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/MyChatMemberAction.cs
@@ -0,0 +1,27 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(MyChatMemberAction))]
+public abstract class MyChatMemberAction : BaseAction
+{
+ ///
+ /// Chat member updated.
+ ///
+ public ChatMemberUpdated ChatMemberUpdated { get; set; }
+
+ ///
+ protected MyChatMemberAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ ChatMemberUpdated = update.MyChatMember!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/PollAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/PollAction.cs
new file mode 100644
index 0000000..2980c5c
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/PollAction.cs
@@ -0,0 +1,27 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(PollAction))]
+public abstract class PollAction : BaseAction
+{
+ ///
+ /// The original Poll.
+ ///
+ public Poll Poll { get; protected set; }
+
+ ///
+ protected PollAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ Poll = update.Poll!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/PollAnswerAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/PollAnswerAction.cs
new file mode 100644
index 0000000..affd707
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/PollAnswerAction.cs
@@ -0,0 +1,24 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+[ActionType(nameof(PollAnswerAction))]
+public abstract class PollAnswerAction : BaseAction
+{
+ ///
+ /// The original Poll.
+ ///
+ public PollAnswer Answer { get; protected set; }
+
+ ///
+ protected PollAnswerAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ var update = NavigatorContextAccessor.NavigatorContext.GetOriginalEvent();
+
+ Answer = update.PollAnswer!;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/PreCheckoutQueryAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/PreCheckoutQueryAction.cs
new file mode 100644
index 0000000..aa973e8
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/PreCheckoutQueryAction.cs
@@ -0,0 +1,19 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(PreCheckoutQueryAction))]
+public abstract class PreCheckoutQueryAction : BaseAction
+{
+ ///
+ protected PreCheckoutQueryAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ //TODO
+ }
+
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/ShippingQueryAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/ShippingQueryAction.cs
new file mode 100644
index 0000000..a8c6438
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/ShippingQueryAction.cs
@@ -0,0 +1,19 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(ShippingQueryAction))]
+public abstract class ShippingQueryAction : BaseAction
+{
+ ///
+ protected ShippingQueryAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ //TODO
+ }
+
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Actions/Updates/SupergroupCreatedAction.cs b/src/Navigator.Providers.Telegram/Actions/Updates/SupergroupCreatedAction.cs
new file mode 100644
index 0000000..d9d4a1e
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Actions/Updates/SupergroupCreatedAction.cs
@@ -0,0 +1,19 @@
+using Navigator.Actions;
+using Navigator.Actions.Attributes;
+using Navigator.Context;
+
+namespace Navigator.Providers.Telegram.Actions.Updates;
+
+///
+/// TODO
+///
+[ActionType(nameof(SupergroupCreatedAction))]
+public abstract class SupergroupCreatedAction : BaseAction
+{
+ ///
+ protected SupergroupCreatedAction(INavigatorContextAccessor navigatorContextAccessor) : base(navigatorContextAccessor)
+ {
+ //TODO
+ }
+
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Entities/TelegramBot.cs b/src/Navigator.Providers.Telegram/Entities/TelegramBot.cs
new file mode 100644
index 0000000..ac59a62
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Entities/TelegramBot.cs
@@ -0,0 +1,59 @@
+using Navigator.Context;
+using Navigator.Entities;
+
+namespace Navigator.Providers.Telegram.Entities;
+
+public class TelegramBot : Bot
+{
+ public TelegramBot(long externalIdentifier) : base($"{nameof(TelegramBot)}.{externalIdentifier.ToString()}")
+ {
+ ExternalIdentifier = externalIdentifier;
+ }
+
+ ///
+ /// Telegram identifier for the bot.
+ ///
+ public long ExternalIdentifier { get; init; }
+
+ ///
+ /// Username of the bot.
+ ///
+ public string Username { get; init; }
+
+ ///
+ /// First name of the bot.
+ ///
+ public string FirstName { get; init; }
+
+ ///
+ /// Last name of the bot.
+ ///
+ /// Optional.
+ ///
+ ///
+ public string? LastName { get; init; }
+
+ ///
+ /// Whether the bot can join groups or not.
+ ///
+ /// Optional. Only available on
+ ///
+ ///
+ public bool? CanJoinGroups { get; set; }
+
+ ///
+ /// Whether the bot can read all group messages or not.
+ ///
+ /// Optional. Only available on
+ ///
+ ///
+ public bool? CanReadAllGroupMessages { get; set; }
+
+ ///
+ /// Whether the bot supports inline queries or not.
+ ///
+ /// Optional. Only available on
+ ///
+ ///
+ public bool? SupportsInlineQueries { get; set; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Entities/TelegramChat.cs b/src/Navigator.Providers.Telegram/Entities/TelegramChat.cs
new file mode 100644
index 0000000..c266a88
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Entities/TelegramChat.cs
@@ -0,0 +1,35 @@
+using Navigator.Entities;
+
+namespace Navigator.Providers.Telegram.Entities;
+
+public class TelegramChat : Chat
+{
+ public TelegramChat(long externalIdentifier) : base($"{nameof(TelegramChat)}.{externalIdentifier.ToString()}")
+ {
+ ExternalIdentifier = externalIdentifier;
+ }
+
+ ///
+ /// Telegram identifier for the chat.
+ ///
+ public long ExternalIdentifier { get; init; }
+
+ ///
+ /// Title of the chat, if any.
+ ///
+ public string? Title { get; init; }
+
+ ///
+ /// Type of the chat, can be any of .
+ ///
+ public TelegramChatType Type { get; init; }
+}
+
+public enum TelegramChatType
+{
+ Private,
+ Group,
+ Channel,
+ Supergroup,
+ Sender
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Entities/TelegramConversation.cs b/src/Navigator.Providers.Telegram/Entities/TelegramConversation.cs
new file mode 100644
index 0000000..f37924c
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Entities/TelegramConversation.cs
@@ -0,0 +1,27 @@
+using Navigator.Entities;
+
+namespace Navigator.Providers.Telegram.Entities;
+
+public class TelegramConversation : Conversation
+{
+ public TelegramConversation()
+ {
+
+ }
+
+ public TelegramConversation(TelegramUser user, TelegramChat? chat) : base(user, chat)
+ {
+ User = user;
+ Chat = chat;
+ }
+
+ ///
+ /// Telegram user.
+ ///
+ public new TelegramUser User { get; init; }
+
+ ///
+ /// Telegram chat.
+ ///
+ public new TelegramChat? Chat { get; init; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Entities/TelegramUser.cs b/src/Navigator.Providers.Telegram/Entities/TelegramUser.cs
new file mode 100644
index 0000000..85b774d
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Entities/TelegramUser.cs
@@ -0,0 +1,45 @@
+using Navigator.Entities;
+
+namespace Navigator.Providers.Telegram.Entities;
+
+public class TelegramUser : User
+{
+ public TelegramUser(long externalIdentifier) : base($"{nameof(TelegramUser)}.{externalIdentifier.ToString()}")
+ {
+ ExternalIdentifier = externalIdentifier;
+ }
+
+ ///
+ /// Telegram identifier for the user.
+ ///
+ public long ExternalIdentifier { get; init; }
+
+ ///
+ /// Username of the user, if any.
+ ///
+ /// Optional.
+ ///
+ ///
+ public string? Username { get; init; }
+
+ ///
+ /// First name of the user.
+ ///
+ public string FirstName { get; init; }
+
+ ///
+ /// Last name of the user.
+ ///
+ /// Optional.
+ ///
+ ///
+ public string? LastName { get; init; }
+
+ ///
+ /// Language code of the user.
+ ///
+ /// Optional.
+ ///
+ ///
+ public string? LanguageCode { get; init; }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Extensions/TelegramUpdateExtensions.cs b/src/Navigator.Providers.Telegram/Extensions/TelegramUpdateExtensions.cs
new file mode 100644
index 0000000..9bcdeff
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Extensions/TelegramUpdateExtensions.cs
@@ -0,0 +1,93 @@
+using Navigator.Providers.Telegram.Entities;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+using Chat = Telegram.Bot.Types.Chat;
+using User = Telegram.Bot.Types.User;
+
+namespace Navigator.Providers.Telegram.Extensions;
+
+internal static class TelegramUpdateExtensions
+{
+ public static string? ExtractCommand(this Message message, string? botName)
+ {
+ if (message.Entities?.First().Type != MessageEntityType.BotCommand) return default;
+
+ var command = message.EntityValues?.First();
+
+ if (command?.Contains('@') == false) return command;
+
+ if (botName is not null && !command?.Contains(botName) == true) return default;
+
+ command = command?[..command.IndexOf('@')];
+
+ return command;
+ }
+
+ public static string? ExtractArguments(this Message message)
+ {
+ return message.Text is not null && message.Text.Contains(' ')
+ ? message.Text.Remove(0, message.Text.IndexOf(' ') + 1)
+ : default;
+ }
+
+ public static User? GetUserOrDefault(this Update update)
+ {
+ return update.Type switch
+ {
+ UpdateType.Message => update.Message?.From,
+ UpdateType.InlineQuery => update.InlineQuery?.From,
+ UpdateType.ChosenInlineResult => update.ChosenInlineResult?.From,
+ UpdateType.CallbackQuery => update.CallbackQuery?.From,
+ UpdateType.EditedMessage => update.EditedMessage?.From,
+ UpdateType.ChannelPost => update.ChannelPost?.From,
+ UpdateType.EditedChannelPost => update.EditedChannelPost?.From,
+ UpdateType.ShippingQuery => update.ShippingQuery?.From,
+ UpdateType.PreCheckoutQuery => update.PreCheckoutQuery?.From,
+ _ => default
+ };
+ }
+
+ public static Chat? GetChatOrDefault(this Update update)
+ {
+ return update.Type switch
+ {
+ UpdateType.CallbackQuery => update.CallbackQuery?.Message?.Chat,
+ UpdateType.Message => update.Message?.Chat,
+ UpdateType.EditedMessage => update.EditedMessage?.Chat,
+ UpdateType.ChannelPost => update.ChannelPost?.Chat,
+ UpdateType.EditedChannelPost => update.EditedChannelPost?.Chat,
+ _ => default
+ };
+ }
+
+ public static TelegramConversation GetConversation(this Update update)
+ {
+ var rawUser = update.GetUserOrDefault();
+ var rawChat = update.GetChatOrDefault();
+
+ if (rawUser is null)
+ {
+ throw new NavigatorException("No conversation could be built, user not found.");
+ }
+
+ var user = new TelegramUser(rawUser.Id)
+ {
+ Username = rawUser.Username,
+ FirstName = rawUser.FirstName,
+ LastName = rawUser.LastName,
+ LanguageCode = rawUser.LanguageCode
+ };
+
+ var chat = default(TelegramChat);
+
+ if (rawChat is not null)
+ {
+ chat = new TelegramChat(rawChat.Id)
+ {
+ Title = rawChat.Title
+ };
+ }
+
+ return new TelegramConversation(user, chat);
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/Hosted/SetTelegramBotWebHookHostedService.cs b/src/Navigator.Providers.Telegram/Hosted/SetTelegramBotWebHookHostedService.cs
new file mode 100644
index 0000000..7dc931a
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/Hosted/SetTelegramBotWebHookHostedService.cs
@@ -0,0 +1,55 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Navigator.Configuration;
+using Telegram.Bot;
+
+namespace Navigator.Providers.Telegram.Hosted;
+
+///
+/// WebHook service for navigator's telegram provider.
+///
+public class SetTelegramBotWebHookHostedService : BackgroundService
+{
+ private readonly ILogger _logger;
+ private readonly IServiceScopeFactory _serviceScopeFactory;
+ private readonly string _webHookUrl;
+
+ ///
+ /// Default constructor.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SetTelegramBotWebHookHostedService(ILogger logger, IServiceScopeFactory serviceScopeFactory,
+ NavigatorOptions navigatorOptions)
+ {
+ _logger = logger;
+ _serviceScopeFactory = serviceScopeFactory;
+
+ if (string.IsNullOrWhiteSpace(navigatorOptions.GetWebHookBaseUrl()))
+ {
+ throw new ArgumentNullException(nameof(navigatorOptions), "An URL for WebHook is required.");
+ }
+
+ _webHookUrl = $"{navigatorOptions.GetWebHookBaseUrl()}/{navigatorOptions.GetWebHookEndpointOrDefault()}";
+ }
+
+ ///
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogTrace("Starting with setup of webhook.");
+ _logger.LogTrace("Using webHook url {WebHookUrl}", _webHookUrl);
+
+ using var scope = _serviceScopeFactory.CreateScope();
+
+ var navigatorClient = scope.ServiceProvider.GetRequiredService();
+
+ await navigatorClient.SetWebhookAsync(_webHookUrl, cancellationToken: stoppingToken);
+
+ var me = await navigatorClient.GetMeAsync(stoppingToken);
+
+ _logger.LogInformation($"Telegram Bot Client is receiving updates for bot: @{me.Username} at the url: {_webHookUrl}");
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Abstractions/Navigator.Abstractions.csproj b/src/Navigator.Providers.Telegram/Navigator.Providers.Telegram.csproj
similarity index 50%
rename from src/Navigator.Abstractions/Navigator.Abstractions.csproj
rename to src/Navigator.Providers.Telegram/Navigator.Providers.Telegram.csproj
index ceb2d9c..84629dd 100644
--- a/src/Navigator.Abstractions/Navigator.Abstractions.csproj
+++ b/src/Navigator.Providers.Telegram/Navigator.Providers.Telegram.csproj
@@ -1,31 +1,28 @@
- net5.0
+ net6.0
+ true
enable
- true
true
- 0.9.99-beta
- Navigator.Abstractions
- Navigator Framework Abstractions
+ Navigator.Providers.Telegram
Lucas Maximiliano Marino
- A highly opinionated telegram bot framework, mainly based on Telegram.Bot.
+ Telegram provider for Navigator Framework. Implementation based on Telegram.Bot
https://github.com/navigatorframework/navigator
- https://github.com/navigatorframework/navigator/blob/master/LICENSE
https://github.com/navigatorframework/navigator
+ https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
Telegram, Bot, Framework, Navigator
true
- Copyright © Lucas Maximiliano Marino 2021
- https://raw.githubusercontent.com/navigatorframework/navigator/master/assets/logo.png
+ AGPL-3.0-only
+ Copyright © Lucas Maximiliano Marino 2022
-
+
-
+
-
-
+
-
+
diff --git a/src/Navigator.Providers.Telegram/NavigatorContextExtensions.cs b/src/Navigator.Providers.Telegram/NavigatorContextExtensions.cs
new file mode 100644
index 0000000..f1981d9
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/NavigatorContextExtensions.cs
@@ -0,0 +1,258 @@
+using Navigator.Context;
+using Navigator.Context.Extensions.Bundled.OriginalEvent;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+
+namespace Navigator.Providers.Telegram;
+
+///
+/// Useful extensions for Navigator Context.
+///
+public static class NavigatorContextExtensions
+{
+ ///
+ /// TODO
+ ///
+ ///
+ ///
+ public static NavigatorTelegramClient GetTelegramClient(this INavigatorContext context)
+ {
+ return context.Provider.GetTelegramClient();
+ }
+
+ // #region TelegramUser
+ //
+ // ///
+ // /// Get a Telegram user.
+ // ///
+ // ///
+ // /// When user is not found.
+ // ///
+ // public static User GetTelegramUser(this INavigatorContext ctx)
+ // {
+ // var user = ctx.GetTelegramUserOrDefault();
+ //
+ // return user ?? throw new Exception("User not found in update.");
+ // }
+ //
+ // ///
+ // /// Get a Telegram user or default if not found.
+ // ///
+ // ///
+ // ///
+ // public static User? GetTelegramUserOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type switch
+ // {
+ // UpdateType.Message => ctx.Update.Message.From,
+ // UpdateType.InlineQuery => ctx.Update.InlineQuery.From,
+ // UpdateType.ChosenInlineResult => ctx.Update.ChosenInlineResult.From,
+ // UpdateType.CallbackQuery => ctx.Update.CallbackQuery.From,
+ // UpdateType.EditedMessage => ctx.Update.EditedMessage.From,
+ // UpdateType.ChannelPost => ctx.Update.ChannelPost.From,
+ // UpdateType.EditedChannelPost => ctx.Update.EditedChannelPost.From,
+ // UpdateType.ShippingQuery => ctx.Update.ShippingQuery.From,
+ // UpdateType.PreCheckoutQuery => ctx.Update.PreCheckoutQuery.From,
+ // _ => default
+ // };
+ // }
+ //
+ // #endregion
+ //
+ #region Chat
+
+ ///
+ /// Gets a .
+ ///
+ ///
+ /// When chat is not found.
+ ///
+ public static Chat GetTelegramChat(this INavigatorContext ctx)
+ {
+ var chat = ctx.GetTelegramChatOrDefault();
+
+ return chat ?? throw new Exception("Chat was not found.");
+ }
+
+ ///
+ /// Get a Telegram chat or default if not found.
+ ///
+ ///
+ /// Telegram Chat
+ public static Chat? GetTelegramChatOrDefault(this INavigatorContext ctx)
+ {
+ return ctx.GetOriginalEventOrDefault()?.Type switch
+ {
+ UpdateType.CallbackQuery => ctx.GetOriginalEvent().CallbackQuery?.Message?.Chat,
+ UpdateType.Message => ctx.GetOriginalEvent().Message?.Chat,
+ UpdateType.EditedMessage => ctx.GetOriginalEvent().EditedMessage?.Chat,
+ UpdateType.ChannelPost => ctx.GetOriginalEvent().ChannelPost?.Chat,
+ UpdateType.EditedChannelPost => ctx.GetOriginalEvent().EditedChannelPost?.Chat,
+ _ => default
+ };
+ }
+
+ #endregion
+ //
+ // #region Message
+ //
+ // ///
+ // /// Get a Telegram message.
+ // ///
+ // ///
+ // /// When message is not found.
+ // ///
+ // public static Message GetMessage(this INavigatorContext ctx)
+ // {
+ // return ctx.GetMessageOrDefault() ?? throw new Exception("Message not found in update.");
+ // }
+ //
+ // ///
+ // /// Get a Telegram message or default if not found.
+ // ///
+ // ///
+ // ///
+ // public static Message? GetMessageOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.Message ? ctx.Update.Message : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region InlineQuery
+ //
+ // ///
+ // /// Get a Telegram inline query
+ // ///
+ // ///
+ // /// When inline query is not found.
+ // ///
+ // public static InlineQuery GetInlineQuery(this INavigatorContext ctx)
+ // {
+ // return ctx.GetInlineQueryOrDefault() ?? throw new Exception("InlineQuery not found in update.");
+ // }
+ //
+ // ///
+ // /// Get a Telegram inline query or default if not found.
+ // ///
+ // ///
+ // ///
+ // public static InlineQuery? GetInlineQueryOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.InlineQuery ? ctx.Update.InlineQuery : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region ChosenInlineResult
+ //
+ // public static ChosenInlineResult GetChosenInlineResult(this INavigatorContext ctx)
+ // {
+ // return ctx.GetChosenInlineResultOrDefault() ?? throw new Exception("ChosenInlineResult not found in update.");
+ // }
+ //
+ // public static ChosenInlineResult? GetChosenInlineResultOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.ChosenInlineResult ? ctx.Update.ChosenInlineResult : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region CallbackQuery
+ //
+ // public static CallbackQuery GetCallbackQuery(this INavigatorContext ctx)
+ // {
+ // return ctx.GetCallbackQueryOrDefault() ?? throw new Exception("CallbackQuery not found in update.");
+ // }
+ //
+ // public static CallbackQuery? GetCallbackQueryOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.CallbackQuery ? ctx.Update.CallbackQuery : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region EditedMessage
+ //
+ // public static Message GetEditedMessage(this INavigatorContext ctx)
+ // {
+ // return ctx.GetEditedMessageOrDefault() ?? throw new Exception("EditedMessage not found in update.");
+ // }
+ //
+ // public static Message? GetEditedMessageOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.EditedMessage ? ctx.Update.EditedMessage : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region ChannelPost
+ //
+ // public static Message GetChannelPost(this INavigatorContext ctx)
+ // {
+ // return ctx.GetChannelPostOrDefault() ?? throw new Exception("ChannelPost not found in update.");
+ // }
+ //
+ // public static Message? GetChannelPostOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.ChannelPost ? ctx.Update.ChannelPost : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region EditedChannelPost
+ //
+ // public static Message GetEditedChannelPost(this INavigatorContext ctx)
+ // {
+ // return ctx.GetEditedChannelPostOrDefault() ?? throw new Exception("EditedChannelPost not found in update.");
+ // }
+ //
+ // public static Message? GetEditedChannelPostOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.EditedChannelPost ? ctx.Update.EditedChannelPost : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region ShippingQuery
+ //
+ // public static ShippingQuery GetShippingQuery(this INavigatorContext ctx)
+ // {
+ // return ctx.GetShippingQueryOrDefault() ?? throw new Exception("ShippingQuery not found in update.");
+ // }
+ //
+ // public static ShippingQuery? GetShippingQueryOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.ShippingQuery ? ctx.Update.ShippingQuery : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region PreCheckoutQuery
+ //
+ // public static PreCheckoutQuery GetPreCheckoutQuery(this INavigatorContext ctx)
+ // {
+ // return ctx.GetPreCheckoutQueryOrDefault() ?? throw new Exception("PreCheckoutQuery not found in update.");
+ // }
+ //
+ // public static PreCheckoutQuery? GetPreCheckoutQueryOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.PreCheckoutQuery ? ctx.Update.PreCheckoutQuery : default;
+ // }
+ //
+ // #endregion
+ //
+ // #region Poll
+ //
+ // public static Poll GetPoll(this INavigatorContext ctx)
+ // {
+ // return ctx.GetPollOrDefault() ?? throw new Exception("Poll not found in update.");
+ // }
+ //
+ // public static Poll? GetPollOrDefault(this INavigatorContext ctx)
+ // {
+ // return ctx.Update.Type == UpdateType.Poll ? ctx.Update.Poll : default;
+ // }
+ //
+ // #endregion
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/NavigatorProviderConfigurationExtensions.cs b/src/Navigator.Providers.Telegram/NavigatorProviderConfigurationExtensions.cs
new file mode 100644
index 0000000..6279f99
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/NavigatorProviderConfigurationExtensions.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.DependencyInjection;
+using Navigator.Configuration;
+using Navigator.Configuration.Provider;
+using Navigator.Context;
+using Navigator.Providers.Telegram.Hosted;
+
+namespace Navigator.Providers.Telegram;
+
+public static class NavigatorProviderConfigurationExtensions
+{
+ public static NavigatorConfiguration Telegram(this NavigatorProviderConfiguration providerConfiguration,
+ Action options)
+ {
+ var telegramProviderOptions = new NavigatorTelegramProviderOptions();
+ options.Invoke(telegramProviderOptions);
+
+ return providerConfiguration.Provider(configuration =>
+ {
+ configuration.Options.Import(telegramProviderOptions.RetrieveAllOptions());
+
+ configuration.Services.AddSingleton();
+ configuration.Services.AddSingleton(sp => sp.GetRequiredService());
+ configuration.Services.AddScoped();
+ configuration.Services.AddScoped();
+ configuration.Services.AddScoped();
+ configuration.Services.AddHostedService();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/NavigatorProviderExtensions.cs b/src/Navigator.Providers.Telegram/NavigatorProviderExtensions.cs
new file mode 100644
index 0000000..e9fdbfa
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/NavigatorProviderExtensions.cs
@@ -0,0 +1,32 @@
+namespace Navigator.Providers.Telegram;
+
+public static class NavigatorProviderExtensions
+{
+ public static NavigatorTelegramClient GetTelegramClient(this INavigatorProvider provider)
+ {
+ var client = provider.GetTelegramClientOrDefault();
+
+ if (client is not null)
+ {
+ return client;
+ }
+
+ throw new Exception($"Navigator client was not of type {nameof(NavigatorTelegramClient)}")
+ {
+ Data =
+ {
+ {nameof(Type), provider.GetClient().GetType()}
+ }
+ };
+ }
+
+ public static NavigatorTelegramClient? GetTelegramClientOrDefault(this INavigatorProvider provider)
+ {
+ if (provider.GetClient() is NavigatorTelegramClient navigatorTelegramClient)
+ {
+ return navigatorTelegramClient;
+ }
+
+ return default;
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/NavigatorRouteProviderConfigurationExtensions.cs b/src/Navigator.Providers.Telegram/NavigatorRouteProviderConfigurationExtensions.cs
new file mode 100644
index 0000000..6526ea4
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/NavigatorRouteProviderConfigurationExtensions.cs
@@ -0,0 +1,58 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Navigator.Configuration;
+using Navigator.Configuration.Provider;
+using Newtonsoft.Json;
+using Telegram.Bot.Types;
+
+namespace Navigator.Providers.Telegram;
+
+public static class NavigatorRouteProviderConfigurationExtensions
+{
+ public static NavigatorRouteConfiguration Telegram(this NavigatorRouteProviderConfiguration routeProviderConfiguration)
+ {
+ return routeProviderConfiguration.Provider(builder =>
+ {
+ using var scope = builder.ServiceProvider.CreateScope();
+
+ var options = scope.ServiceProvider.GetRequiredService();
+
+ builder.MapPost(options.GetWebHookEndpointOrDefault(), ProcessTelegramUpdate);
+ });
+ }
+
+ private static async Task ProcessTelegramUpdate(HttpContext context)
+ {
+ context.Response.StatusCode = 200;
+
+ if (context.Request.ContentType != "application/json")
+ {
+ return;
+ }
+
+ var telegramUpdate = await ParseTelegramUpdate(context.Request.Body);
+
+ if (telegramUpdate is not null)
+ {
+ var navigatorMiddleware = context.RequestServices.GetRequiredService();
+
+ await navigatorMiddleware.Process(telegramUpdate);
+ }
+ }
+
+ private static async Task ParseTelegramUpdate(Stream stream)
+ {
+ try
+ {
+ var reader = new StreamReader(stream);
+ var update = JsonConvert.DeserializeObject(await reader.ReadToEndAsync());
+
+ return update.Id == default ? default : update;
+ }
+ catch
+ {
+ return default;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Navigator.Providers.Telegram/NavigatorTelegramClient.cs b/src/Navigator.Providers.Telegram/NavigatorTelegramClient.cs
new file mode 100644
index 0000000..2b13de0
--- /dev/null
+++ b/src/Navigator.Providers.Telegram/NavigatorTelegramClient.cs
@@ -0,0 +1,34 @@
+using Navigator.Configuration;
+using Navigator.Entities;
+using Navigator.Providers.Telegram.Entities;
+using Telegram.Bot;
+
+namespace Navigator.Providers.Telegram;
+
+public class NavigatorTelegramClient : TelegramBotClient, INavigatorClient
+{
+ ///