Merge branch 'ref' into 'master'
ytdl exception handling, move to boxes, ГЫЧА))) See merge request alzheimer-gaming/publishhelperbot!3
This commit is contained in:
		
						commit
						748523ce7b
					
				
					 7 changed files with 426 additions and 262 deletions
				
			
		| 
						 | 
					@ -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,30 +1,25 @@
 | 
				
			||||||
open System
 | 
					open System
 | 
				
			||||||
open System.Net.Http
 | 
					open System.Net.Http
 | 
				
			||||||
open System.Threading
 | 
					 | 
				
			||||||
open System.Threading.Tasks
 | 
					open System.Threading.Tasks
 | 
				
			||||||
open PublishHelperBot.Handlers
 | 
					 | 
				
			||||||
open PublishHelperBot.Environment
 | 
					open PublishHelperBot.Environment
 | 
				
			||||||
open PublishHelperBot.YoutubeDl
 | 
					open PublishHelperBot.YoutubeDl
 | 
				
			||||||
open Serilog.Core
 | 
					open PublishHelperBot.Telegram
 | 
				
			||||||
open Telegram.Bot
 | 
					open Telegram.Bot
 | 
				
			||||||
open Telegram.Bot.Polling
 | 
					open Telegram.Bot.Polling
 | 
				
			||||||
open Telegram.Bot.Types
 | 
					open Telegram.Bot.Types
 | 
				
			||||||
open Telegram.Bot.Types.Enums
 | 
					open Telegram.Bot.Types.Enums
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let createBot (config: BotConfig, http: HttpClient) = TelegramBotClient(config.token, http)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let config = createConfig "SBPB_CONFIG_PATH"
 | 
					let config = createConfig "SBPB_CONFIG_PATH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let botClient = createBot (config, new HttpClient())
 | 
					let botClient = TelegramBotClient (config.token, new HttpClient())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let youtubeDlService =
 | 
					let youtubeDlClient =
 | 
				
			||||||
    let youtubeDlClient =
 | 
					 | 
				
			||||||
    YoutubeDlClient.createClient {
 | 
					    YoutubeDlClient.createClient {
 | 
				
			||||||
        Client = new HttpClient()
 | 
					        Client = new HttpClient()
 | 
				
			||||||
        BaseUrl = config.YoutubeDlUrl
 | 
					        BaseUrl = config.YoutubeDlUrl
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let tgService =
 | 
					let tgService =
 | 
				
			||||||
    TgService.createService {
 | 
					    TgService.createService {
 | 
				
			||||||
        Client = botClient
 | 
					        Client = botClient
 | 
				
			||||||
        ChannelId = config.chanelId
 | 
					        ChannelId = config.chanelId
 | 
				
			||||||
| 
						 | 
					@ -32,46 +27,34 @@ let youtubeDlService =
 | 
				
			||||||
        AdminChatId = config.adminChatId
 | 
					        AdminChatId = config.adminChatId
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let youtubeDlService =
 | 
				
			||||||
    YoutubeDlService.createService youtubeDlClient tgService
 | 
					    YoutubeDlService.createService youtubeDlClient tgService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let startDate = DateTime.UtcNow
 | 
					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 handlePollingError _ (e: Exception) _ =
 | 
				
			||||||
    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)
 | 
					 | 
				
			||||||
    try 
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
    with
 | 
					 | 
				
			||||||
    | e ->
 | 
					 | 
				
			||||||
        $"Случилась страшная хуйня:\n {e.Message}\n{e.StackTrace}" |> Logging.logger.Error
 | 
					 | 
				
			||||||
        Task.CompletedTask
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let handlePollingError (_: ITelegramBotClient) (e: Exception) (_: CancellationToken) =
 | 
					 | 
				
			||||||
    Logging.logger.Error(e, "Polling error")
 | 
					    Logging.logger.Error(e, "Polling error")
 | 
				
			||||||
    Task.CompletedTask
 | 
					    Task.CompletedTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let receiverOptions = ReceiverOptions(AllowedUpdates = Array.zeroCreate<UpdateType> 0)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Logging.logger.Information("Starting bot")
 | 
					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("Я родился")
 | 
					Logging.logger.Information("Я родился")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Console.ReadKey() |> ignore
 | 
					Console.ReadKey() |> ignore
 | 
				
			||||||
| 
						 | 
					@ -12,8 +12,9 @@
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
        <Compile Include="Environment.fs" />
 | 
					        <Compile Include="Environment.fs" />
 | 
				
			||||||
 | 
					        <Compile Include="Types.fs" />
 | 
				
			||||||
 | 
					        <Compile Include="Telegram.fs" />
 | 
				
			||||||
        <Compile Include="YoutubeDl.fs" />
 | 
					        <Compile Include="YoutubeDl.fs" />
 | 
				
			||||||
        <Compile Include="Handlers.fs" />
 | 
					 | 
				
			||||||
        <Compile Include="Program.fs" />
 | 
					        <Compile Include="Program.fs" />
 | 
				
			||||||
        <Content Include="config.example.json" />
 | 
					        <Content Include="config.example.json" />
 | 
				
			||||||
    </ItemGroup>
 | 
					    </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 Microsoft.FSharp.Core
 | 
				
			||||||
open Newtonsoft.Json
 | 
					open Newtonsoft.Json
 | 
				
			||||||
open PublishHelperBot.Environment
 | 
					open PublishHelperBot.Environment
 | 
				
			||||||
open Telegram.Bot
 | 
					open PublishHelperBot.Types
 | 
				
			||||||
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>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type YoutubeDlClientConfig = {
 | 
					type YoutubeDlClientConfig = {
 | 
				
			||||||
    BaseUrl: string
 | 
					    BaseUrl: string
 | 
				
			||||||
| 
						 | 
					@ -49,7 +18,6 @@ type YoutubeDlClientConfig = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[<RequireQualifiedAccess>]
 | 
					[<RequireQualifiedAccess>]
 | 
				
			||||||
module YoutubeDlClient =
 | 
					module YoutubeDlClient =
 | 
				
			||||||
 | 
					 | 
				
			||||||
    type private YoutubeDlClientActions = Create | Check | Delete
 | 
					    type private YoutubeDlClientActions = Create | Check | Delete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    type private HttpMethods = GET | POST | DELETE
 | 
					    type private HttpMethods = GET | POST | DELETE
 | 
				
			||||||
| 
						 | 
					@ -192,69 +160,6 @@ module YoutubeDlClient =
 | 
				
			||||||
                inbox.Post(CleanJob(externalId, tcs))
 | 
					                inbox.Post(CleanJob(externalId, tcs))
 | 
				
			||||||
                tcs.Task |> Async.AwaitTask }
 | 
					                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>]
 | 
					[<RequireQualifiedAccess>]
 | 
				
			||||||
module YoutubeDlService =
 | 
					module YoutubeDlService =
 | 
				
			||||||
    type private Msg =
 | 
					    type private Msg =
 | 
				
			||||||
| 
						 | 
					@ -383,6 +288,7 @@ module YoutubeDlService =
 | 
				
			||||||
                                        "Failed to receive video from youtube client, job = {job}, message = {message}",
 | 
					                                        "Failed to receive video from youtube client, job = {job}, message = {message}",
 | 
				
			||||||
                                        externalId,
 | 
					                                        externalId,
 | 
				
			||||||
                                        e.message)
 | 
					                                        e.message)
 | 
				
			||||||
 | 
					                                    Logging.logger.Information("Deleting file path = {path}", job.SavePath)
 | 
				
			||||||
                                    File.Delete(job.SavePath)
 | 
					                                    File.Delete(job.SavePath)
 | 
				
			||||||
                                    return! loop jobQueue None
 | 
					                                    return! loop jobQueue None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,14 +20,17 @@ def report_state(id: str):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_video(url: str, file_path: str, id: str):
 | 
					def load_video(url: str, file_path: str, id: str):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
        opts = {
 | 
					        opts = {
 | 
				
			||||||
        "format": 'best[height<=480][ext=mp4]',
 | 
					            "format": 'best[height<=480][ext=mp4]/best[ext=mp4]',
 | 
				
			||||||
            "quiet": True,
 | 
					            "quiet": True,
 | 
				
			||||||
            "outtmpl": file_path,
 | 
					            "outtmpl": file_path,
 | 
				
			||||||
            "progress_hooks": [report_state(id)]
 | 
					            "progress_hooks": [report_state(id)]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        with YoutubeDL(opts) as ydl:
 | 
					        with YoutubeDL(opts) as ydl:
 | 
				
			||||||
            ydl.download([url])
 | 
					            ydl.download([url])
 | 
				
			||||||
 | 
					    except Exception:
 | 
				
			||||||
 | 
					        del backgroundJobs[id]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@app.get("/api/info")
 | 
					@app.get("/api/info")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue