Add files via upload

This commit is contained in:
Richard Mwewa
2023-12-03 18:54:23 +00:00
committed by GitHub
parent c544ede53b
commit 145d33ef9e
27 changed files with 6768 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
Imports System.IO
Imports System.Net.Http
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
''' <summary>
''' Handles requests to Reddit and Github APIs.
''' </summary>
Public Class ApiHandler
Public Property LogFile As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RedditPostScrapingTool", "logs", $"debug.log")
Public Property Headers As String = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.2 Safari/605.1.15"
Public Property UpdatesEndpoint As String = "https://api.github.com/repos/bellingcat/reddit-post-scraping-tool/releases/latest"
''' <summary>
''' Asyncrosnously scrape Reddit data.
''' </summary>
''' <returns>Json object containing scraped data.</returns>
Public Async Function AsyncGetPosts(subreddit As String, listing As String, limit As Integer, timeframe As String) As Task(Of JArray)
Dim PostsEndpoint As String = $"https://www.reddit.com/r/{subreddit}/{listing}.json?limit={limit}&t={timeframe}"
Return Await PaginatedPosts(endpoint:=PostsEndpoint, limit:=limit)
End Function
''' <summary>
''' Retrieves posts in a paginated manner until the specified limit is reached.
''' </summary>
''' <param name="endpoint">The API endpoint for retrieving posts.</param>
''' <param name="limit">The limit on the number of posts to retrieve.</param>
''' <returns>A Task(Of JArray) representing the asynchronous operation, which upon completion returns a JArray of posts.</returns>
Private Async Function PaginatedPosts(endpoint As String, limit As Integer) As Task(Of JArray)
Dim allPosts As New JArray()
Dim lastPostId As String = ""
Dim useAfter As Boolean = limit > 100
While allPosts.Count < limit
Dim endpointWithAfter As String = If(useAfter And Not String.IsNullOrEmpty(lastPostId), $"{endpoint}&after={lastPostId}", endpoint)
Dim postsData As JObject = Await AsyncGetData(endpoint:=endpointWithAfter)
Dim postsChildren As JArray = postsData("data")("children")
If postsChildren.Count = 0 Then
Exit While
End If
allPosts.Merge(postsChildren)
lastPostId = postsChildren.Last("data")("id").ToString()
End While
Return allPosts
End Function
''' <summary>
''' Asyncrosnously gets remote version information from the repository release page.
''' </summary>
''' <returns>Json object containing update data.</returns>
Public Async Function CheckUpdatesAsync() As Task(Of JObject)
Return Await AsyncGetData(endpoint:=UpdatesEndpoint)
End Function
''' <summary>
''' Asyncronously retrieves a JObject from the specified endpoint.
''' </summary>
''' <param name="endpoint">The URL endpoint to retrieve data from.</param>
''' <returns>A JObject containing the retrieved data.</returns>
Private Async Function AsyncGetData(endpoint As String) As Task(Of JObject)
Try
Using httpClient As New HttpClient()
httpClient.DefaultRequestHeaders.Add("User-Agent", Headers)
Dim response As HttpResponseMessage = Await httpClient.GetAsync(endpoint)
If response.IsSuccessStatusCode Then
Dim json As String = response.Content.ReadAsStringAsync().Result
Dim data As JObject = JsonConvert.DeserializeObject(Of JObject)(json)
Return data
Else
MessageBox.Show(response.ReasonPhrase, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Using
Catch ex As Exception
My.Computer.FileSystem.WriteAllText(LogFile, $"{DateTime.Now}: {ex}{Environment.NewLine}", True)
MessageBox.Show($"{ex.Message}. Please see the debug log '{LogFile}' for more information.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Return New JObject()
End Function
End Class

View File

@@ -0,0 +1,115 @@
Imports Newtonsoft.Json.Linq
Public Class PostsProcessor
Private Shared ReadOnly ApiHandler As New ApiHandler()
''' <summary>
''' Checks if the given Reddit post contains the given keyword in its text.
''' </summary>
''' <param name="post">The Reddit post to check.</param>
''' <param name="keyword">The keyword to check for.</param>
''' <returns>True if the post contains the keyword, False otherwise.</returns>
Public Shared Function PostContainsKeyword(post As JObject, keyword As String) As Boolean
Return post("data")("selftext").ToString.ToLower(Globalization.CultureInfo.InvariantCulture).Contains(keyword.ToLower(System.Globalization.CultureInfo.InvariantCulture))
End Function
''' <summary>
''' Collects user inputs, fetches Reddit posts based on the inputs, checks if posts contain the keyword, and saves posts to a JSON file if necessary.
''' </summary>
''' <param name="JSONToolStripMenuItem">Indicates whether to save the posts to a JSON file.</param>
''' <remarks>
''' This function initializes the DataGridView, iterates over each post, adds the posts containing the keyword to the DataGridView and updates the UI.
''' It also shows a message if the keyword was not found in any of the posts or if the inputs are empty.
''' </remarks>
Public Shared Async Sub ProcessRedditPosts(settings)
' Collect inputs from the user.
Dim inputs = Utilities.CollectInputs()
If inputs.HasValue Then
' Fetch Reddit posts based on the inputs.
Dim processor As New PostsProcessor()
Dim posts As JArray = Await ApiHandler.AsyncGetPosts(subreddit:=inputs.Value.Subreddit, listing:=inputs.Value.Listing, limit:=inputs.Value.Limit, timeframe:=inputs.Value.Timeframe)
Dim totalPosts As Integer = 0
Dim keywordFound As Boolean = False
Dim foundPosts As Integer = 0
Dim foundPostsList As New JArray
PostsWindow.DataGridViewPosts.Rows.Clear()
PostsWindow.DataGridViewPosts.Columns.Clear()
PostsWindow.DataGridViewPosts.Columns.Add("PostCount", "Index")
PostsWindow.DataGridViewPosts.Columns.Add("PostAuthor", "Author")
PostsWindow.DataGridViewPosts.Columns.Add("PostID", "ID")
PostsWindow.DataGridViewPosts.Columns.Add("PostTitle", "Title")
PostsWindow.DataGridViewPosts.Columns.Add("PostText", "Text")
PostsWindow.DataGridViewPosts.Columns.Add("PostSubreddit", "Subreddit")
PostsWindow.DataGridViewPosts.Columns.Add("SubredditVisibility", "Subreddit Type")
PostsWindow.DataGridViewPosts.Columns.Add("PostThumbnail", "Thumbnail")
PostsWindow.DataGridViewPosts.Columns.Add("PostIsNSFW", "NSFW")
PostsWindow.DataGridViewPosts.Columns.Add("PostIsGilded", "Is Gilded")
PostsWindow.DataGridViewPosts.Columns.Add("PostUpvotes", "Upvotes")
PostsWindow.DataGridViewPosts.Columns.Add("PostUpvoteRatio", "Upvote Ratio")
PostsWindow.DataGridViewPosts.Columns.Add("PostDownvotes", "Downvotes")
PostsWindow.DataGridViewPosts.Columns.Add("PostIsCrosspostable", "↪️ Is Shareable")
PostsWindow.DataGridViewPosts.Columns.Add("PostScore", "Score")
PostsWindow.DataGridViewPosts.Columns.Add("PostCategory", "Category")
PostsWindow.DataGridViewPosts.Columns.Add("PostDomain", "Domain")
PostsWindow.DataGridViewPosts.Columns.Add("PostPermalink", "Permalink")
PostsWindow.DataGridViewPosts.Columns.Add("PostCreatedAt", "Created At")
' Iterate over each post.
For Each post In posts
totalPosts += 1
' Check if the post contains the keyword
If PostsProcessor.PostContainsKeyword(post, inputs.Value.Keyword.ToLower(Globalization.CultureInfo.InvariantCulture)) Then
foundPosts += 1
foundPostsList.Add(post)
PostsWindow.DataGridViewPosts.Rows.Add(totalPosts,
post("data")("author"),
post("data")("id"),
post("data")("title"),
post("data")("selftext"),
post("data")("subreddit_name_prefixed"),
post("data")("subreddit_type"),
post("data")("thumbnail"),
post("data")("over_18"),
post("data")("gilded"),
post("data")("ups"),
post("data")("upvote_ratio"),
post("data")("downs"),
post("data")("is_crosspostable"),
post("data")("score"),
post("data")("category"),
post("data")("domain"),
post("data")("permalink"),
post("data")("created"))
PostsWindow.Text = $"Showing {foundPosts}/{inputs.Value.Limit} {inputs.Value.Listing} posts containing the word {inputs.Value.Keyword}, from r/{inputs.Value.Subreddit}"
PostsWindow.Show()
keywordFound = True
End If
Next
' Check if the keyword was found in any posts
If Not keywordFound Then
MessageBox.Show($"Keyword `{inputs.Value.Keyword}` was not found in any of the " + posts("data")("children").Count.ToString(Globalization.CultureInfo.InvariantCulture) _
+ $" {inputs.Value.Listing} posts from r/{inputs.Value.Subreddit}", "Not Found", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
If settings.SaveToJson Then
' Save posts to a JSON file if SaveToJson is True.
Utilities.SavePostsToJson(posts:=foundPostsList)
End If
If settings.SaveToCsv Then
' Save posts to a CSV file if SaveToCsv is True.
Utilities.SavePostsToCSV(posts:=foundPostsList)
End If
Else
End If
End Sub
End Class

View File

@@ -0,0 +1,230 @@
Imports System.IO
Imports System.Text.Json
Imports Newtonsoft.Json.Linq
Public Class SettingsManager
''' <summary>
''' Represents the Dark Mode property.
''' Indicates whether the dark mode is enabled or disabled.
''' </summary>
Public Property DarkMode As Boolean
Public Property SaveToJson As Boolean
Public Property SaveToCsv As Boolean
Private ReadOnly settingsFilePath As String = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RPST",
"settings.json"
)
''' <summary>
''' Loads application settings from the 'settings.json' file.
''' If the settings file doesn't exist, it creates a new file with default settings.
''' </summary>
Public Sub LoadSettings()
' Check if the settings.json file exists
' and load the configurations from it
If File.Exists(settingsFilePath) Then
Dim json As String = File.ReadAllText(settingsFilePath)
Dim options As New JsonSerializerOptions With {.PropertyNameCaseInsensitive = True}
Dim settings = JsonSerializer.Deserialize(Of SettingsManager)(json, options)
DarkMode = settings.DarkMode
SaveToJson = settings.SaveToJson
SaveToCsv = settings.SaveToCsv
MainWindow.DarkModeToolStripMenuItem.Checked = settings.DarkMode
MainWindow.ToJSONToolStripMenuItem.Checked = settings.SaveToJson
MainWindow.ToCSVToolStripMenuItem.Checked = settings.SaveToCsv
Else
' Settings file does not exist
' Create a new file with default settings 'False'
Dim defaultSettings = New SettingsManager With {.DarkMode = False, .SaveToCsv = False, .SaveToJson = False}
Dim jsonOutput = JsonSerializer.Serialize(defaultSettings)
File.WriteAllText(settingsFilePath, jsonOutput)
SaveToJson = False
SaveToCsv = False
MainWindow.ToJSONToolStripMenuItem.Checked = False
MainWindow.ToCSVToolStripMenuItem.Checked = False
If Utilities.IsSystemDarkTheme() Then
DarkMode = True
MainWindow.DarkModeToolStripMenuItem.Checked = True
Else
DarkMode = False
MainWindow.DarkModeToolStripMenuItem.Checked = False
End If
End If
End Sub
''' <summary>
''' Retrieves application settings from a JSON file.
''' </summary>
''' <returns>A Dictionary containing the names and values of all settings.
''' If the settings file doesn't exist, returns a Dictionary with default values.</returns>
Private Function GetSettings() As Dictionary(Of String, Object)
Dim settings As New Dictionary(Of String, Object)
If File.Exists(settingsFilePath) Then
' Read and parse the JSON settings file.
Dim json As String = File.ReadAllText(settingsFilePath)
Dim jObject As JObject = JObject.Parse(json)
' Loop through each property in the JObject and add it to the settings Dictionary.
For Each item As JProperty In jObject.Properties()
settings.Add(item.Name, item.Value.ToObject(Of Object)())
Next
Else
End If
Return settings
End Function
''' <summary>
''' Saves the provided settings to the 'settings.json' file.
''' </summary>
''' <param name="settings">An instance of the SettingsManager containing the configurations to be saved.</param>
Private Sub SaveSettings(settings)
Dim jsonOutput = JsonSerializer.Serialize(settings)
File.WriteAllText(settingsFilePath, jsonOutput)
End Sub
''' <summary>
''' Applies the current settings to the application's interface. This includes
''' toggling SaveToJson, SaveToCsv, and applying the visual theme based on the Dark Mode setting.
''' </summary>
Public Sub ApplySettings()
' Retrieve the current settings
Dim settings As Dictionary(Of String, Object) = GetSettings()
' Apply the SaveToJson setting to the menu item checkbox
MainWindow.ToJSONToolStripMenuItem.Checked = CBool(settings("SaveToJson"))
' Apply the SaveToCsv setting to the menu item checkbox
MainWindow.ToCSVToolStripMenuItem.Checked = CBool(settings("SaveToCsv"))
' Apply the color scheme based on the Dark Mode setting
ApplyColorScheme(isDarkMode:=CBool(settings("DarkMode")))
End Sub
''' <summary>
''' Applies the color scheme based on the given Dark Mode setting.
''' Colors are defined in a mapping for easier maintenance and flexibility.
''' </summary>
''' <param name="isDarkMode">Indicates whether Dark Mode is enabled.</param>
Public Shared Sub ApplyColorScheme(ByVal isDarkMode As Boolean)
' Initialize color mapping
Dim colorMap As New Dictionary(Of String, Color)
If isDarkMode Then
' Dark Mode colors
colorMap("MainBackground") = ColorTranslator.FromHtml("#FF121212")
colorMap("TextBoxBackground") = ColorTranslator.FromHtml("#FF2E2E2E")
colorMap("Foreground") = SystemColors.Control
colorMap("MenuBackground") = ColorTranslator.FromHtml("#FF121212")
colorMap("AboutBackground") = ColorTranslator.FromHtml("#FF121212")
colorMap("AboutForeground") = SystemColors.Control
colorMap("TabPageBackground") = ColorTranslator.FromHtml("#FF2E2E2E")
colorMap("TabPageForeground") = SystemColors.Control
colorMap("ButtonForeground") = Color.Black
Else
' Light Mode colors
colorMap("MainBackground") = Color.Gainsboro
colorMap("TextBoxBackground") = SystemColors.Control
colorMap("Foreground") = ColorTranslator.FromHtml("#FF121212")
colorMap("MenuBackground") = Color.Gainsboro
colorMap("AboutBackground") = Color.Gainsboro
colorMap("AboutForeground") = SystemColors.WindowText
colorMap("TabPageBackground") = SystemColors.Control
colorMap("TabPageForeground") = SystemColors.WindowText
colorMap("ButtonForeground") = Color.Black
End If
' Applying Main Form colors
MainWindow.BackColor = colorMap("MainBackground")
MainWindow.TextBoxKeyword.BackColor = colorMap("TextBoxBackground")
MainWindow.TextBoxSubreddit.BackColor = colorMap("TextBoxBackground")
MainWindow.NumericUpDownLimit.BackColor = colorMap("TextBoxBackground")
MainWindow.ComboBoxListing.BackColor = colorMap("TextBoxBackground")
MainWindow.ComboBoxTimeframe.BackColor = colorMap("TextBoxBackground")
MainWindow.TextBoxKeyword.ForeColor = colorMap("Foreground")
MainWindow.TextBoxSubreddit.ForeColor = colorMap("Foreground")
MainWindow.NumericUpDownLimit.ForeColor = colorMap("Foreground")
MainWindow.ComboBoxListing.ForeColor = colorMap("Foreground")
MainWindow.ComboBoxTimeframe.ForeColor = colorMap("Foreground")
MainWindow.LabelKeyword.ForeColor = colorMap("Foreground")
MainWindow.LabelSubreddit.ForeColor = colorMap("Foreground")
MainWindow.LabelLimit.ForeColor = colorMap("Foreground")
MainWindow.LabelListing.ForeColor = colorMap("Foreground")
MainWindow.LabelTimeframe.ForeColor = colorMap("Foreground")
' Applying Right-Click Menu colors
MainWindow.SettingsToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.DarkModeToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.SavePostsToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.ToJSONToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.ToCSVToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.AboutToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.CheckForUpdatesToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.QuitToolStripMenuItem.BackColor = colorMap("MenuBackground")
MainWindow.SettingsToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.DarkModeToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.SavePostsToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.ToJSONToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.ToCSVToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.AboutToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.CheckForUpdatesToolStripMenuItem.ForeColor = colorMap("Foreground")
MainWindow.QuitToolStripMenuItem.ForeColor = colorMap("Foreground")
' Applying About Box colors
AboutBox.BackColor = colorMap("AboutBackground")
AboutBox.TabPageAbout.BackColor = colorMap("TabPageBackground")
AboutBox.TabPageAuthor.BackColor = colorMap("TabPageBackground")
AboutBox.ForeColor = colorMap("AboutForeground")
AboutBox.LabelProgramName.ForeColor = colorMap("AboutForeground")
AboutBox.LabelDescription.ForeColor = colorMap("AboutForeground")
AboutBox.LabelCopyright.ForeColor = colorMap("AboutForeground")
AboutBox.LabelVersion.ForeColor = colorMap("AboutForeground")
AboutBox.LabelAuthor.ForeColor = colorMap("AboutForeground")
AboutBox.ButtonClose.ForeColor = colorMap("ButtonForeground")
' Updating Dark Mode Text
If isDarkMode Then
MainWindow.DarkModeToolStripMenuItem.Text = "Dark Mode: Disable"
Else
MainWindow.DarkModeToolStripMenuItem.Text = "Dark Mode: Enable"
End If
End Sub
''' <summary>
''' Toggles specific settings on or off based on the provided parameters.
''' </summary>
''' <param name="enabled">A Boolean indicating if the setting option should be enabled or not.</param>
''' <param name="saveTo">A String specifying the type of setting to toggle ('json', 'csv', or 'darkmode').</param>
Public Sub ToggleSettings(enabled As Boolean, saveTo As String)
' Read the existing settings from the settings file
Dim json As String = File.ReadAllText(settingsFilePath)
Dim options As New JsonSerializerOptions With {.PropertyNameCaseInsensitive = True}
Dim settings As SettingsManager = JsonSerializer.Deserialize(Of SettingsManager)(json, options)
' Update the settings based on the specified saveTo parameter
If saveTo.ToLower(Globalization.CultureInfo.InvariantCulture) = "json" Then
settings.SaveToJson = enabled
ElseIf saveTo.ToLower(Globalization.CultureInfo.InvariantCulture) = "csv" Then
settings.SaveToCsv = enabled
ElseIf saveTo.ToLower(Globalization.CultureInfo.InvariantCulture) = "darkmode" Then
settings.DarkMode = enabled
Else
' Handle unexpected value of saveTo (if needed)
End If
' Save the updated settings back to the settings file
SaveSettings(settings)
' Apply the updated settings to the application
ApplySettings()
End Sub
End Class

View File

@@ -0,0 +1,173 @@
Imports System.IO
Imports Microsoft.Win32
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Utilities
''' <summary>
''' Determines if the Windows system theme is in dark mode.
''' </summary>
''' <returns>
''' True if the dark mode is enabled, otherwise false.
''' </returns>
Public Shared Function IsSystemDarkTheme() As Boolean
Dim registryKey As RegistryKey = Registry.CurrentUser.OpenSubKey(
"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
)
If registryKey IsNot Nothing Then
Dim appsUseLightTheme As Object = registryKey.GetValue("AppsUseLightTheme")
Return appsUseLightTheme IsNot Nothing AndAlso CType(appsUseLightTheme, Integer) = 0
Else
Return False
End If
End Function
''' <summary>
''' Shows the license notice in a messagebox.
''' </summary>
''' <returns>
''' Result of the Dialog (Yes/No).
''' </returns>
Public Shared Function LicenseAgreement()
Dim result As DialogResult = MessageBox.Show($"{My.Application.Info.Copyright}
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the ""Software""), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "MIT License", MessageBoxButtons.OK, MessageBoxIcon.Information)
Return result
End Function
''' <summary>
''' Checks for the existence of the 'logs' directory under the 'RPST' directory within the user's AppData\Roaming folder.
''' If the directory does not exist, it creates one.
''' </summary>
''' <remarks>
''' The directory path is 'C:\Users\<username>\AppData\Roaming\RPST\logs'.
''' If the 'RPST' or 'logs' directories do not exist, the function will create them.
''' If the directories already exist, the function will not perform any actions.
''' </remarks>
Public Shared Sub PathFinder()
Dim directoryPath As String = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RPST", "logs"
)
If Not Directory.Exists(directoryPath) Then
Directory.CreateDirectory(directoryPath)
End If
End Sub
''' <summary>
''' Collects and validates user inputs from StartForm and returns them as a Tuple.
''' </summary>
''' <returns>
''' Tuple containing:
''' Keyword (String) - Keyword entered by user in theMainWindow.
''' Subreddit (String) - Subreddit entered by user in theMainWindow.
''' Listing (String) - Listing chosen by user in the StartForm, defaults to 'top' if none is selected.
''' Limit (Integer) - Limit entered by user in the StartForm, defaults to 10 if the entered value is over 100.
''' Timeframe (String) - Timeframe chosen by user in the StartForm, defaults to 'all' if none is selected.
''' </returns>
''' <remarks>
''' If keyword or subreddit are empty, Displays a warning and returns nothing.
''' </remarks>
Public Shared Function CollectInputs() As (Keyword As String, Subreddit As String, Listing As String, Limit As Integer, Timeframe As String)?
Dim keyword As String = MainWindow.TextBoxKeyword.Text.Trim()
Dim subreddit As String = MainWindow.TextBoxSubreddit.Text.Trim()
' Convert the Listing and Subreddit to lowercase using InvariantCulture.
Dim listing As String = If(String.IsNullOrEmpty(MainWindow.ComboBoxListing.Text), "top", MainWindow.ComboBoxListing.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim())
Dim timeframe As String = If(String.IsNullOrEmpty(MainWindow.ComboBoxTimeframe.Text), "all", MainWindow.ComboBoxTimeframe.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim())
Dim limit As Integer = MainWindow.NumericUpDownLimit.Value
' Validate inputs.
If String.IsNullOrEmpty(keyword) AndAlso String.IsNullOrEmpty(subreddit) Then
MessageBox.Show("Keyword and Subreddit should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing
ElseIf String.IsNullOrEmpty(keyword) Then
MessageBox.Show("Keyword field should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing
ElseIf String.IsNullOrEmpty(subreddit) Then
MessageBox.Show("Subreddit field should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing
End If
Return (keyword, subreddit, listing, limit, timeframe)
End Function
''' <summary>
''' Saves the gives posts' data to a JSON file.
''' </summary>
''' <param name="Posts">The object containing posts to be saved.</param>
''' <remarks>
''' This function allows the user to select a location to save the posts.
''' If the user confirms the save location, the posts will be serialized
''' to JSON with an indented format and written to the chosen file.
''' A success message will be displayed to the user upon successful save.
''' </remarks>
Public Shared Sub SavePostsToJson(posts As Object)
Dim saveFileDialog As New SaveFileDialog With {
.Filter = "JSON files (*.json)|*.json",
.Title = "Save posts to JSON"
}
If saveFileDialog.ShowDialog() = DialogResult.OK Then
Dim fileName As String = saveFileDialog.FileName
Dim serializerSettings As New JsonSerializerSettings With {
.Formatting = Formatting.Indented
}
Dim json As String = JsonConvert.SerializeObject(posts, serializerSettings)
File.WriteAllText(fileName, json)
MessageBox.Show($"Posts saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
''' <summary>
''' Saves Reddit posts contained in a JArray to a CSV file.
''' </summary>
''' <param name="posts">A JArray containing the Reddit posts to be saved.</param>
''' <remarks>
''' This function displays a SaveFileDialog to allow the user to specify the file name and location.
''' It then iterates through the JArray to write each post's details (totalPosts, title, subreddit, author, score) into the selected CSV file.
''' </remarks>
Public Shared Sub SavePostsToCSV(posts As JArray)
Dim saveFileDialog As New SaveFileDialog With {
.Filter = "CSV files (*.csv)|*.csv",
.Title = "Save posts to CSV"
}
If saveFileDialog.ShowDialog() = DialogResult.OK Then
Dim fileName As String = saveFileDialog.FileName
Using csvWriter As New StreamWriter(fileName)
' Write the header.
csvWriter.WriteLine("Index,Author,ID,Subreddit,Visibility,Thumbnail,NSFW,Gilded,Upvotes,Upvote Ratio,Downvotes,Award,Top Award,Is cross-postable?,Score,Category,Text,Domain,Permalink,Created At,Approved At,Approved By")
Dim postCount As Integer = 0
For Each post In posts
postCount += 1
csvWriter.WriteLine($"{postCount},{post("data")("author")},{post("data")("id")},{post("data")("subreddit_name_prefixed")},{post("data")("subreddit_type")},{post("data")("thumbnail")},{post("data")("over_18")},{post("data")("gilded")},{post("data")("ups")},{post("data")("upvote_ratio")},{post("data")("downs")},{post("data")("total_awards_received")},{post("data")("top_awarded_type")},{post("data")("is_crosspostable")},{post("data")("score")},{post("data")("category")},{post("data")("selftext")},{post("data")("domain")},{post("data")("permalink")},{post("data")("created")},{post("data")("approved_at_utc")},{post("data")("approved_by")}")
Next
End Using
MessageBox.Show($"Posts saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
End Class