package main import ( "NukaNewsBot/commands" "context" "encoding/json" "fmt" "log" "os" "strings" "time" "github.com/PuerkitoBio/goquery" "github.com/bwmarrin/discordgo" "github.com/chromedp/chromedp" ) type Config struct { DiscordBotToken string `json:"discord_bot_token"` ChannelMap map[string]string `json:"channel_map"` RoleMap map[string]string `json:"role_map"` URL string `json:"url"` } func main() { //Initialize Time //date := "April 16, 2024" date := time.Now().Format("January 2, 2006") // At the beginning of the main function log.Println("Starting the bot...") // Open and read the configuration file configFile, err := os.Open("config.json") if err != nil { fmt.Println("Error opening config file:", err) return } defer configFile.Close() // Parse the configuration from JSON var config Config err = json.NewDecoder(configFile).Decode(&config) if err != nil { fmt.Println("Error decoding config JSON:", err) return } // After loading the configuration log.Println("Loaded configuration from config.json") // Access the Discord bot token and channel ID from the configuration token := config.DiscordBotToken if token == "" { fmt.Println("Discord bot token is not set in config file") return } channelMap := config.ChannelMap if len(channelMap) == 0 { fmt.Println("Channel map is not set in config file") return } roleMap := config.RoleMap if len(roleMap) == 0 { fmt.Println("Role map is not set in config file") return } url := config.URL if url == "" { fmt.Println("URL is not set in config file") return } // Create a new Discord session using the provided bot token dg, err := discordgo.New("Bot " + token) if err != nil { fmt.Println("Error creating Discord session:", err) return } // After creating the Discord session log.Println("Created Discord session") // Register commandHandler as a callback for message creation events dg.AddHandler(commands.CommandHandler) // After creating the Discord session log.Println("Listening for Commands") // Open a websocket connection to Discord and begin listening err = dg.Open() if err != nil { fmt.Println("Error opening Discord connection:", err) return } // After opening the Discord connection log.Println("Opened Discord connection") // Run the scraping and message sending function at start up //sendNotifications(dg, fetchUrl(url), channelMap, roleMap, date) // Schedule the scraping and message sending function to run once a day ticker := time.NewTicker(24 * time.Hour) defer ticker.Stop() // Run the scraping and message sending function when the ticker ticks go func() { for { select { case <-ticker.C: sendNotifications(dg, fetchUrl(url), channelMap, roleMap, date) } } }() // Wait here until CTRL-C or other term signal is received fmt.Println("Bot is now running. Press CTRL-C to exit.") <-make(chan struct{}) } func fetchUrl(url string) string { // Create a new context ctx := context.Background() ctx, cancel := chromedp.NewContext( ctx, chromedp.WithLogf(log.Printf), ) defer cancel() // Navigate to the Fallout news page var html string err := chromedp.Run(ctx, chromedp.Tasks{ chromedp.Navigate(url), chromedp.OuterHTML("html", &html), }) if err != nil { log.Fatal(err) } // Return the HTML content return html } // Add a function to extract relevant tags from the HTML content func extractNewsArticles(html string) []map[string]string { var articles []map[string]string doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) if err != nil { fmt.Println("Error parsing HTML:", err) return articles } // Find and extract each article doc.Find("article.news-module-feed-item").Each(func(i int, s *goquery.Selection) { article := make(map[string]string) // Extract link link, exists := s.Find("a.news-module-feed-item-image").Attr("href") if exists { article["link"] = "https://fallout.bethesda.net" + link } // Extract title title := strings.TrimSpace(s.Find("h3.news-module-feed-item-title").Text()) article["title"] = title // Extract tag tag := strings.TrimSpace(s.Find("span.news-module-feed-item-details-tag").Text()) article["tag"] = tag // Extract date date := strings.TrimSpace(s.Find("span.news-module-feed-item-details-date").Text()) article["date"] = date // Extract game game := strings.TrimSpace(s.Find("span.news-module-feed-item-details-game").Text()) article["game"] = game // Extract blurb blurb := strings.TrimSpace(s.Find("p.news-module-feed-item-blurb").Text()) article["blurb"] = blurb // Extract image URL imageURL, exists := s.Find("img.news-module-feed-item-image-tag").Attr("src") if exists { article["imageURL"] = "https:" + imageURL } articles = append(articles, article) }) return articles } func sendNotifications(session *discordgo.Session, html string, channelMap, roleMap map[string]string, specifiedDate string) { // Extract articles from the HTML content articles := extractNewsArticles(html) // Iterate over extracted articles for _, article := range articles { tag := article["tag"] date := article["date"] link := article["link"] blurb := article["blurb"] title := article["title"] imageURL := article["imageURL"] // Check if the tag is in the desired tags list if channelID, ok := channelMap[tag]; ok { // Check if the article's date matches the specified date if date == specifiedDate { // Create a rich embed embed := &discordgo.MessageEmbed{ Title: title, Description: blurb, URL: link, Image: &discordgo.MessageEmbedImage{ URL: imageURL, }, Color: 0x00ff00, // Green color for the embed } // Send a message to the corresponding Discord channel message := fmt.Sprintf("New <@&%s> have been released!", roleMap[tag]) _, err := session.ChannelMessageSendComplex(channelID, &discordgo.MessageSend{ Content: message, Embed: embed, AllowedMentions: &discordgo.MessageAllowedMentions{}, // Allow mentions }) if err != nil { fmt.Printf("Error sending message to Discord channel %s: %s\n", channelID, err) continue } fmt.Println("Message sent to Discord channel:", channelID) } } } }