mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-25 07:25:22 -07:00
Add ability to approve or deny a request directly from a Telegram chat
This commit is contained in:
parent
f7ce3c36e3
commit
a9055efbd2
10 changed files with 229 additions and 20 deletions
|
@ -1,9 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Telegram
|
||||
{
|
||||
public interface ITelegramApi
|
||||
{
|
||||
Task Send(string message, string botApi, string chatId, string parseMode);
|
||||
Task Send(string message, string botApi, string chatId, string parseMode, IDictionary<string, string> data);
|
||||
}
|
||||
}
|
12
src/Ombi.Api.Telegram/ITelegramRequestAnswer.cs
Normal file
12
src/Ombi.Api.Telegram/ITelegramRequestAnswer.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Telegram
|
||||
{
|
||||
public interface ITelegramRequestAnswer
|
||||
{
|
||||
Task<string> ApproveMovie(int id);
|
||||
Task<string> DenyMovie(int id);
|
||||
Task<string> ApproveTv(int id);
|
||||
Task<string> DenyTv(int id);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Telegram.Bot" Version="15.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,33 +1,109 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.InputFiles;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace Ombi.Api.Telegram
|
||||
{
|
||||
public class TelegramApi : ITelegramApi
|
||||
{
|
||||
public TelegramApi(IApi api)
|
||||
public TelegramApi(ITelegramRequestAnswer answer)
|
||||
{
|
||||
_api = api;
|
||||
_answer = answer;
|
||||
}
|
||||
|
||||
private readonly IApi _api;
|
||||
private const string BaseUrl = "https://api.telegram.org/";
|
||||
|
||||
private readonly ITelegramRequestAnswer _answer;
|
||||
|
||||
private static string _botApi;
|
||||
private static TelegramBotClient _client;
|
||||
|
||||
|
||||
public async Task Send(string message, string botApi, string chatId, string parseMode)
|
||||
public async Task Send(string message, string botApi, string chatId, string parseMode, IDictionary<string, string> data)
|
||||
{
|
||||
var request = new Request($"bot{botApi}/sendMessage", BaseUrl, HttpMethod.Post);
|
||||
|
||||
var body = new
|
||||
if (_client == null || _botApi != botApi)
|
||||
{
|
||||
text = message,
|
||||
parse_mode = parseMode,
|
||||
chat_id= chatId
|
||||
UpdateClient(botApi);
|
||||
}
|
||||
|
||||
string notificationType = data["NotificationType"];
|
||||
if (notificationType != "NewRequest")
|
||||
{
|
||||
await SendSimpleMessage(message, chatId, parseMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SendQueryMessage(message, chatId, parseMode, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void UpdateClient(string botApi)
|
||||
{
|
||||
if (_client != null)
|
||||
{
|
||||
_client.StopReceiving();
|
||||
}
|
||||
|
||||
_botApi = botApi;
|
||||
_client = new TelegramBotClient(_botApi);
|
||||
_client.OnCallbackQuery += async (sender, args) =>
|
||||
{
|
||||
await _client.EditMessageCaptionAsync(
|
||||
chatId: args.CallbackQuery.Message.Chat.Id,
|
||||
messageId: args.CallbackQuery.Message.MessageId,
|
||||
caption: args.CallbackQuery.Message.Caption
|
||||
);
|
||||
|
||||
var parameters = args.CallbackQuery.Data.Split(' ');
|
||||
var answer = parameters[0];
|
||||
var id = int.Parse(parameters[1]);
|
||||
|
||||
var result =
|
||||
answer == "approve_Movie" ? await _answer.ApproveMovie(id) :
|
||||
answer == "deny_Movie" ? await _answer.DenyMovie(id) :
|
||||
answer == "approve_TvShow" ? await _answer.ApproveTv(id) :
|
||||
answer == "deny_TvShow" ? await _answer.DenyTv(id) :
|
||||
"Incorrect answer";
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
await SendSimpleMessage(result, args.CallbackQuery.Message.Chat.Id.ToString());
|
||||
}
|
||||
};
|
||||
|
||||
request.AddJsonBody(body);
|
||||
await _api.Request(request);
|
||||
_client.StartReceiving();
|
||||
}
|
||||
|
||||
private async Task SendSimpleMessage(string message, string chatId, string parseMode = "default")
|
||||
{
|
||||
await _client.SendTextMessageAsync(
|
||||
chatId: chatId,
|
||||
text: message,
|
||||
parseMode: (ParseMode)Enum.Parse(typeof(ParseMode), parseMode, true)
|
||||
);
|
||||
}
|
||||
|
||||
private async Task SendQueryMessage(string message, string chatId, string parseMode, IDictionary<string, string> data)
|
||||
{
|
||||
string image = data["PosterImage"];
|
||||
string type = data["Type"];
|
||||
string id = data["ItemId"];
|
||||
|
||||
await _client.SendPhotoAsync(
|
||||
chatId: chatId,
|
||||
photo: new InputOnlineFile(image),
|
||||
caption: message,
|
||||
parseMode: (ParseMode)Enum.Parse(typeof(ParseMode), parseMode, true),
|
||||
replyMarkup: new InlineKeyboardMarkup(new[]
|
||||
{
|
||||
InlineKeyboardButton.WithCallbackData("Approve", $"approve_{type} {id}"),
|
||||
InlineKeyboardButton.WithCallbackData("Deny", $"deny_{type} {id}"),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
37
src/Ombi.Api.Telegram/TelegramApiExtensions.cs
Normal file
37
src/Ombi.Api.Telegram/TelegramApiExtensions.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Telegram
|
||||
{
|
||||
public static class TelegramApiExtensions
|
||||
{
|
||||
public static IServiceCollection AddTelegramCallbacks<TMovieEngine, TTvEngine, TResult>(
|
||||
this IServiceCollection services,
|
||||
|
||||
Func<IServiceProvider, TMovieEngine> initMovie,
|
||||
Func<TMovieEngine, int, Task<TResult>> approveMovie,
|
||||
Func<TMovieEngine, int, Task<TResult>> denyMovie,
|
||||
|
||||
Func<IServiceProvider, TTvEngine> initTvShow,
|
||||
Func<TTvEngine, int, Task<TResult>> approveTv,
|
||||
Func<TTvEngine, int, Task<TResult>> denyTv,
|
||||
|
||||
Func<TResult, string> formater
|
||||
)
|
||||
{
|
||||
services.AddSingleton<ITelegramRequestAnswer>(rootProvider =>
|
||||
{
|
||||
return new TelegramRequestAnswer<TMovieEngine, TTvEngine>(rootProvider, initMovie, initTvShow)
|
||||
{
|
||||
OnApproveMovie = async (engine, id) => formater(await approveMovie(engine, id)),
|
||||
OnDenyMovie = async (engine, id) => formater(await denyMovie(engine, id)),
|
||||
OnApproveTv = async (engine, id) => formater(await approveTv(engine, id)),
|
||||
OnDenyTv = async (engine, id) => formater(await denyTv(engine, id)),
|
||||
};
|
||||
});
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
46
src/Ombi.Api.Telegram/TelegramRequestAnswer.cs
Normal file
46
src/Ombi.Api.Telegram/TelegramRequestAnswer.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ombi.Api.Telegram
|
||||
{
|
||||
public class TelegramRequestAnswer<TMovieEngine, TTvEngine> : ITelegramRequestAnswer
|
||||
{
|
||||
public TelegramRequestAnswer(
|
||||
IServiceProvider rootProvider,
|
||||
Func<IServiceProvider, TMovieEngine> initMovie,
|
||||
Func<IServiceProvider, TTvEngine> initTv
|
||||
)
|
||||
{
|
||||
_rootProvider = rootProvider;
|
||||
_initMovie = initMovie;
|
||||
_initTv = initTv;
|
||||
}
|
||||
|
||||
|
||||
private readonly IServiceProvider _rootProvider;
|
||||
private readonly Func<IServiceProvider, TMovieEngine> _initMovie;
|
||||
private readonly Func<IServiceProvider, TTvEngine> _initTv;
|
||||
|
||||
public Func<TMovieEngine, int, Task<string>> OnApproveMovie { get; set; }
|
||||
public Func<TMovieEngine, int, Task<string>> OnDenyMovie { get; set; }
|
||||
public Func<TTvEngine, int, Task<string>> OnApproveTv { get; set; }
|
||||
public Func<TTvEngine, int, Task<string>> OnDenyTv { get; set; }
|
||||
|
||||
|
||||
public async Task<string> ApproveMovie(int id) => await Execute(_initMovie, OnApproveMovie, id);
|
||||
public async Task<string> DenyMovie(int id) => await Execute(_initMovie, OnDenyMovie, id);
|
||||
public async Task<string> ApproveTv(int id) => await Execute(_initTv, OnApproveTv, id);
|
||||
public async Task<string> DenyTv(int id) => await Execute(_initTv, OnDenyTv, id);
|
||||
|
||||
|
||||
private async Task<string> Execute<TEngine>(Func<IServiceProvider, TEngine> init, Func<TEngine, int, Task<string>> action, int id)
|
||||
{
|
||||
using (var scope = _rootProvider.CreateScope())
|
||||
{
|
||||
var engine = init(scope.ServiceProvider);
|
||||
return await action(engine, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,7 +101,7 @@ namespace Ombi.DependencyInjection
|
|||
public static void RegisterHttp(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext.User);
|
||||
services.AddScoped<IPrincipal>(sp => sp.GetService<IHttpContextAccessor>().HttpContext?.User);
|
||||
}
|
||||
|
||||
public static void RegisterApi(this IServiceCollection services)
|
||||
|
|
|
@ -10,6 +10,8 @@ using Ombi.Store.Entities;
|
|||
using Ombi.Store.Repository;
|
||||
using Ombi.Store.Repository.Requests;
|
||||
using Ombi.Api.Telegram;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ombi.Notifications.Agents
|
||||
{
|
||||
|
@ -83,7 +85,7 @@ namespace Ombi.Notifications.Agents
|
|||
{
|
||||
try
|
||||
{
|
||||
await Api.Send(model.Message, settings.BotApi, settings.ChatId, settings.ParseMode);
|
||||
await Api.Send(model.Message, settings.BotApi, settings.ChatId, settings.ParseMode, model.Data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -97,6 +99,13 @@ namespace Ombi.Notifications.Agents
|
|||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
Data = new Dictionary<string, string>
|
||||
{
|
||||
[nameof(NotificationType)] = NotificationType.NewRequest.ToString(),
|
||||
[nameof(NotificationMessageCurlys.PosterImage)] = "https://image.tmdb.org/t/p/w300//btTdmkgIvOi0FFip1sPuZI2oQG6.jpg",
|
||||
[nameof(NotificationMessageCurlys.Type)] = RequestType.Movie.ToString(),
|
||||
[nameof(NotificationMessageCurlys.ItemId)] = 0.ToString(),
|
||||
}
|
||||
};
|
||||
await Send(notification, settings);
|
||||
}
|
||||
|
@ -109,10 +118,15 @@ namespace Ombi.Notifications.Agents
|
|||
Logger.LogInformation($"Template {type} is disabled for {NotificationAgent.Telegram}");
|
||||
return;
|
||||
}
|
||||
|
||||
var notificationData = parsed.Data.ToDictionary(x => x.Key, x => x.Value);
|
||||
notificationData[nameof(NotificationType)] = type.ToString();
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = parsed.Message,
|
||||
Data = notificationData,
|
||||
};
|
||||
|
||||
await Send(notification, settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Ombi.Notifications
|
|||
{
|
||||
LoadIssues(opts);
|
||||
|
||||
ItemId = req.Id.ToString();
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
|
@ -68,6 +69,7 @@ namespace Ombi.Notifications
|
|||
{
|
||||
LoadIssues(opts);
|
||||
|
||||
ItemId = req.Id.ToString();
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
|
@ -114,6 +116,8 @@ namespace Ombi.Notifications
|
|||
public void Setup(NotificationOptions opts, ChildRequests req, CustomizationSettings s, UserNotificationPreferences pref)
|
||||
{
|
||||
LoadIssues(opts);
|
||||
|
||||
ItemId = req.Id.ToString();
|
||||
string title;
|
||||
if (req == null)
|
||||
{
|
||||
|
@ -216,6 +220,7 @@ namespace Ombi.Notifications
|
|||
}
|
||||
|
||||
// User Defined
|
||||
public string ItemId { get; set; }
|
||||
public string RequestedUser { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string IssueUser => UserName;
|
||||
|
@ -248,6 +253,7 @@ namespace Ombi.Notifications
|
|||
|
||||
public Dictionary<string, string> Curlys => new Dictionary<string, string>
|
||||
{
|
||||
{nameof(ItemId), ItemId },
|
||||
{nameof(RequestedUser), RequestedUser },
|
||||
{nameof(Title), Title },
|
||||
{nameof(RequestedDate), RequestedDate },
|
||||
|
|
|
@ -13,6 +13,7 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.DependencyInjection;
|
||||
using Ombi.Extensions;
|
||||
|
@ -24,7 +25,7 @@ using Ombi.Store.Context;
|
|||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
using Serilog;
|
||||
using SQLitePCL;
|
||||
using Ombi.Api.Telegram;
|
||||
using ILogger = Serilog.ILogger;
|
||||
|
||||
namespace Ombi
|
||||
|
@ -96,6 +97,18 @@ namespace Ombi
|
|||
.AllowAnyHeader();
|
||||
}));
|
||||
|
||||
services.AddTelegramCallbacks(
|
||||
provider => provider.GetRequiredService<IMovieRequestEngine>(),
|
||||
async (engine, id) => await engine.ApproveMovieById(id),
|
||||
async (engine, id) => await engine.DenyMovieById(id, string.Empty),
|
||||
|
||||
provider => provider.GetRequiredService<ITvRequestEngine>(),
|
||||
async (engine, id) => await engine.ApproveChildRequest(id),
|
||||
async (engine, id) => await engine.DenyChildRequest(id, string.Empty),
|
||||
|
||||
result => result.Message ?? result.ErrorMessage
|
||||
);
|
||||
|
||||
// Build the intermediate service provider
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue