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,71 +1,60 @@
|
|||||||
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 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 youtubeDlClient =
|
||||||
|
YoutubeDlClient.createClient {
|
||||||
|
Client = new HttpClient()
|
||||||
|
BaseUrl = config.YoutubeDlUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
let tgService =
|
||||||
|
TgService.createService {
|
||||||
|
Client = botClient
|
||||||
|
ChannelId = config.chanelId
|
||||||
|
YoutubeDlClient = youtubeDlClient
|
||||||
|
AdminChatId = config.adminChatId
|
||||||
|
}
|
||||||
|
|
||||||
let youtubeDlService =
|
let youtubeDlService =
|
||||||
let youtubeDlClient =
|
|
||||||
YoutubeDlClient.createClient {
|
|
||||||
Client = new HttpClient()
|
|
||||||
BaseUrl = config.YoutubeDlUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
let tgService =
|
|
||||||
TgService.createService {
|
|
||||||
Client = botClient
|
|
||||||
ChannelId = config.chanelId
|
|
||||||
YoutubeDlClient = youtubeDlClient
|
|
||||||
AdminChatId = config.adminChatId
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
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) =
|
|
||||||
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):
|
||||||
opts = {
|
try:
|
||||||
"format": 'best[height<=480][ext=mp4]',
|
opts = {
|
||||||
"quiet": True,
|
"format": 'best[height<=480][ext=mp4]/bestvideo+bestaudio[ext=mp4]',
|
||||||
"outtmpl": file_path,
|
"quiet": True,
|
||||||
"progress_hooks": [report_state(id)]
|
"outtmpl": file_path,
|
||||||
}
|
"progress_hooks": [report_state(id)]
|
||||||
with YoutubeDL(opts) as ydl:
|
}
|
||||||
ydl.download([url])
|
with YoutubeDL(opts) as ydl:
|
||||||
|
ydl.download([url])
|
||||||
|
except Exception:
|
||||||
|
del backgroundJobs[id]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/info")
|
@app.get("/api/info")
|
||||||
|
Loading…
Reference in New Issue
Block a user