package main import ( "flag" "fmt" "log" "os" "os/signal" "syscall" //"strings" "regexp" "context" "github.com/bwmarrin/discordgo" "github.com/wader/goutubedl" "mvdan.cc/xurls/v2" // Peak lazy ) func main() { // Lazyinator 9001 var ( session *discordgo.Session err error ) // Variables used for registering slash commands var ( commands = []*discordgo.ApplicationCommand { { Name: "ping", Description: "Hopefully replies with pong or else I'll be sad.", }, { Name: "sping", Description: "Hopefully replies with pong silently or else I'll be sad.", }, { Name: "slowping", Description: "Hopefully replies with pong 10 seconds later or else I'll be sad.", }, { Name: "version", Description: "Gets the bot's current version.", }, } commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate) { "ping": ping, "sping": sping, "slowping": slowping, "version": version, } ) // Bot params var ( GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") BotToken = flag.String("token", "", "Bot access token") ) flag.Parse() session, err = discordgo.New("Bot " + *BotToken) if err != nil { fmt.Println("error creating Discord session,", err) return } // This runs slash commands session.AddHandler(func(session *discordgo.Session, interaction *discordgo.InteractionCreate) { if h, ok := commandHandlers[interaction.ApplicationCommandData().Name]; ok { if interaction.Interaction.Member != nil { log.Printf("%s#%s invoked command %s", interaction.Member.User.Username, interaction.Member.User.Discriminator, interaction.ApplicationCommandData().Name) } else if interaction.Interaction.User != nil { log.Printf("%s#%s invoked command %s", interaction.User.Username, interaction.User.Discriminator, interaction.ApplicationCommandData().Name) } h(session, interaction) } }) // Message to print when bot is ready to go session.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { log.Printf("Logged in as: %v#%v\n", session.State.User.Username, session.State.User.Discriminator) }) // Register the messageCreate func as a callback for MessageCreate events. session.AddHandler(messageCreate) // In this example, we only care about receiving message events. //s.Identify.Intents = discordgo.IntentsGuildMessages // Open a websocket connection to Discord and begin listening. err = session.Open() if err != nil { fmt.Println("error opening connection,", err) return } log.Println("Adding commands...") registeredCommands, err := session.ApplicationCommandBulkOverwrite(session.State.User.ID, *GuildID, commands) if err != nil { log.Panic(err) } // Just incase defer session.Close() // Wait here until CTRL-C or other term signal is received. log.Println("Bot is running. Press CTRL-C to exit.") sc := make(chan os.Signal, 1) signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) <-sc log.Printf("Signal received, closing bot...") // Cleanly close down the Discord session. for _, v := range registeredCommands { err := session.ApplicationCommandDelete(session.State.User.ID, *GuildID, v.ID) if err != nil { log.Panicf("Cannot delete '%v' command: %v", v.Name, err) } } session.Close() } // This function will be called (due to AddHandler above) every time a new // message is created on any channel that the authenticated bot has access to. func messageCreate(session *discordgo.Session, message *discordgo.MessageCreate) { // Ignore all messages created by the bot itself // This isn't required in this specific example but it's a good practice. if message.Author.ID == session.State.User.ID { return } /* // If the message is "ping" reply with "Pong!" if message.Content == "ping" { session.ChannelMessageSend(message.ChannelID, "Pong!") } // If the message is "pong" reply with "Ping!" if message.Content == "pong" { session.ChannelMessageSend(message.ChannelID, "Ping!") } */ //log.Printf("[%s] %s#%s: %s", message.ID, message.Author.Username, message.Author.Discriminator, message.Content) rxStrict := xurls.Strict() urls := rxStrict.FindAllString(message.Content, -1) if len(urls) > 0 { response := discordgo.MessageSend { Content: "Woah there partner! I detect some services that aren't very privacy friendly. Let me help you with that!\n", Reference: message.Reference(), Files: []*discordgo.File{}, } log.Printf("Message %s has URLs!", message.ID) respond := false for _, url := range urls { if output, _ := regexp.MatchString("(http.*twitter.com/.*/status)|(http.*t.co/.*)", url); output { log.Println("Cringe twitter post detected.") if shortned, _ := regexp.MatchString("http.*t.co/.*", url); shortned { var err error log.Println("Short URL detected. Getting long version.") url, err = getRedirectURL(url) if err != nil { log.Println(err) continue } } response.Content = response.Content + regexp.MustCompile("https://twitter.com/").ReplaceAllString(url, "https://nitter.pussthecat.org/") goutubedl.Path = "yt-dlp" result, err := goutubedl.New(context.Background(), url, goutubedl.Options{}) if err != nil { // If it's complaining due to a lack of videos, don't care. if noVideo, _ := regexp.MatchString(".*There's no video in this tweet.*", err.Error()); ! noVideo { log.Println(err) } //session.ChannelMessageSend(message.ChannelID, err.Error()) continue } else { choice, err := getLargestFormat(result, 8*1024*1024) log.Printf("Choice: %s | Size: %fM\n", choice.FormatID, choice.FilesizeApprox/1024/1024) if err == nil { downloadResult, err := result.Download(context.Background(), choice.FormatID) if err != nil { log.Println(err) //session.ChannelMessageSend(message.ChannelID, err.Error()) continue } response.Files = append(response.Files, &discordgo.File { Name: fmt.Sprintf("%s.%s", result.Info.ID, choice.Ext), ContentType: "text/plain", // This is of course not true, but Discord doesn't give a shit Reader: downloadResult, }) defer downloadResult.Close() } else { log.Println(err) } } respond = true } } if respond { if result, err := session.ChannelMessageSendComplex(message.ChannelID, &response); err != nil { log.Println(result) log.Println(err) } else { log.Printf("Successfully responded to %s", message.ID) } } } }