diff --git a/app/bot/model.go b/app/bot/model.go index a01c27b..7c318ba 100644 --- a/app/bot/model.go +++ b/app/bot/model.go @@ -52,6 +52,7 @@ func NewBot(token string, commands commands.CallbackMap, enableMongo bool, url s Database: databaseConfig, } b.RegisterModule(youtube.NewModule(b)) + b.RegisterModule(youtube.NewModuleFull(b)) return b, nil } diff --git a/app/bot/service.go b/app/bot/service.go index 8a3bdaf..4c7db04 100644 --- a/app/bot/service.go +++ b/app/bot/service.go @@ -32,22 +32,31 @@ func (b *Bot) Run(timeout int) error { return nil } -func (b *Bot) notifyModules(message *tbot.Message) error { - var moduleErrs error +func (b *Bot) notifyModules(update *tbot.Update) error { + var moduleErrs *multierror.Error for _, module := range b.Modules { - if err := module.MessageUpdate(message); err != nil { - moduleErrs = multierror.Append(moduleErrs, err) + if msg := update.Message; msg != nil { + moduleErrs = multierror.Append(moduleErrs, module.MessageUpdate(msg)) + } + if callback := update.CallbackQuery; callback != nil { + callback.Message.Text = update.CallbackQuery.Data + moduleErrs = multierror.Append(moduleErrs, module.MessageUpdate(callback.Message)) } } - return moduleErrs + return moduleErrs.ErrorOrNil() } func (b *Bot) update(update tbot.Update) { // Ignore any non-Message Updates. - if update.Message == nil { + if update.Message == nil && update.CallbackQuery == nil { return } + if update.Message == nil && update.CallbackQuery != nil { + update.Message = update.CallbackQuery.Message + update.Message.Text = update.CallbackQuery.Data + } + log.Info.PrintTKV("[{{update_id}}] {{username}}: {{text}}", "update_id", update.UpdateID, "username", update.Message.From.UserName, @@ -55,7 +64,7 @@ func (b *Bot) update(update tbot.Update) { ) // Run through errors - if err := b.notifyModules(update.Message); err != nil { + if err := b.notifyModules(&update); err != nil { log.Error.Println("unexpected error!", err.Error()) } diff --git a/app/botmodule/youtube/model.go b/app/botmodule/youtube/model.go index 6936d4c..b6e9db1 100644 --- a/app/botmodule/youtube/model.go +++ b/app/botmodule/youtube/model.go @@ -13,6 +13,7 @@ type ( pattern *regexp.Regexp client youtube.Client botApiRepo public.BotApiRepo + askToDownload bool } YoutubeMessage struct { @@ -40,19 +41,34 @@ const ( WEBM VideoType = "video/webm" ) +const ( + downloadCommand = "/ytdl" +) + var ( - ytLinkRegex = "(((?:https?:)?\\/\\/)?((?:www|m)\\.)?((?:youtube\\.com))(\\/(shorts\\/))([\\w\\-]+)(\\S+)?)" + ytShortsLinkRegex = "(((?:https?:)?\\/\\/)?((?:www|m)\\.)?((?:youtube\\.com))(\\/(shorts\\/))([\\w\\-]+)(\\S+)?)" + ytLinkRegex = `(?m)(((?:https?:)?//)?((?:www|m).)?(((?:youtube\.com)/watch\?v=([\w]+)*))|(((?:https?:)?//)?((?:www|m).)?((?:youtu\.be/)([\w]+)*)))` log = logger.Factory.NewLogger("youtube") allowedQuality = []QualityType{HD, HD720, HD1080, MEDIUM} allowedType = []VideoType{MP4, MKV, WEBM} + + rShorts = regexp.MustCompile(ytShortsLinkRegex) + rFull = regexp.MustCompile(ytLinkRegex) ) func NewModule(botApiRepo public.BotApiRepo) *YoutubeModule { - r := regexp.MustCompile(ytLinkRegex) + return &YoutubeModule{ + pattern: rShorts, + client: youtube.Client{}, + botApiRepo: botApiRepo, + } +} +func NewModuleFull(botApiRepo public.BotApiRepo) *YoutubeModule { return &YoutubeModule{ - pattern: r, + pattern: rFull, client: youtube.Client{}, botApiRepo: botApiRepo, + askToDownload: true, } } diff --git a/app/botmodule/youtube/service.go b/app/botmodule/youtube/service.go index becb406..0883a52 100644 --- a/app/botmodule/youtube/service.go +++ b/app/botmodule/youtube/service.go @@ -156,36 +156,88 @@ func (yt *YoutubeModule) Download(links []string, folder string) ([]YoutubeMessa func (ytm *YoutubeModule) MessageUpdate(message *tbot.Message) error { links := ytm.Match(message.Text) + matches := len(links) > 0 - if len(links) != 0 && !message.From.IsBot { - loadingMsgCfg := tbot.NewMessage(message.Chat.ID, "loading...") - loadingMsgCfg.ReplyToMessageID = message.MessageID - var loadingMsg *tbot.Message = nil - if newLoadingMsg, err := ytm.botApiRepo.SendMessage(loadingMsgCfg); err != nil { - log.Error.Println("сan not reply loading message to the message: ", err.Error()) - } else { - loadingMsg = &newLoadingMsg - } + log.Info.Println("MessageUpdate ", message.Text, " ", links, " ", strings.HasPrefix(message.Text, downloadCommand)) - defer func() { - if err := ytm.botApiRepo.DeleteMessage(loadingMsg.Chat.ID, loadingMsg.MessageID); err != nil { - log.Error.PrintT("failed to remove message in Chat ", loadingMsg.Chat, " with links ", links, ", error: ", err.Error()) - } - }() + if !matches { + // Nothing to do here. + return nil + } - log.Info.PrintTKV( - "detected youtube short links of {{length}} length from {{user}}", - "length", len(links), "user", message.From.String()) + if strings.HasPrefix(message.Text, downloadCommand) { + if err := ytm.botApiRepo.DeleteMessage(message.Chat.ID, message.MessageID); err != nil { + log.Error.PrintT("failed to remove message in Chat ", message.Chat, " with links ", links, ", error: ", err.Error()) + } + return ytm.download(message, links) + } - folder, err := ioutil.TempDir("/tmp", "yt*") - if err != nil { + if ytm.askToDownload { + promptMsgCfg := tbot.NewMessage(message.Chat.ID, "wanna download it?") + promptMsgCfg.ReplyToMessageID = message.MessageID + + promptMsgCfg.ReplyMarkup = tbot.NewInlineKeyboardMarkup( + tbot.NewInlineKeyboardRow( + tbot.NewInlineKeyboardButtonData("Yes", fmt.Sprintf("%s %s", downloadCommand, links[0])), + tbot.NewInlineKeyboardButtonData("No", "-"), + ), + ) + if _, err := ytm.botApiRepo.SendMessage(promptMsgCfg); err != nil { + log.Error.Println("can not reply prompt message to the message: ", err.Error()) return err } + return nil + } + return ytm.download(message, links) +} - defer os.RemoveAll(folder) +func (ytm *YoutubeModule) download(message *tbot.Message, links []string) error { + loadingMsgCfg := tbot.NewMessage(message.Chat.ID, "loading...") + loadingMsgCfg.ReplyToMessageID = message.MessageID + var loadingMsg *tbot.Message = nil + if newLoadingMsg, err := ytm.botApiRepo.SendMessage(loadingMsgCfg); err != nil { + log.Error.Println("сan not reply loading message to the message: ", err.Error()) + } else { + loadingMsg = &newLoadingMsg + } - yms, err := ytm.Download(links, folder) - if err != nil { + defer func() { + if err := ytm.botApiRepo.DeleteMessage(loadingMsg.Chat.ID, loadingMsg.MessageID); err != nil { + log.Error.PrintT("failed to remove message in Chat ", loadingMsg.Chat, " with links ", links, ", error: ", err.Error()) + } + }() + + log.Info.PrintTKV( + "detected youtube short links of {{length}} length from {{user}}", + "length", len(links), "user", message.From.String()) + + folder, err := ioutil.TempDir("/tmp", "yt*") + if err != nil { + return err + } + + defer os.RemoveAll(folder) + + yms, err := ytm.Download(links, folder) + if err != nil { + log.Error.Println(err.Error()) + // Let's try to reply to message with error message + v := tbot.NewMessage(message.Chat.ID, fmt.Sprintf("failed to process video: %s", err.Error())) + v.ReplyToMessageID = message.MessageID + + if _, err := ytm.botApiRepo.SendMessage(v); err != nil { + log.Error.Println("failed to reply to message: ", err.Error()) + } + return err + } + var filesErrs error + for _, ym := range yms { + v := tbot.NewVideoUpload(message.Chat.ID, ym.Path) + v.Caption = ym.FormCaption() + v.ParseMode = tbot.ModeMarkdown + v.ReplyToMessageID = message.MessageID + + if _, err = ytm.botApiRepo.SendMessage(v); err != nil { log.Error.Println(err.Error()) // Let's try to reply to message with error message v := tbot.NewMessage(message.Chat.ID, fmt.Sprintf("failed to process video: %s", err.Error())) @@ -193,32 +245,12 @@ func (ytm *YoutubeModule) MessageUpdate(message *tbot.Message) error { if _, err := ytm.botApiRepo.SendMessage(v); err != nil { log.Error.Println("failed to reply to message: ", err.Error()) - } - return err - } - var filesErrs error - for _, ym := range yms { - v := tbot.NewVideoUpload(message.Chat.ID, ym.Path) - v.Caption = ym.FormCaption() - v.ParseMode = tbot.ModeMarkdown - v.ReplyToMessageID = message.MessageID - - if _, err = ytm.botApiRepo.SendMessage(v); err != nil { - log.Error.Println(err.Error()) - // Let's try to reply to message with error message - v := tbot.NewMessage(message.Chat.ID, fmt.Sprintf("failed to process video: %s", err.Error())) - v.ReplyToMessageID = message.MessageID - - if _, err := ytm.botApiRepo.SendMessage(v); err != nil { - log.Error.Println("failed to reply to message: ", err.Error()) - if err := ytm.botApiRepo.DeleteMessage(loadingMsg.Chat.ID, loadingMsg.MessageID); err != nil { - log.Error.Println(err.Error()) - } - filesErrs = multierror.Append(filesErrs, err) + if err := ytm.botApiRepo.DeleteMessage(loadingMsg.Chat.ID, loadingMsg.MessageID); err != nil { + log.Error.Println(err.Error()) } + filesErrs = multierror.Append(filesErrs, err) } } - return filesErrs } - return nil -} + return filesErrs +} \ No newline at end of file