add ping move to boxes etc
This commit is contained in:
parent
f0e6aaa7e5
commit
979120383a
@ -1,103 +0,0 @@
|
||||
module PublishHelperBot.Handlers
|
||||
|
||||
open System.Threading.Tasks
|
||||
open Microsoft.FSharp.Core
|
||||
open PublishHelperBot.Environment
|
||||
open PublishHelperBot.YoutubeDl
|
||||
open Telegram.Bot
|
||||
open Telegram.Bot.Types
|
||||
open Telegram.Bot.Types.Enums
|
||||
|
||||
type BaseHandlerArgs = Update * BotConfig
|
||||
|
||||
type HandlerArgs = Update * BotConfig * ITelegramBotClient
|
||||
|
||||
type HandlerRequirements = BaseHandlerArgs -> bool
|
||||
|
||||
type Handler = HandlerArgs -> Task
|
||||
|
||||
type Handler<'deps> = 'deps * HandlerArgs -> Task
|
||||
|
||||
// Utils
|
||||
let UpdateIsAMessage (x: Update) = x.Type = UpdateType.Message
|
||||
|
||||
let FromAdminChat (x: Message, c: BotConfig) = x.Chat.Id = c.adminChatId
|
||||
|
||||
let HasReply (x: Message) = not(isNull x.ReplyToMessage)
|
||||
|
||||
let ExtractPhotoFromMessage (x: Message) = Array.map (fun (p: PhotoSize) -> p.FileId) x.Photo
|
||||
|
||||
let HasText (x: Message) = not(isNull x.Text)
|
||||
|
||||
let UrlsAsAlbumInputMedia (urls: string[]): IAlbumInputMedia[] =
|
||||
Array.map (fun (x: string) -> InputMediaPhoto(x)) urls
|
||||
|
||||
// Post (Relay) command
|
||||
type RelayCaptionMode =
|
||||
| WithAuthor
|
||||
| Anonymous
|
||||
| Unknown
|
||||
|
||||
let RelaySupportedContent (x: Message) =
|
||||
match x.Type with
|
||||
| MessageType.Text -> true
|
||||
| MessageType.Photo -> true
|
||||
| MessageType.Video -> true
|
||||
| _ -> false
|
||||
|
||||
let RelayCaptionType (command: string) =
|
||||
match command with
|
||||
| _ when command.StartsWith "\\post anon" -> Anonymous
|
||||
| _ when command.StartsWith "\\post" -> WithAuthor
|
||||
| _ -> Unknown
|
||||
|
||||
let RelayCaption (name: string, url: string) = $"<a href=\"{url}\">Прислал</a> {name}"
|
||||
let RelayParseMode = ParseMode.Html;
|
||||
|
||||
let RelayResolveCaption (mode: RelayCaptionMode, username: string, linkUrl: string) =
|
||||
match mode with
|
||||
| WithAuthor -> RelayCaption(username, linkUrl)
|
||||
| _ -> null
|
||||
|
||||
let public RelayMatch: HandlerRequirements = fun (u, c) ->
|
||||
UpdateIsAMessage u &&
|
||||
FromAdminChat <| (u.Message, c) &&
|
||||
HasReply u.Message &&
|
||||
HasText u.Message &&
|
||||
RelaySupportedContent u.Message.ReplyToMessage &&
|
||||
not (RelayCaptionType u.Message.Text = RelayCaptionMode.Unknown)
|
||||
|
||||
let public RelayHandler: Handler = fun (update, config, tg) ->
|
||||
let reply = update.Message.ReplyToMessage
|
||||
let channelId = config.chanelId
|
||||
let author = $"{reply.From.FirstName} {reply.From.LastName}"
|
||||
let captionMode = RelayCaptionType update.Message.Text
|
||||
|
||||
let photoMedia = lazy Array.get (ExtractPhotoFromMessage reply) 0
|
||||
let caption = lazy RelayResolveCaption(captionMode, author, config.relayUrl)
|
||||
|
||||
match reply.Type with
|
||||
| MessageType.Text -> tg.ForwardMessageAsync(channelId, reply.Chat.Id, reply.MessageId)
|
||||
| MessageType.Photo -> tg.SendPhotoAsync(channelId, photoMedia.Value, caption = caption.Value,
|
||||
parseMode = RelayParseMode)
|
||||
| MessageType.Video -> tg.SendVideoAsync(channelId, reply.Video.FileId, caption = caption.Value,
|
||||
parseMode = RelayParseMode)
|
||||
| _ -> Task.CompletedTask
|
||||
|
||||
|
||||
// YoutubeDL repost
|
||||
|
||||
let YoutubeRepostMatchCmd = "\\ytdl"
|
||||
let public YoutubeRepostMatch: HandlerRequirements = fun (u, c) ->
|
||||
UpdateIsAMessage u &&
|
||||
FromAdminChat <| (u.Message, c) &&
|
||||
HasText <| u.Message &&
|
||||
u.Message.Text.StartsWith YoutubeRepostMatchCmd &&
|
||||
u.Message.Text.Split(' ').Length = 2
|
||||
|
||||
let public YoutubeRepostHandler: Handler<IYoutubeDlService> = fun (yt, (u, c, tg)) ->
|
||||
task {
|
||||
let trim (x: string) = x.Trim()
|
||||
let! id = YoutubeRepostMatchCmd |> u.Message.Text.Split |> Array.last |> trim |> yt.AddJob
|
||||
do! tg.SendTextMessageAsync(c.adminChatId, id.ToString()) |> Async.AwaitTask |> Async.Ignore
|
||||
}
|
@ -1,22 +1,18 @@
|
||||
open System
|
||||
open System.Net.Http
|
||||
open System.Threading
|
||||
open System.Threading.Tasks
|
||||
open PublishHelperBot.Handlers
|
||||
open PublishHelperBot.Environment
|
||||
open PublishHelperBot.YoutubeDl
|
||||
open PublishHelperBot.Telegram
|
||||
open Telegram.Bot
|
||||
open Telegram.Bot.Polling
|
||||
open Telegram.Bot.Types
|
||||
open Telegram.Bot.Types.Enums
|
||||
|
||||
let createBot (config: BotConfig, http: HttpClient) = TelegramBotClient(config.token, http)
|
||||
|
||||
let config = createConfig "SBPB_CONFIG_PATH"
|
||||
|
||||
let botClient = createBot (config, new HttpClient())
|
||||
let botClient = TelegramBotClient (config.token, new HttpClient())
|
||||
|
||||
let youtubeDlService =
|
||||
let youtubeDlClient =
|
||||
YoutubeDlClient.createClient {
|
||||
Client = new HttpClient()
|
||||
@ -31,41 +27,34 @@ let youtubeDlService =
|
||||
AdminChatId = config.adminChatId
|
||||
}
|
||||
|
||||
let youtubeDlService =
|
||||
YoutubeDlService.createService youtubeDlClient tgService
|
||||
|
||||
let isObsoleteUpdate =
|
||||
let startDate = DateTime.UtcNow
|
||||
fun (update: Update) ->
|
||||
update.Type = UpdateType.Message
|
||||
&& update.Message.Date < startDate
|
||||
|
||||
let (|ObsoleteUpdate|RelayMatchUpdate|YoutubeRepostMatchUpdate|SkipUpdate|) (update: Update) =
|
||||
let isObsoleteUpdate (update: Update) =
|
||||
update.Type = UpdateType.Message && update.Message.Date < startDate
|
||||
match update with
|
||||
| _ when isObsoleteUpdate update -> ObsoleteUpdate
|
||||
| _ when RelayMatch (update, config) -> RelayMatchUpdate
|
||||
| _ when YoutubeRepostMatch (update, config) -> YoutubeRepostMatchUpdate
|
||||
| _ -> SkipUpdate
|
||||
|
||||
let updateHandle (bc: ITelegramBotClient) (update: Update) (ct: CancellationToken): Task =
|
||||
let tgCtx = (update, config, bc)
|
||||
match update with
|
||||
| RelayMatchUpdate() ->
|
||||
Logging.logger.Information("RelayMatchUpdate")
|
||||
RelayHandler tgCtx
|
||||
| YoutubeRepostMatchUpdate() ->
|
||||
YoutubeRepostHandler <| (youtubeDlService, tgCtx)
|
||||
| ObsoleteUpdate() ->
|
||||
Logging.logger.Information("Skipping obsolete update")
|
||||
Task.CompletedTask
|
||||
| SkipUpdate() ->
|
||||
Logging.logger.Information("Skipping update")
|
||||
Task.CompletedTask
|
||||
|
||||
let handlePollingError (_: ITelegramBotClient) (e: Exception) (_: CancellationToken) =
|
||||
let handlePollingError _ (e: Exception) _ =
|
||||
Logging.logger.Error(e, "Polling error")
|
||||
Task.CompletedTask
|
||||
|
||||
let receiverOptions = ReceiverOptions(AllowedUpdates = Array.zeroCreate<UpdateType> 0)
|
||||
|
||||
Logging.logger.Information("Starting bot")
|
||||
botClient.StartReceiving(updateHandle, handlePollingError, receiverOptions)
|
||||
|
||||
let botHandler = TgUpdateHandler.createHandler config tgService youtubeDlService
|
||||
botClient.StartReceiving(
|
||||
(fun client update _ ->
|
||||
if not (isObsoleteUpdate update) then
|
||||
botHandler.PostUpdate(update)
|
||||
Task.CompletedTask
|
||||
),
|
||||
handlePollingError,
|
||||
ReceiverOptions(
|
||||
AllowedUpdates = Array.zeroCreate<UpdateType> 0
|
||||
)
|
||||
)
|
||||
|
||||
Logging.logger.Information("Я родился")
|
||||
|
||||
Console.ReadKey() |> ignore
|
@ -12,8 +12,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="Environment.fs" />
|
||||
<Compile Include="Types.fs" />
|
||||
<Compile Include="Telegram.fs" />
|
||||
<Compile Include="YoutubeDl.fs" />
|
||||
<Compile Include="Handlers.fs" />
|
||||
<Compile Include="Program.fs" />
|
||||
<Content Include="config.example.json" />
|
||||
</ItemGroup>
|
||||
|
297
PublishHelperBot/Telegram.fs
Normal file
297
PublishHelperBot/Telegram.fs
Normal file
@ -0,0 +1,297 @@
|
||||
module PublishHelperBot.Telegram
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open Microsoft.FSharp.Control
|
||||
open PublishHelperBot.Environment
|
||||
open PublishHelperBot.Types
|
||||
open Telegram.Bot
|
||||
open Telegram.Bot.Types
|
||||
open Telegram.Bot.Types.Enums
|
||||
open Telegram.Bot.Types.InputFiles
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module BotUpdateType =
|
||||
let private getRelayCaptionType (command: string) =
|
||||
match command with
|
||||
| _ when command.StartsWith "\\post anon" -> Anonymous
|
||||
| _ when command.StartsWith "\\post" -> WithAuthor
|
||||
| _ -> Unknown
|
||||
|
||||
let private updateIsAMessage (update: Update) =
|
||||
update.Type = UpdateType.Message
|
||||
|
||||
let private fromAdminChat (message: Message, adminChatId: ConfigChatId) =
|
||||
message.Chat.Id = adminChatId
|
||||
|
||||
let private hasReply (x: Message) =
|
||||
not (isNull x.ReplyToMessage)
|
||||
|
||||
let private hasText (x: Message) =
|
||||
not (isNull x.Text)
|
||||
|
||||
let private hasRelaySupportedContent (x: Message) =
|
||||
match x.Type with
|
||||
| MessageType.Text -> true
|
||||
| MessageType.Photo -> true
|
||||
| MessageType.Video -> true
|
||||
| _ -> false
|
||||
|
||||
let [<Literal>] private YoutubeRepostMatchCmd = "\\ytdl"
|
||||
let private isYoutubeRepost (update: Update, adminChatId: ConfigChatId) =
|
||||
updateIsAMessage update &&
|
||||
fromAdminChat (update.Message, adminChatId) &&
|
||||
hasText update.Message &&
|
||||
update.Message.Text.StartsWith YoutubeRepostMatchCmd &&
|
||||
update.Message.Text.Split(' ').Length = 2
|
||||
|
||||
let private isRelay (update: Update, adminChatId: ConfigChatId) =
|
||||
updateIsAMessage update &&
|
||||
fromAdminChat (update.Message, adminChatId) &&
|
||||
hasReply update.Message &&
|
||||
hasRelaySupportedContent update.Message.ReplyToMessage &&
|
||||
not (getRelayCaptionType update.Message.Text = RelayCaptionMode.Unknown)
|
||||
|
||||
let private isPing (update: Update, adminChatId: ConfigChatId) =
|
||||
updateIsAMessage update &&
|
||||
fromAdminChat (update.Message, adminChatId) &&
|
||||
hasText update.Message &&
|
||||
update.Message.Text.StartsWith("\ping")
|
||||
|
||||
let getUpdateType (update: Update) (config: BotConfig) =
|
||||
match update with
|
||||
| _ when isPing (update, config.adminChatId) ->
|
||||
BotUpdateType.Ping
|
||||
|
||||
| _ when isYoutubeRepost(update, config.adminChatId) ->
|
||||
let url = update.Message.Text.Split(' ').[1]
|
||||
BotUpdateType.YoutubeRepost url
|
||||
|
||||
| _ when isRelay(update, config.adminChatId) ->
|
||||
let reply = update.Message.ReplyToMessage
|
||||
let getCaption() =
|
||||
let captionMode = getRelayCaptionType update.Message.Text
|
||||
let author = $"{reply.From.FirstName} {reply.From.LastName}"
|
||||
match captionMode with
|
||||
| WithAuthor -> $"<a href=\"{config.relayUrl}\">Прислал</a> {author}"
|
||||
| _ -> null
|
||||
|
||||
match reply.Type with
|
||||
| MessageType.Text ->
|
||||
let args = {
|
||||
ReplyChatId = reply.Chat.Id
|
||||
ReplyMessageId = reply.MessageId
|
||||
Relay = RelayType.Text
|
||||
}
|
||||
BotUpdateType.RelayUpdate args
|
||||
|
||||
| MessageType.Photo ->
|
||||
let caption = getCaption()
|
||||
let media =
|
||||
reply.Photo
|
||||
|> Array.map (fun (p: PhotoSize) -> p.FileId)
|
||||
|> Array.tryHead
|
||||
|
||||
match media with
|
||||
| Some media ->
|
||||
let args = {
|
||||
ReplyChatId = reply.Chat.Id
|
||||
ReplyMessageId = reply.MessageId
|
||||
Relay = RelayType.Photo (media, caption)
|
||||
}
|
||||
BotUpdateType.RelayUpdate args
|
||||
|
||||
| None ->
|
||||
BotUpdateType.Skip
|
||||
|
||||
| MessageType.Video ->
|
||||
let caption = getCaption()
|
||||
let args = {
|
||||
ReplyChatId = reply.Chat.Id
|
||||
ReplyMessageId = reply.MessageId
|
||||
Relay = RelayType.Video (reply.Video.FileId, caption)
|
||||
}
|
||||
BotUpdateType.RelayUpdate args
|
||||
|
||||
| _ ->
|
||||
BotUpdateType.Skip
|
||||
|
||||
| _ ->
|
||||
BotUpdateType.Skip
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TgService =
|
||||
type private Msg =
|
||||
| Ping
|
||||
| PostVideo of url: string * savePath: string * externalId: Guid
|
||||
| PostRelay of RelayArgs
|
||||
| PostMessageToAdminChat of text: string
|
||||
|
||||
let private createInbox (config: TgServiceConfig) =
|
||||
MailboxProcessor.Start(fun inbox ->
|
||||
let rec loop () =
|
||||
async {
|
||||
match! inbox.Receive() with
|
||||
| Ping ->
|
||||
Logging.logger.Information("Sending ГЫЧА)))0")
|
||||
let sticker = InputOnlineFile(
|
||||
value = "CAACAgIAAx0CQj8KlAACBPBj-ylrAcDqnwvpgEssCuN0aTilywACoxYAAvy_sEqzXsNGSWYfpS4E"
|
||||
)
|
||||
do!
|
||||
config.Client.SendStickerAsync(
|
||||
config.AdminChatId,
|
||||
sticker
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
return! loop ()
|
||||
|
||||
| PostRelay args ->
|
||||
Logging.logger.Information("Posting relay, relay = {relay}", args)
|
||||
match args.Relay with
|
||||
| RelayType.Text ->
|
||||
do!
|
||||
config.Client.ForwardMessageAsync(
|
||||
config.ChannelId,
|
||||
args.ReplyChatId,
|
||||
args.ReplyMessageId
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
|
||||
| RelayType.Photo(media, caption) ->
|
||||
do!
|
||||
config.Client.SendPhotoAsync(
|
||||
config.ChannelId,
|
||||
media,
|
||||
caption,
|
||||
parseMode = ParseMode.Html
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
|
||||
| RelayType.Video(media, caption) ->
|
||||
do!
|
||||
config.Client.SendVideoAsync(
|
||||
config.ChannelId,
|
||||
media,
|
||||
caption = caption,
|
||||
parseMode = ParseMode.Html
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
|
||||
return! loop ()
|
||||
|
||||
| PostMessageToAdminChat text ->
|
||||
do!
|
||||
config.Client.SendTextMessageAsync(
|
||||
config.AdminChatId, text
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
|
||||
return! loop ()
|
||||
|
||||
| PostVideo (url, savePath, externalId) ->
|
||||
try
|
||||
Logging.logger.Information("Reading file path = {path}", savePath)
|
||||
use file = File.OpenRead(savePath)
|
||||
if (file.Length / 1024L / 1024L) < 50L then
|
||||
let input = InputOnlineFile(file, Path.GetRandomFileName())
|
||||
let caption = $"Source: {url}"
|
||||
Logging.logger.Information(
|
||||
"Sending video to channel, channelId = {channelId}, caption = {caption}",
|
||||
config.ChannelId,
|
||||
caption)
|
||||
do! config.Client.SendVideoAsync(
|
||||
config.ChannelId,
|
||||
input,
|
||||
caption = caption
|
||||
)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Catch
|
||||
|> Async.Ignore
|
||||
else
|
||||
inbox.Post(PostMessageToAdminChat($"Да блять, видео вышло больше 50мб: {externalId}"))
|
||||
finally
|
||||
Logging.logger.Information("Deleting file path = {path}", savePath)
|
||||
File.Delete(savePath)
|
||||
|
||||
match! config.YoutubeDlClient.CleanJob(externalId) with
|
||||
| Ok _ -> ()
|
||||
| Error _ -> ()
|
||||
return! loop ()
|
||||
}
|
||||
loop ()
|
||||
)
|
||||
|
||||
let createService config =
|
||||
let inbox = createInbox config
|
||||
{ new ITgService with
|
||||
member this.PostRelay(args) =
|
||||
inbox.Post(PostRelay(args))
|
||||
|
||||
member this.Ping() =
|
||||
inbox.Post(Ping)
|
||||
|
||||
member this.PostMessageToAdminChat(text) =
|
||||
inbox.Post(PostMessageToAdminChat(text))
|
||||
|
||||
member this.PostVideo(url, savePath, externalId) =
|
||||
inbox.Post(PostVideo(url, savePath, externalId)) }
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TgUpdateHandler =
|
||||
type private Msg =
|
||||
| NewUpdate of Update
|
||||
|
||||
let private createInbox (config: BotConfig) (service: ITgService) (ytService: IYoutubeDlService) =
|
||||
MailboxProcessor.Start(fun inbox ->
|
||||
let rec loop() =
|
||||
async {
|
||||
match! inbox.Receive() with
|
||||
| NewUpdate update ->
|
||||
try
|
||||
match BotUpdateType.getUpdateType update config with
|
||||
| BotUpdateType.Skip ->
|
||||
Logging.logger.Information("Skipping update")
|
||||
|
||||
| BotUpdateType.Ping ->
|
||||
Logging.logger.Information("Received ping")
|
||||
service.Ping()
|
||||
|
||||
| BotUpdateType.YoutubeRepost url ->
|
||||
Logging.logger.Information("Received youtube repost update")
|
||||
async {
|
||||
let! id = ytService.AddJob(url)
|
||||
service.PostMessageToAdminChat(id.ToString())
|
||||
} |> Async.Start
|
||||
|
||||
| BotUpdateType.RelayUpdate relayArgs ->
|
||||
Logging.logger.Information("Relay update")
|
||||
service.PostRelay(relayArgs)
|
||||
|
||||
with ex ->
|
||||
Logging.logger.Error(ex, "Блядь")
|
||||
try
|
||||
service.PostMessageToAdminChat "паша сука"
|
||||
with ex ->
|
||||
Logging.logger.Error(ex, "Да блядь")
|
||||
|
||||
return! loop ()
|
||||
|
||||
}
|
||||
loop()
|
||||
)
|
||||
|
||||
let createHandler config service ytService =
|
||||
let inbox = createInbox config service ytService
|
||||
{ new ITgUpdateHandler with
|
||||
member this.PostUpdate update =
|
||||
inbox.Post(NewUpdate(update)) }
|
77
PublishHelperBot/Types.fs
Normal file
77
PublishHelperBot/Types.fs
Normal file
@ -0,0 +1,77 @@
|
||||
module PublishHelperBot.Types
|
||||
|
||||
open System
|
||||
open Telegram.Bot
|
||||
open Telegram.Bot.Types
|
||||
|
||||
type ConfigChatId = int64
|
||||
|
||||
type CreateYoutubeDlJob = {
|
||||
url: string
|
||||
savePath: string
|
||||
}
|
||||
|
||||
type CreateYoutubeDlJobSuccess = {
|
||||
task: Guid
|
||||
}
|
||||
|
||||
type YoutubeDlStateResponse = {
|
||||
state: string
|
||||
}
|
||||
|
||||
type YoutubeDlError = {
|
||||
message: string
|
||||
}
|
||||
|
||||
type CreateJobResult = Result<CreateYoutubeDlJobSuccess, YoutubeDlError>
|
||||
|
||||
type CheckJobResult = Result<YoutubeDlStateResponse, YoutubeDlError>
|
||||
|
||||
type CleanJobResult = Result<YoutubeDlStateResponse, YoutubeDlError>
|
||||
|
||||
type IYoutubeDlClient =
|
||||
abstract member CreateJob: CreateYoutubeDlJob -> Async<CreateJobResult>
|
||||
abstract member CheckJob: externalId: Guid -> Async<CheckJobResult>
|
||||
abstract member CleanJob: externalId: Guid -> Async<CleanJobResult>
|
||||
|
||||
type IYoutubeDlService =
|
||||
abstract member AddJob: url: string -> Async<Guid>
|
||||
|
||||
type TgServiceConfig = {
|
||||
Client: ITelegramBotClient
|
||||
ChannelId: ConfigChatId
|
||||
AdminChatId: ConfigChatId
|
||||
YoutubeDlClient: IYoutubeDlClient
|
||||
}
|
||||
|
||||
type RelayCaptionMode =
|
||||
| WithAuthor
|
||||
| Anonymous
|
||||
| Unknown
|
||||
|
||||
type RelayType =
|
||||
| Text
|
||||
| Photo of media: string * caption: string
|
||||
| Video of video: string * caption: string
|
||||
|
||||
type RelayArgs = {
|
||||
ReplyChatId: int64
|
||||
ReplyMessageId: int
|
||||
Relay: RelayType
|
||||
}
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
type BotUpdateType =
|
||||
| RelayUpdate of RelayArgs
|
||||
| YoutubeRepost of url: string
|
||||
| Ping
|
||||
| Skip
|
||||
|
||||
type ITgUpdateHandler =
|
||||
abstract member PostUpdate: Update -> unit
|
||||
|
||||
type ITgService =
|
||||
abstract member PostRelay: args: RelayArgs -> unit
|
||||
abstract member PostMessageToAdminChat: text: string -> unit
|
||||
abstract member Ping: unit -> unit
|
||||
abstract member PostVideo: url: string * savePath: string * externalId: Guid -> unit
|
@ -9,38 +9,7 @@ open System.Threading.Tasks
|
||||
open Microsoft.FSharp.Core
|
||||
open Newtonsoft.Json
|
||||
open PublishHelperBot.Environment
|
||||
open Telegram.Bot
|
||||
open Telegram.Bot.Types.InputFiles
|
||||
|
||||
type ChatId = int64
|
||||
|
||||
type CreateYoutubeDlJob = {
|
||||
url: string
|
||||
savePath: string
|
||||
}
|
||||
|
||||
type CreateYoutubeDlJobSuccess = {
|
||||
task: Guid
|
||||
}
|
||||
|
||||
type YoutubeDlStateResponse = {
|
||||
state: string
|
||||
}
|
||||
|
||||
type YoutubeDlError = {
|
||||
message: string
|
||||
}
|
||||
|
||||
type CreateJobResult = Result<CreateYoutubeDlJobSuccess, YoutubeDlError>
|
||||
|
||||
type CheckJobResult = Result<YoutubeDlStateResponse, YoutubeDlError>
|
||||
|
||||
type CleanJobResult = Result<YoutubeDlStateResponse, YoutubeDlError>
|
||||
|
||||
type IYoutubeDlClient =
|
||||
abstract member CreateJob: CreateYoutubeDlJob -> Async<CreateJobResult>
|
||||
abstract member CheckJob: externalId: Guid -> Async<CheckJobResult>
|
||||
abstract member CleanJob: externalId: Guid -> Async<CleanJobResult>
|
||||
open PublishHelperBot.Types
|
||||
|
||||
type YoutubeDlClientConfig = {
|
||||
BaseUrl: string
|
||||
@ -49,7 +18,6 @@ type YoutubeDlClientConfig = {
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module YoutubeDlClient =
|
||||
|
||||
type private YoutubeDlClientActions = Create | Check | Delete
|
||||
|
||||
type private HttpMethods = GET | POST | DELETE
|
||||
@ -192,69 +160,6 @@ module YoutubeDlClient =
|
||||
inbox.Post(CleanJob(externalId, tcs))
|
||||
tcs.Task |> Async.AwaitTask }
|
||||
|
||||
type TgServiceConfig = {
|
||||
Client: ITelegramBotClient
|
||||
ChannelId: ChatId
|
||||
AdminChatId: ChatId
|
||||
YoutubeDlClient: IYoutubeDlClient
|
||||
}
|
||||
|
||||
type ITgService =
|
||||
abstract member PostVideo: url: string * savePath: string * externalId: Guid -> unit
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module TgService =
|
||||
type private Msg =
|
||||
| PostVideo of url: string * savePath: string * externalId: Guid
|
||||
|
||||
let private createInbox (config: TgServiceConfig) =
|
||||
MailboxProcessor.Start(fun inbox ->
|
||||
let rec loop () =
|
||||
async {
|
||||
match! inbox.Receive() with
|
||||
| PostVideo (url, savePath, externalId) ->
|
||||
try
|
||||
try
|
||||
Logging.logger.Information("Reading file path = {path}", savePath)
|
||||
use file = File.OpenRead(savePath)
|
||||
if (file.Length / 1024L / 1024L) < 50L then
|
||||
let input = InputOnlineFile(file, Path.GetRandomFileName())
|
||||
let caption = $"Source: {url}"
|
||||
Logging.logger.Information(
|
||||
"Sending video to channel, channelId = {channelId}, caption = {caption}",
|
||||
config.ChannelId,
|
||||
caption)
|
||||
do! config.Client.SendVideoAsync(
|
||||
config.ChannelId,
|
||||
input,
|
||||
caption = caption
|
||||
)
|
||||
|> Async.AwaitTask |> Async.Ignore
|
||||
else
|
||||
do! config.Client.SendTextMessageAsync(config.AdminChatId, $"Да блять, видео вышло больше 50мб: {externalId}") |> Async.AwaitTask |> Async.Ignore
|
||||
with ex ->
|
||||
Logging.logger.Error(ex, "Failed to send video")
|
||||
finally
|
||||
Logging.logger.Information("Deleting file path = {path}", savePath)
|
||||
File.Delete(savePath)
|
||||
|
||||
match! config.YoutubeDlClient.CleanJob(externalId) with
|
||||
| Ok _ -> ()
|
||||
| Error _ -> ()
|
||||
return! loop ()
|
||||
}
|
||||
loop ()
|
||||
)
|
||||
|
||||
let createService config =
|
||||
let inbox = createInbox config
|
||||
{ new ITgService with
|
||||
member this.PostVideo(url, savePath, externalId) =
|
||||
inbox.Post(PostVideo(url, savePath, externalId)) }
|
||||
|
||||
type IYoutubeDlService =
|
||||
abstract member AddJob: url: string -> Async<Guid>
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module YoutubeDlService =
|
||||
type private Msg =
|
||||
@ -383,6 +288,7 @@ module YoutubeDlService =
|
||||
"Failed to receive video from youtube client, job = {job}, message = {message}",
|
||||
externalId,
|
||||
e.message)
|
||||
Logging.logger.Information("Deleting file path = {path}", job.SavePath)
|
||||
File.Delete(job.SavePath)
|
||||
return! loop jobQueue None
|
||||
|
||||
|
@ -20,14 +20,17 @@ def report_state(id: str):
|
||||
|
||||
|
||||
def load_video(url: str, file_path: str, id: str):
|
||||
try:
|
||||
opts = {
|
||||
"format": 'best[height<=480][ext=mp4]',
|
||||
"format": 'best[height<=480][ext=mp4]/bestvideo+bestaudio[ext=mp4]',
|
||||
"quiet": True,
|
||||
"outtmpl": file_path,
|
||||
"progress_hooks": [report_state(id)]
|
||||
}
|
||||
with YoutubeDL(opts) as ydl:
|
||||
ydl.download([url])
|
||||
except Exception:
|
||||
del backgroundJobs[id]
|
||||
|
||||
|
||||
@app.get("/api/info")
|
||||
|
Loading…
Reference in New Issue
Block a user