publishhelperbot/PublishHelperBot/Telegram.fs
2023-03-04 11:40:25 +00:00

322 lines
13 KiB
Forth

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 &&
hasText update.Message &&
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 PostVideoArgs
| 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 args ->
try
Logging.logger.Information("PostVideo args = {args}", args)
Logging.logger.Information("Reading file path = {path}", args.SavePath)
use file = File.OpenRead(args.SavePath)
if (file.Length / 1024L / 1024L) < 50L then
let input = InputOnlineFile(file, Path.GetRandomFileName())
let caption = $"Source: {args.Url}"
Logging.logger.Information(
"Sending video to channel, channelId = {channelId}, caption = {caption}",
config.ChannelId,
caption)
let dimensions =
args.Width
|> Option.bind (fun w ->
args.Height
|> Option.map (fun h -> (w, h))
)
let sendVideo =
match dimensions with
| Some (width, height) ->
config.Client.SendVideoAsync(
config.ChannelId,
input,
caption = caption,
width = width,
height = height
)
| None ->
config.Client.SendVideoAsync(
config.ChannelId,
input,
caption = caption
)
do! sendVideo
|> Async.AwaitTask
|> Async.Catch
|> Async.Ignore
else
inbox.Post(PostMessageToAdminChat($"Да блять, видео вышло больше 50мб: {args.ExternalId}"))
finally
Logging.logger.Information("Deleting file path = {path}", args.SavePath)
File.Delete(args.SavePath)
match! config.YoutubeDlClient.CleanJob(args.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(args) =
inbox.Post(PostVideo(args)) }
[<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)) }