Merge pull request #16 from bellingcat/dev

Dev
This commit is contained in:
Richard Mwewa
2023-08-31 20:49:46 +02:00
committed by GitHub
25 changed files with 8854 additions and 2524 deletions

View File

@@ -1,5 +1,5 @@
# RPST (Reddit Post Scraping Tool) # RPST (Reddit Post Scraping Tool)
Given a subreddit name and a keyword, RPST will return all posts from a specified listing (default is 'top') that contain the provided keyword. Retrieve **Reddit** posts that contain the specified keyword from a specified subreddit.
[![Upload Python Package](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/python-publish.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/python-publish.yml) [![CodeQL](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/codeql.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/codeql.yml) ![.Net](https://img.shields.io/badge/.NET-5C2D91?style=flat&logo=.net&logoColor=white) ![Python](https://img.shields.io/badge/python-3670A0?style=flat&logo=python&logoColor=ffdd54) [![Upload Python Package](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/python-publish.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/python-publish.yml) [![CodeQL](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/codeql.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/codeql.yml) ![.Net](https://img.shields.io/badge/.NET-5C2D91?style=flat&logo=.net&logoColor=white) ![Python](https://img.shields.io/badge/python-3670A0?style=flat&logo=python&logoColor=ffdd54)

View File

@@ -25,13 +25,23 @@ Partial Class AboutBox
Dim resources As ComponentModel.ComponentResourceManager = New ComponentModel.ComponentResourceManager(GetType(AboutBox)) Dim resources As ComponentModel.ComponentResourceManager = New ComponentModel.ComponentResourceManager(GetType(AboutBox))
PictureBoxLogo = New PictureBox() PictureBoxLogo = New PictureBox()
LabelProgramName = New Label() LabelProgramName = New Label()
LabelProgramDescription = New Label() LabelDescription = New Label()
TabControl1 = New TabControl()
TabPageAbout = New TabPage()
LabelCopyright = New Label()
LinkLabelLicense = New LinkLabel()
LinkLabelReadtheWiki = New LinkLabel() LinkLabelReadtheWiki = New LinkLabel()
Panel1 = New Panel() TabPageAuthor = New TabPage()
LinkLabelVersion = New LinkLabel() LinkLabelEmail = New LinkLabel()
LicenseRichTextBox = New RichTextBox() LinkLabelBMC = New LinkLabel()
LinkLabelAboutMe = New LinkLabel()
LabelAuthor = New Label()
LabelVersion = New Label()
ButtonClose = New Button()
CType(PictureBoxLogo, ComponentModel.ISupportInitialize).BeginInit() CType(PictureBoxLogo, ComponentModel.ISupportInitialize).BeginInit()
Panel1.SuspendLayout() TabControl1.SuspendLayout()
TabPageAbout.SuspendLayout()
TabPageAuthor.SuspendLayout()
SuspendLayout() SuspendLayout()
' '
' PictureBoxLogo ' PictureBoxLogo
@@ -40,7 +50,7 @@ Partial Class AboutBox
PictureBoxLogo.Image = CType(resources.GetObject("PictureBoxLogo.Image"), Image) PictureBoxLogo.Image = CType(resources.GetObject("PictureBoxLogo.Image"), Image)
PictureBoxLogo.Location = New Point(12, 12) PictureBoxLogo.Location = New Point(12, 12)
PictureBoxLogo.Name = "PictureBoxLogo" PictureBoxLogo.Name = "PictureBoxLogo"
PictureBoxLogo.Size = New Size(88, 93) PictureBoxLogo.Size = New Size(62, 64)
PictureBoxLogo.SizeMode = PictureBoxSizeMode.StretchImage PictureBoxLogo.SizeMode = PictureBoxSizeMode.StretchImage
PictureBoxLogo.TabIndex = 0 PictureBoxLogo.TabIndex = 0
PictureBoxLogo.TabStop = False PictureBoxLogo.TabStop = False
@@ -48,76 +58,168 @@ Partial Class AboutBox
' LabelProgramName ' LabelProgramName
' '
LabelProgramName.AutoSize = True LabelProgramName.AutoSize = True
LabelProgramName.Font = New Font("Segoe Script", 9.75F, FontStyle.Bold, GraphicsUnit.Point) LabelProgramName.Font = New Font("Segoe UI Semibold", 9.75F, FontStyle.Bold, GraphicsUnit.Point)
LabelProgramName.ForeColor = SystemColors.ControlText LabelProgramName.ForeColor = SystemColors.ControlText
LabelProgramName.Location = New Point(3, 15) LabelProgramName.Location = New Point(80, 33)
LabelProgramName.Name = "LabelProgramName" LabelProgramName.Name = "LabelProgramName"
LabelProgramName.Size = New Size(48, 20) LabelProgramName.Size = New Size(44, 17)
LabelProgramName.TabIndex = 3 LabelProgramName.TabIndex = 3
LabelProgramName.Text = "Name" LabelProgramName.Text = "Name"
' '
' LabelProgramDescription ' LabelDescription
' '
LabelProgramDescription.AutoSize = True LabelDescription.AutoSize = True
LabelProgramDescription.Font = New Font("Segoe UI Semibold", 9F, FontStyle.Bold, GraphicsUnit.Point) LabelDescription.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point)
LabelProgramDescription.ForeColor = SystemColors.ControlText LabelDescription.ForeColor = SystemColors.ControlText
LabelProgramDescription.Location = New Point(3, 43) LabelDescription.Location = New Point(6, 7)
LabelProgramDescription.Name = "LabelProgramDescription" LabelDescription.Name = "LabelDescription"
LabelProgramDescription.Size = New Size(68, 15) LabelDescription.Size = New Size(67, 15)
LabelProgramDescription.TabIndex = 4 LabelDescription.TabIndex = 4
LabelProgramDescription.Text = "Description" LabelDescription.Text = "Description"
'
' TabControl1
'
TabControl1.Controls.Add(TabPageAbout)
TabControl1.Controls.Add(TabPageAuthor)
TabControl1.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point)
TabControl1.Location = New Point(12, 91)
TabControl1.Name = "TabControl1"
TabControl1.SelectedIndex = 0
TabControl1.Size = New Size(322, 152)
TabControl1.TabIndex = 8
'
' TabPageAbout
'
TabPageAbout.BackColor = Color.Transparent
TabPageAbout.Controls.Add(LabelCopyright)
TabPageAbout.Controls.Add(LinkLabelLicense)
TabPageAbout.Controls.Add(LabelDescription)
TabPageAbout.Controls.Add(LinkLabelReadtheWiki)
TabPageAbout.Font = New Font("Segoe UI Semibold", 9F, FontStyle.Bold, GraphicsUnit.Point)
TabPageAbout.Location = New Point(4, 24)
TabPageAbout.Name = "TabPageAbout"
TabPageAbout.Padding = New Padding(3)
TabPageAbout.Size = New Size(314, 124)
TabPageAbout.TabIndex = 0
TabPageAbout.Text = "About"
'
' LabelCopyright
'
LabelCopyright.AutoSize = True
LabelCopyright.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point)
LabelCopyright.Location = New Point(6, 97)
LabelCopyright.Name = "LabelCopyright"
LabelCopyright.Size = New Size(60, 15)
LabelCopyright.TabIndex = 7
LabelCopyright.Text = "Copyright"
'
' LinkLabelLicense
'
LinkLabelLicense.AutoSize = True
LinkLabelLicense.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point)
LinkLabelLicense.Location = New Point(6, 52)
LinkLabelLicense.Name = "LinkLabelLicense"
LinkLabelLicense.Size = New Size(84, 15)
LinkLabelLicense.TabIndex = 5
LinkLabelLicense.TabStop = True
LinkLabelLicense.Text = "🗒️ MIT License"
' '
' LinkLabelReadtheWiki ' LinkLabelReadtheWiki
' '
LinkLabelReadtheWiki.AutoSize = True LinkLabelReadtheWiki.AutoSize = True
LinkLabelReadtheWiki.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point) LinkLabelReadtheWiki.Font = New Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point)
LinkLabelReadtheWiki.Location = New Point(313, 43) LinkLabelReadtheWiki.Location = New Point(6, 74)
LinkLabelReadtheWiki.Name = "LinkLabelReadtheWiki" LinkLabelReadtheWiki.Name = "LinkLabelReadtheWiki"
LinkLabelReadtheWiki.Size = New Size(79, 15) LinkLabelReadtheWiki.Size = New Size(94, 15)
LinkLabelReadtheWiki.TabIndex = 6 LinkLabelReadtheWiki.TabIndex = 6
LinkLabelReadtheWiki.TabStop = True LinkLabelReadtheWiki.TabStop = True
LinkLabelReadtheWiki.Text = "Read the Wiki" LinkLabelReadtheWiki.Text = "📖 Read the Wiki"
' '
' Panel1 ' TabPageAuthor
' '
Panel1.BackColor = SystemColors.Control TabPageAuthor.BackColor = Color.Transparent
Panel1.Controls.Add(LinkLabelVersion) TabPageAuthor.Controls.Add(LinkLabelEmail)
Panel1.Controls.Add(LabelProgramDescription) TabPageAuthor.Controls.Add(LinkLabelBMC)
Panel1.Controls.Add(LabelProgramName) TabPageAuthor.Controls.Add(LinkLabelAboutMe)
Panel1.Controls.Add(LinkLabelReadtheWiki) TabPageAuthor.Controls.Add(LabelAuthor)
Panel1.Location = New Point(106, 12) TabPageAuthor.ForeColor = SystemColors.ControlText
Panel1.Name = "Panel1" TabPageAuthor.Location = New Point(4, 24)
Panel1.Size = New Size(409, 93) TabPageAuthor.Name = "TabPageAuthor"
Panel1.TabIndex = 7 TabPageAuthor.Padding = New Padding(3)
TabPageAuthor.Size = New Size(314, 124)
TabPageAuthor.TabIndex = 1
TabPageAuthor.Text = "Author"
' '
' LinkLabelVersion ' LinkLabelEmail
' '
LinkLabelVersion.AutoSize = True LinkLabelEmail.AutoSize = True
LinkLabelVersion.Location = New Point(347, 17) LinkLabelEmail.Location = New Point(6, 89)
LinkLabelVersion.Name = "LinkLabelVersion" LinkLabelEmail.Name = "LinkLabelEmail"
LinkLabelVersion.Size = New Size(45, 15) LinkLabelEmail.Size = New Size(51, 15)
LinkLabelVersion.TabIndex = 7 LinkLabelEmail.TabIndex = 3
LinkLabelVersion.TabStop = True LinkLabelEmail.TabStop = True
LinkLabelVersion.Text = "Version" LinkLabelEmail.Text = "📧 Email"
' '
' LicenseRichTextBox ' LinkLabelBMC
' '
LicenseRichTextBox.Font = New Font("Cambria", 9.75F, FontStyle.Regular, GraphicsUnit.Point) LinkLabelBMC.AutoSize = True
LicenseRichTextBox.Location = New Point(12, 113) LinkLabelBMC.Location = New Point(3, 66)
LicenseRichTextBox.Name = "LicenseRichTextBox" LinkLabelBMC.Name = "LinkLabelBMC"
LicenseRichTextBox.ReadOnly = True LinkLabelBMC.Size = New Size(111, 15)
LicenseRichTextBox.Size = New Size(503, 329) LinkLabelBMC.TabIndex = 2
LicenseRichTextBox.TabIndex = 1 LinkLabelBMC.TabStop = True
LicenseRichTextBox.Text = "License notice" LinkLabelBMC.Text = "🍵 Buy Me A Coffee"
'
' LinkLabelAboutMe
'
LinkLabelAboutMe.AutoSize = True
LinkLabelAboutMe.Location = New Point(6, 43)
LinkLabelAboutMe.Name = "LinkLabelAboutMe"
LinkLabelAboutMe.Size = New Size(75, 15)
LinkLabelAboutMe.TabIndex = 1
LinkLabelAboutMe.TabStop = True
LinkLabelAboutMe.Text = "🔗 About.me"
'
' LabelAuthor
'
LabelAuthor.AutoSize = True
LabelAuthor.Font = New Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point)
LabelAuthor.Location = New Point(6, 15)
LabelAuthor.Name = "LabelAuthor"
LabelAuthor.Size = New Size(96, 15)
LabelAuthor.TabIndex = 0
LabelAuthor.Text = "Richard Mwewa"
'
' LabelVersion
'
LabelVersion.AutoSize = True
LabelVersion.Font = New Font("Segoe UI", 8.25F, FontStyle.Regular, GraphicsUnit.Point)
LabelVersion.Location = New Point(80, 53)
LabelVersion.Name = "LabelVersion"
LabelVersion.Size = New Size(45, 13)
LabelVersion.TabIndex = 9
LabelVersion.Text = "Version"
'
' ButtonClose
'
ButtonClose.Location = New Point(275, 249)
ButtonClose.Name = "ButtonClose"
ButtonClose.Size = New Size(61, 23)
ButtonClose.TabIndex = 6
ButtonClose.Text = "&Close"
ButtonClose.UseVisualStyleBackColor = True
' '
' AboutBox ' AboutBox
' '
AutoScaleDimensions = New SizeF(7F, 15F) AutoScaleDimensions = New SizeF(7F, 15F)
AutoScaleMode = AutoScaleMode.Font AutoScaleMode = AutoScaleMode.Font
BackColor = Color.Gainsboro BackColor = Color.Gainsboro
ClientSize = New Size(526, 453) CancelButton = ButtonClose
Controls.Add(LicenseRichTextBox) ClientSize = New Size(346, 285)
Controls.Add(Panel1) Controls.Add(ButtonClose)
Controls.Add(LabelVersion)
Controls.Add(TabControl1)
Controls.Add(LabelProgramName)
Controls.Add(PictureBoxLogo) Controls.Add(PictureBoxLogo)
FormBorderStyle = FormBorderStyle.FixedSingle FormBorderStyle = FormBorderStyle.FixedSingle
Icon = CType(resources.GetObject("$this.Icon"), Icon) Icon = CType(resources.GetObject("$this.Icon"), Icon)
@@ -128,16 +230,30 @@ Partial Class AboutBox
StartPosition = FormStartPosition.CenterScreen StartPosition = FormStartPosition.CenterScreen
Text = "About" Text = "About"
CType(PictureBoxLogo, ComponentModel.ISupportInitialize).EndInit() CType(PictureBoxLogo, ComponentModel.ISupportInitialize).EndInit()
Panel1.ResumeLayout(False) TabControl1.ResumeLayout(False)
Panel1.PerformLayout() TabPageAbout.ResumeLayout(False)
TabPageAbout.PerformLayout()
TabPageAuthor.ResumeLayout(False)
TabPageAuthor.PerformLayout()
ResumeLayout(False) ResumeLayout(False)
PerformLayout()
End Sub End Sub
Friend WithEvents PictureBoxLogo As PictureBox Friend WithEvents PictureBoxLogo As PictureBox
Friend WithEvents LabelProgramName As Label Friend WithEvents LabelProgramName As Label
Friend WithEvents LabelProgramDescription As Label Friend WithEvents LabelDescription As Label
Friend WithEvents LinkLabelReadtheWiki As LinkLabel
Friend WithEvents Panel1 As Panel
Friend WithEvents LicenseRichTextBox As RichTextBox Friend WithEvents LicenseRichTextBox As RichTextBox
Friend WithEvents LinkLabelVersion As LinkLabel Friend WithEvents DataGridView1 As DataGridView
Friend WithEvents TabControl1 As TabControl
Friend WithEvents TabPageAbout As TabPage
Friend WithEvents TabPageAuthor As TabPage
Friend WithEvents LabelVersion As Label
Friend WithEvents LinkLabelLicense As LinkLabel
Friend WithEvents ButtonClose As Button
Friend WithEvents LabelCopyright As Label
Friend WithEvents LinkLabelReadtheWiki As LinkLabel
Friend WithEvents LabelAuthor As Label
Friend WithEvents LinkLabelAboutMe As LinkLabel
Friend WithEvents LinkLabelEmail As LinkLabel
Friend WithEvents LinkLabelBMC As LinkLabel
End Class End Class

File diff suppressed because it is too large Load Diff

View File

@@ -2,27 +2,6 @@
Public Class AboutBox Public Class AboutBox
ReadOnly settings As New SettingsManager() ReadOnly settings As New SettingsManager()
Public Property LicenseText As String = $"MIT License
{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."
''' <summary> ''' <summary>
''' Handles the Load event for the AboutBox form. ''' Handles the Load event for the AboutBox form.
@@ -30,15 +9,26 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param> ''' <param name="e">The event data.</param>
Private Sub AboutBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load Private Sub AboutBox_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Text = $"About {My.Application.Info.AssemblyName}"
settings.LoadSettings() settings.LoadSettings()
settings.ToggleSettings(settings.DarkMode, "darkmode") settings.ToggleSettings(settings.DarkMode, "darkmode")
LabelProgramName.Text = My.Application.Info.ProductName LabelProgramName.Text = My.Application.Info.ProductName
LabelProgramDescription.Text = "Given a subreddit name and a keyword, LabelDescription.Text = "Retrieve Reddit posts that contain the specified keyword
RPST returns all top posts (by default) from a specified subreddit. "
that contain the specified keyword." LabelVersion.Text = $"Version {My.Application.Info.Version}"
LinkLabelVersion.Text = $"v{My.Application.Info.Version}" LabelCopyright.Text = My.Application.Info.Copyright
LicenseRichTextBox.Text = LicenseText End Sub
''' <summary>
''' Handles the LinkClicked event for the LinkLabelLicense control.
''' Opens A MessageBox showing the License Notice.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param>
Private Sub LinkLabelLicense_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabelLicense.LinkClicked
Utilities.LicenseAgreement()
End Sub End Sub
''' <summary> ''' <summary>
@@ -51,7 +41,43 @@ that contain the specified keyword."
Shell("cmd /c start https://github.com/bellingcat/reddit-post-scraping-tool/wiki") Shell("cmd /c start https://github.com/bellingcat/reddit-post-scraping-tool/wiki")
End Sub End Sub
Private Sub LinkLabelVersion_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabelVersion.LinkClicked ''' <summary>
Shell($"cmd /c start https://github.com/bellingcat/reddit-post-scraping-tool/releases/tag/{My.Application.Info.Version}") ''' Handles the LinkClicked event for the LinkLabelAboutMe control.
''' Opens A MessageBox showing the License Notice.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param>
Private Sub LinkLabelAboutMe_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabelAboutMe.LinkClicked
Shell("cmd /c start https://about.me/rly0nheart")
End Sub
''' <summary>
''' Handles the LinkClicked event for the LinkLabelBMC control.
''' Opens A MessageBox showing the License Notice.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param>
Private Sub LinkLabelBMC_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabelBMC.LinkClicked
Shell("cmd /c start https://buymeacoffee.com/_rly0nheart")
End Sub
''' <summary>
''' Handles the LinkClicked event for the LinkLabelEmail control.
''' Opens A MessageBox showing the License Notice.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param>
Private Sub LinkLabelEmail_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabelEmail.LinkClicked
Shell("cmd /c start mailto:rly0nheart@duck.com")
End Sub
''' <summary>
''' Handles the Click event for ButtonOK event.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The event data.</param>
Private Sub ButtonOK_Click(sender As Object, e As EventArgs) Handles ButtonClose.Click
Me.Close()
End Sub End Sub
End Class End Class

View File

@@ -12,27 +12,32 @@ Public Class ApiHandler
Public Property UpdatesEndpoint As String = "https://api.github.com/repos/bellingcat/reddit-post-scraping-tool/releases/latest" Public Property UpdatesEndpoint As String = "https://api.github.com/repos/bellingcat/reddit-post-scraping-tool/releases/latest"
''' <summary> ''' <summary>
''' Scrape Reddit data. ''' Asyncrosnously scrape Reddit data.
''' </summary> ''' </summary>
''' <returns>Json object containing scraped data.</returns> ''' <returns>Json object containing scraped data.</returns>
Public Function ScrapeReddit(subreddit As String, listing As String, limit As Integer, timeframe As String) As JObject Public Async Function ScrapeRedditAsync(subreddit As String, listing As String, limit As Integer, timeframe As String) As Task(Of JObject)
Dim ApiEndpoint As String = $"https://reddit.com/r/{subreddit}/{listing}.json?limit={limit}&t={timeframe}" Dim ApiEndpoint As String = $"https://reddit.com/r/{subreddit}/{listing}.json?limit={limit}&t={timeframe}"
Return GetJObjectFromEndpoint(ApiEndpoint) Return Await GetJObjectFromEndpointAsync(endpoint:=ApiEndpoint)
End Function End Function
''' <summary> ''' <summary>
''' Gets remote version information from the repository release page. ''' Asyncrosnously gets remote version information from the repository release page.
''' </summary> ''' </summary>
''' <returns>Json object containing update data.</returns> ''' <returns>Json object containing update data.</returns>
Public Function CheckUpdates() As JObject Public Async Function CheckUpdatesAsync() As Task(Of JObject)
Return GetJObjectFromEndpoint(UpdatesEndpoint) Return Await GetJObjectFromEndpointAsync(endpoint:=UpdatesEndpoint)
End Function End Function
Private Function GetJObjectFromEndpoint(endpoint As String) As JObject ''' <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 GetJObjectFromEndpointAsync(endpoint As String) As Task(Of JObject)
Try Try
Using httpClient As New HttpClient() Using httpClient As New HttpClient()
httpClient.DefaultRequestHeaders.Add("User-Agent", headers) httpClient.DefaultRequestHeaders.Add("User-Agent", Headers)
Dim response As HttpResponseMessage = httpClient.GetAsync(endpoint).Result Dim response As HttpResponseMessage = Await httpClient.GetAsync(endpoint)
If response.IsSuccessStatusCode Then If response.IsSuccessStatusCode Then
Dim json As String = response.Content.ReadAsStringAsync().Result Dim json As String = response.Content.ReadAsStringAsync().Result
Dim data As JObject = JsonConvert.DeserializeObject(Of JObject)(json) Dim data As JObject = JsonConvert.DeserializeObject(Of JObject)(json)

View File

@@ -26,7 +26,7 @@ Partial Class FormMain
Dim resources As ComponentModel.ComponentResourceManager = New ComponentModel.ComponentResourceManager(GetType(FormMain)) Dim resources As ComponentModel.ComponentResourceManager = New ComponentModel.ComponentResourceManager(GetType(FormMain))
TextBoxKeyword = New TextBox() TextBoxKeyword = New TextBox()
TextBoxSubreddit = New TextBox() TextBoxSubreddit = New TextBox()
ButtonScrape = New Button() ButtonSearch = New Button()
ComboBoxTimeframe = New ComboBox() ComboBoxTimeframe = New ComboBox()
ComboBoxListing = New ComboBox() ComboBoxListing = New ComboBox()
LabelKeyword = New Label() LabelKeyword = New Label()
@@ -41,7 +41,6 @@ Partial Class FormMain
ToJSONToolStripMenuItem = New ToolStripMenuItem() ToJSONToolStripMenuItem = New ToolStripMenuItem()
ToCSVToolStripMenuItem = New ToolStripMenuItem() ToCSVToolStripMenuItem = New ToolStripMenuItem()
AboutToolStripMenuItem = New ToolStripMenuItem() AboutToolStripMenuItem = New ToolStripMenuItem()
DeveloperToolStripMenuItem = New ToolStripMenuItem()
CheckForUpdatesToolStripMenuItem = New ToolStripMenuItem() CheckForUpdatesToolStripMenuItem = New ToolStripMenuItem()
QuitToolStripMenuItem = New ToolStripMenuItem() QuitToolStripMenuItem = New ToolStripMenuItem()
NumericUpDownLimit = New NumericUpDown() NumericUpDownLimit = New NumericUpDown()
@@ -70,20 +69,19 @@ Partial Class FormMain
TextBoxSubreddit.TabIndex = 4 TextBoxSubreddit.TabIndex = 4
ToolTip.SetToolTip(TextBoxSubreddit, "[required] The subreddit to search in.") ToolTip.SetToolTip(TextBoxSubreddit, "[required] The subreddit to search in.")
' '
' ButtonScrape ' ButtonSearch
' '
ButtonScrape.Location = New Point(167, 174) ButtonSearch.Location = New Point(165, 165)
ButtonScrape.Name = "ButtonScrape" ButtonSearch.Name = "ButtonSearch"
ButtonScrape.Size = New Size(51, 28) ButtonSearch.Size = New Size(55, 28)
ButtonScrape.TabIndex = 6 ButtonSearch.TabIndex = 6
ButtonScrape.Text = "Scrape" ButtonSearch.Text = "Search"
ToolTip.SetToolTip(ButtonScrape, "Hitting ENTER will also start the scraping process.") ToolTip.SetToolTip(ButtonSearch, "Hitting ENTER will also start the scraping process.")
ButtonScrape.UseVisualStyleBackColor = True ButtonSearch.UseVisualStyleBackColor = True
' '
' ComboBoxTimeframe ' ComboBoxTimeframe
' '
ComboBoxTimeframe.AutoCompleteCustomSource.AddRange(New String() {"Hour", "Day", "Week", "Month", "Year"}) ComboBoxTimeframe.AutoCompleteCustomSource.AddRange(New String() {"Hour", "Day", "Week", "Month", "Year"})
ComboBoxTimeframe.AutoCompleteMode = AutoCompleteMode.Append
ComboBoxTimeframe.AutoCompleteSource = AutoCompleteSource.CustomSource ComboBoxTimeframe.AutoCompleteSource = AutoCompleteSource.CustomSource
ComboBoxTimeframe.FormattingEnabled = True ComboBoxTimeframe.FormattingEnabled = True
ComboBoxTimeframe.Items.AddRange(New Object() {"Hour", "Day", "Week", "Month", "Year"}) ComboBoxTimeframe.Items.AddRange(New Object() {"Hour", "Day", "Week", "Month", "Year"})
@@ -97,8 +95,8 @@ Partial Class FormMain
' ComboBoxListing ' ComboBoxListing
' '
ComboBoxListing.AutoCompleteCustomSource.AddRange(New String() {"Controversial", "Hot", "Best", "New", "Rising"}) ComboBoxListing.AutoCompleteCustomSource.AddRange(New String() {"Controversial", "Hot", "Best", "New", "Rising"})
ComboBoxListing.AutoCompleteMode = AutoCompleteMode.Append
ComboBoxListing.AutoCompleteSource = AutoCompleteSource.CustomSource ComboBoxListing.AutoCompleteSource = AutoCompleteSource.CustomSource
ComboBoxListing.BackColor = SystemColors.Window
ComboBoxListing.FormattingEnabled = True ComboBoxListing.FormattingEnabled = True
ComboBoxListing.Items.AddRange(New Object() {"Controversial", "Hot", "Best", "New", "Rising"}) ComboBoxListing.Items.AddRange(New Object() {"Controversial", "Hot", "Best", "New", "Rising"})
ComboBoxListing.Location = New Point(118, 107) ComboBoxListing.Location = New Point(118, 107)
@@ -165,16 +163,16 @@ Partial Class FormMain
' '
' ContextMenuStripRightClick ' ContextMenuStripRightClick
' '
ContextMenuStripRightClick.Items.AddRange(New ToolStripItem() {AboutToolStripMenuItem, DeveloperToolStripMenuItem, CheckForUpdatesToolStripMenuItem, SettingsToolStripMenuItem, QuitToolStripMenuItem}) ContextMenuStripRightClick.Items.AddRange(New ToolStripItem() {SettingsToolStripMenuItem, AboutToolStripMenuItem, CheckForUpdatesToolStripMenuItem, QuitToolStripMenuItem})
ContextMenuStripRightClick.Name = "ContextMenuStrip1" ContextMenuStripRightClick.Name = "ContextMenuStrip1"
ContextMenuStripRightClick.Size = New Size(172, 114) ContextMenuStripRightClick.Size = New Size(181, 114)
' '
' SettingsToolStripMenuItem ' SettingsToolStripMenuItem
' '
SettingsToolStripMenuItem.DropDownItems.AddRange(New ToolStripItem() {DarkModeToolStripMenuItem, SavePostsToolStripMenuItem}) SettingsToolStripMenuItem.DropDownItems.AddRange(New ToolStripItem() {DarkModeToolStripMenuItem, SavePostsToolStripMenuItem})
SettingsToolStripMenuItem.Image = CType(resources.GetObject("SettingsToolStripMenuItem.Image"), Image) SettingsToolStripMenuItem.Image = CType(resources.GetObject("SettingsToolStripMenuItem.Image"), Image)
SettingsToolStripMenuItem.Name = "SettingsToolStripMenuItem" SettingsToolStripMenuItem.Name = "SettingsToolStripMenuItem"
SettingsToolStripMenuItem.Size = New Size(171, 22) SettingsToolStripMenuItem.Size = New Size(180, 22)
SettingsToolStripMenuItem.Text = "Settings" SettingsToolStripMenuItem.Text = "Settings"
' '
' DarkModeToolStripMenuItem ' DarkModeToolStripMenuItem
@@ -182,7 +180,7 @@ Partial Class FormMain
DarkModeToolStripMenuItem.CheckOnClick = True DarkModeToolStripMenuItem.CheckOnClick = True
DarkModeToolStripMenuItem.Image = CType(resources.GetObject("DarkModeToolStripMenuItem.Image"), Image) DarkModeToolStripMenuItem.Image = CType(resources.GetObject("DarkModeToolStripMenuItem.Image"), Image)
DarkModeToolStripMenuItem.Name = "DarkModeToolStripMenuItem" DarkModeToolStripMenuItem.Name = "DarkModeToolStripMenuItem"
DarkModeToolStripMenuItem.Size = New Size(180, 22) DarkModeToolStripMenuItem.Size = New Size(132, 22)
DarkModeToolStripMenuItem.Text = "Dark Mode" DarkModeToolStripMenuItem.Text = "Dark Mode"
' '
' SavePostsToolStripMenuItem ' SavePostsToolStripMenuItem
@@ -191,7 +189,7 @@ Partial Class FormMain
SavePostsToolStripMenuItem.DropDownItems.AddRange(New ToolStripItem() {ToJSONToolStripMenuItem, ToCSVToolStripMenuItem}) SavePostsToolStripMenuItem.DropDownItems.AddRange(New ToolStripItem() {ToJSONToolStripMenuItem, ToCSVToolStripMenuItem})
SavePostsToolStripMenuItem.Image = CType(resources.GetObject("SavePostsToolStripMenuItem.Image"), Image) SavePostsToolStripMenuItem.Image = CType(resources.GetObject("SavePostsToolStripMenuItem.Image"), Image)
SavePostsToolStripMenuItem.Name = "SavePostsToolStripMenuItem" SavePostsToolStripMenuItem.Name = "SavePostsToolStripMenuItem"
SavePostsToolStripMenuItem.Size = New Size(180, 22) SavePostsToolStripMenuItem.Size = New Size(132, 22)
SavePostsToolStripMenuItem.Text = "Save posts" SavePostsToolStripMenuItem.Text = "Save posts"
' '
' ToJSONToolStripMenuItem ' ToJSONToolStripMenuItem
@@ -200,7 +198,7 @@ Partial Class FormMain
ToJSONToolStripMenuItem.CheckOnClick = True ToJSONToolStripMenuItem.CheckOnClick = True
ToJSONToolStripMenuItem.Image = CType(resources.GetObject("ToJSONToolStripMenuItem.Image"), Image) ToJSONToolStripMenuItem.Image = CType(resources.GetObject("ToJSONToolStripMenuItem.Image"), Image)
ToJSONToolStripMenuItem.Name = "ToJSONToolStripMenuItem" ToJSONToolStripMenuItem.Name = "ToJSONToolStripMenuItem"
ToJSONToolStripMenuItem.Size = New Size(180, 22) ToJSONToolStripMenuItem.Size = New Size(116, 22)
ToJSONToolStripMenuItem.Text = "to JSON" ToJSONToolStripMenuItem.Text = "to JSON"
' '
' ToCSVToolStripMenuItem ' ToCSVToolStripMenuItem
@@ -209,7 +207,7 @@ Partial Class FormMain
ToCSVToolStripMenuItem.CheckOnClick = True ToCSVToolStripMenuItem.CheckOnClick = True
ToCSVToolStripMenuItem.Image = CType(resources.GetObject("ToCSVToolStripMenuItem.Image"), Image) ToCSVToolStripMenuItem.Image = CType(resources.GetObject("ToCSVToolStripMenuItem.Image"), Image)
ToCSVToolStripMenuItem.Name = "ToCSVToolStripMenuItem" ToCSVToolStripMenuItem.Name = "ToCSVToolStripMenuItem"
ToCSVToolStripMenuItem.Size = New Size(180, 22) ToCSVToolStripMenuItem.Size = New Size(116, 22)
ToCSVToolStripMenuItem.Text = "to CSV" ToCSVToolStripMenuItem.Text = "to CSV"
' '
' AboutToolStripMenuItem ' AboutToolStripMenuItem
@@ -217,23 +215,15 @@ Partial Class FormMain
AboutToolStripMenuItem.AutoToolTip = True AboutToolStripMenuItem.AutoToolTip = True
AboutToolStripMenuItem.Image = CType(resources.GetObject("AboutToolStripMenuItem.Image"), Image) AboutToolStripMenuItem.Image = CType(resources.GetObject("AboutToolStripMenuItem.Image"), Image)
AboutToolStripMenuItem.Name = "AboutToolStripMenuItem" AboutToolStripMenuItem.Name = "AboutToolStripMenuItem"
AboutToolStripMenuItem.Size = New Size(171, 22) AboutToolStripMenuItem.Size = New Size(180, 22)
AboutToolStripMenuItem.Text = "About" AboutToolStripMenuItem.Text = "About RPST"
'
' DeveloperToolStripMenuItem
'
DeveloperToolStripMenuItem.AutoToolTip = True
DeveloperToolStripMenuItem.Image = CType(resources.GetObject("DeveloperToolStripMenuItem.Image"), Image)
DeveloperToolStripMenuItem.Name = "DeveloperToolStripMenuItem"
DeveloperToolStripMenuItem.Size = New Size(171, 22)
DeveloperToolStripMenuItem.Text = "Developer"
' '
' CheckForUpdatesToolStripMenuItem ' CheckForUpdatesToolStripMenuItem
' '
CheckForUpdatesToolStripMenuItem.AutoToolTip = True CheckForUpdatesToolStripMenuItem.AutoToolTip = True
CheckForUpdatesToolStripMenuItem.Image = CType(resources.GetObject("CheckForUpdatesToolStripMenuItem.Image"), Image) CheckForUpdatesToolStripMenuItem.Image = CType(resources.GetObject("CheckForUpdatesToolStripMenuItem.Image"), Image)
CheckForUpdatesToolStripMenuItem.Name = "CheckForUpdatesToolStripMenuItem" CheckForUpdatesToolStripMenuItem.Name = "CheckForUpdatesToolStripMenuItem"
CheckForUpdatesToolStripMenuItem.Size = New Size(171, 22) CheckForUpdatesToolStripMenuItem.Size = New Size(180, 22)
CheckForUpdatesToolStripMenuItem.Text = "Check for Updates" CheckForUpdatesToolStripMenuItem.Text = "Check for Updates"
' '
' QuitToolStripMenuItem ' QuitToolStripMenuItem
@@ -242,7 +232,7 @@ Partial Class FormMain
QuitToolStripMenuItem.Font = New Font("Segoe UI Semibold", 9F, FontStyle.Bold, GraphicsUnit.Point) QuitToolStripMenuItem.Font = New Font("Segoe UI Semibold", 9F, FontStyle.Bold, GraphicsUnit.Point)
QuitToolStripMenuItem.Image = CType(resources.GetObject("QuitToolStripMenuItem.Image"), Image) QuitToolStripMenuItem.Image = CType(resources.GetObject("QuitToolStripMenuItem.Image"), Image)
QuitToolStripMenuItem.Name = "QuitToolStripMenuItem" QuitToolStripMenuItem.Name = "QuitToolStripMenuItem"
QuitToolStripMenuItem.Size = New Size(171, 22) QuitToolStripMenuItem.Size = New Size(180, 22)
QuitToolStripMenuItem.Text = "Quit" QuitToolStripMenuItem.Text = "Quit"
' '
' NumericUpDownLimit ' NumericUpDownLimit
@@ -270,7 +260,7 @@ Partial Class FormMain
AutoScaleDimensions = New SizeF(7F, 15F) AutoScaleDimensions = New SizeF(7F, 15F)
AutoScaleMode = AutoScaleMode.Font AutoScaleMode = AutoScaleMode.Font
BackColor = SystemColors.Control BackColor = SystemColors.Control
ClientSize = New Size(239, 221) ClientSize = New Size(239, 211)
ContextMenuStrip = ContextMenuStripRightClick ContextMenuStrip = ContextMenuStripRightClick
Controls.Add(ComboBoxTimeframe) Controls.Add(ComboBoxTimeframe)
Controls.Add(TextBoxKeyword) Controls.Add(TextBoxKeyword)
@@ -279,7 +269,7 @@ Partial Class FormMain
Controls.Add(ComboBoxListing) Controls.Add(ComboBoxListing)
Controls.Add(NumericUpDownLimit) Controls.Add(NumericUpDownLimit)
Controls.Add(LabelListing) Controls.Add(LabelListing)
Controls.Add(ButtonScrape) Controls.Add(ButtonSearch)
Controls.Add(LabelLimit) Controls.Add(LabelLimit)
Controls.Add(LabelSubreddit) Controls.Add(LabelSubreddit)
Controls.Add(TextBoxSubreddit) Controls.Add(TextBoxSubreddit)
@@ -297,7 +287,7 @@ Partial Class FormMain
Friend WithEvents TextBoxKeyword As TextBox Friend WithEvents TextBoxKeyword As TextBox
Friend WithEvents TextBoxSubreddit As TextBox Friend WithEvents TextBoxSubreddit As TextBox
Friend WithEvents ButtonScrape As Button Friend WithEvents ButtonSearch As Button
Friend WithEvents ComboBoxTimeframe As ComboBox Friend WithEvents ComboBoxTimeframe As ComboBox
Friend WithEvents ComboBoxListing As ComboBox Friend WithEvents ComboBoxListing As ComboBox
Friend WithEvents LabelKeyword As Label Friend WithEvents LabelKeyword As Label
@@ -311,7 +301,6 @@ Partial Class FormMain
Friend WithEvents ToCSVToolStripMenuItem As ToolStripMenuItem Friend WithEvents ToCSVToolStripMenuItem As ToolStripMenuItem
Friend WithEvents NumericUpDownLimit As NumericUpDown Friend WithEvents NumericUpDownLimit As NumericUpDown
Friend WithEvents AboutToolStripMenuItem As ToolStripMenuItem Friend WithEvents AboutToolStripMenuItem As ToolStripMenuItem
Friend WithEvents DeveloperToolStripMenuItem As ToolStripMenuItem
Friend WithEvents CheckForUpdatesToolStripMenuItem As ToolStripMenuItem Friend WithEvents CheckForUpdatesToolStripMenuItem As ToolStripMenuItem
Friend WithEvents QuitToolStripMenuItem As ToolStripMenuItem Friend WithEvents QuitToolStripMenuItem As ToolStripMenuItem
Friend WithEvents ToolTip As ToolTip Friend WithEvents ToolTip As ToolTip

File diff suppressed because it is too large Load Diff

View File

@@ -11,11 +11,10 @@ Public Class FormMain
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param> ''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
settings.LoadSettings() settings.LoadSettings()
settings.ToggleSettings(settings.DarkMode, "darkmode") settings.ToggleSettings(enabled:=settings.DarkMode, saveTo:="darkmode")
settings.ToggleSettings(settings.SaveToJson, "json") settings.ToggleSettings(enabled:=settings.SaveToJson, saveTo:="json")
settings.ToggleSettings(settings.SaveToCsv, "csv") settings.ToggleSettings(enabled:=settings.SaveToCsv, saveTo:="csv")
Utilities.PathFinder() Utilities.PathFinder()
Utilities.LogFirstTimeLaunch() Utilities.LogFirstTimeLaunch()
@@ -34,32 +33,21 @@ Public Class FormMain
End Sub End Sub
''' <summary>
''' Event handler for the 'Developer' menu item click.
''' It shows the 'Developer' dialog box.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub ToolStripMenuItemDeveloper_Click(sender As Object, e As EventArgs) Handles DeveloperToolStripMenuItem.Click
DeveloperBox.ShowDialog()
End Sub
''' <summary> ''' <summary>
''' Event handler for the 'Check Updates' menu item click. ''' Event handler for the 'Check Updates' menu item click.
''' It checks for application updates and provides update information if a newer version is available. ''' It checks for application updates and provides update information if a newer version is available.
''' </summary> ''' </summary>
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param> ''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub ToolStripMenuItemCheckUpdates_Click(sender As Object, e As EventArgs) Handles CheckForUpdatesToolStripMenuItem.Click Private Async Sub ToolStripMenuItemCheckUpdates_Click(sender As Object, e As EventArgs) Handles CheckForUpdatesToolStripMenuItem.Click
Dim data As JObject = ApiHandler.CheckUpdates() Dim data As JObject = Await ApiHandler.CheckUpdatesAsync()
If data("tag_name").ToString = My.Application.Info.Version.ToString Then If data("tag_name").ToString = My.Application.Info.Version.ToString Then
MessageBox.Show($"You're running the latest version v{My.Application.Info.Version} of {Me.Text}. Check again soon! :)", $"{Me.Text} v{My.Application.Info.Version}", MessageBoxButtons.OK, MessageBoxIcon.Information) MessageBox.Show($"You're running the latest version v{My.Application.Info.Version} of {Me.Text}. Check again soon! :)", $"{Me.Text} v{My.Application.Info.Version}", MessageBoxButtons.OK, MessageBoxIcon.Information)
Else Else
Dim confirm As DialogResult = MessageBox.Show($"A new version v{data("tag_name")} of {Me.Text} is available, would you like to get it? Dim confirm As DialogResult = MessageBox.Show($"A new version v{data("tag_name")} of {Me.Text} is available, would you like to get it?
{data("body")} {data("body")}
", $"{Me.Text} v{data("tag_name")}".ToString, MessageBoxButtons.YesNo, MessageBoxIcon.Question) ", $"{Me.Text} v{data("tag_name")}", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If confirm = DialogResult.Yes Then If confirm = DialogResult.Yes Then
Shell($"cmd /c start {data("html_url")}") Shell($"cmd /c start {data("html_url")}")
End If End If
@@ -88,7 +76,7 @@ Public Class FormMain
''' </summary> ''' </summary>
''' <param name="sender">The sender of the event.</param> ''' <param name="sender">The sender of the event.</param>
''' <param name="e">The EventArgs instance containing the event data.</param> ''' <param name="e">The EventArgs instance containing the event data.</param>
Private Sub ButtonScrape_Click(sender As Object, e As EventArgs) Handles ButtonScrape.Click Private Sub ButtonScrape_Click(sender As Object, e As EventArgs) Handles ButtonSearch.Click
settings.LoadSettings() settings.LoadSettings()
PostsProcessor.ProcessRedditPosts(settings:=settings) PostsProcessor.ProcessRedditPosts(settings:=settings)
End Sub End Sub
@@ -190,7 +178,7 @@ Public Class FormMain
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param> ''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub ToolStripMenuItemDarkMode_CheckedChanged(sender As Object, e As EventArgs) Handles DarkModeToolStripMenuItem.CheckedChanged Private Sub ToolStripMenuItemDarkMode_CheckedChanged(sender As Object, e As EventArgs) Handles DarkModeToolStripMenuItem.CheckedChanged
settings.ToggleSettings(DarkModeToolStripMenuItem.Checked, "darkmode") settings.ToggleSettings(enabled:=DarkModeToolStripMenuItem.Checked, saveTo:="darkmode")
End Sub End Sub
''' <summary> ''' <summary>
@@ -200,7 +188,7 @@ Public Class FormMain
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param> ''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub ToCSVToolStripMenuItem_CheckedChanged(sender As Object, e As EventArgs) Handles ToCSVToolStripMenuItem.CheckedChanged Private Sub ToCSVToolStripMenuItem_CheckedChanged(sender As Object, e As EventArgs) Handles ToCSVToolStripMenuItem.CheckedChanged
settings.ToggleSettings(ToCSVToolStripMenuItem.Checked, "csv") settings.ToggleSettings(enabled:=ToCSVToolStripMenuItem.Checked, saveTo:="csv")
End Sub End Sub
''' <summary> ''' <summary>
@@ -210,6 +198,6 @@ Public Class FormMain
''' <param name="sender">The source of the event.</param> ''' <param name="sender">The source of the event.</param>
''' <param name="e">An EventArgs that contains the event data.</param> ''' <param name="e">An EventArgs that contains the event data.</param>
Private Sub ToJSONToolStripMenuItem_CheckedChanged(sender As Object, e As EventArgs) Handles ToJSONToolStripMenuItem.CheckedChanged Private Sub ToJSONToolStripMenuItem_CheckedChanged(sender As Object, e As EventArgs) Handles ToJSONToolStripMenuItem.CheckedChanged
settings.ToggleSettings(ToJSONToolStripMenuItem.Checked, "json") settings.ToggleSettings(enabled:=ToJSONToolStripMenuItem.Checked, saveTo:="json")
End Sub End Sub
End Class End Class

View File

@@ -48,7 +48,6 @@ Partial Class FormPosts
Controls.Add(DataGridViewPosts) Controls.Add(DataGridViewPosts)
Icon = CType(resources.GetObject("$this.Icon"), Icon) Icon = CType(resources.GetObject("$this.Icon"), Icon)
Name = "FormPosts" Name = "FormPosts"
ShowInTaskbar = False
StartPosition = FormStartPosition.CenterScreen StartPosition = FormStartPosition.CenterScreen
Text = "Posts" Text = "Posts"
CType(DataGridViewPosts, ComponentModel.ISupportInitialize).EndInit() CType(DataGridViewPosts, ComponentModel.ISupportInitialize).EndInit()

View File

@@ -18,7 +18,7 @@
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing"">Blue</data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value> <value>[base64 mime encoded serialized .NET Framework object]</value>
</data> </data>
@@ -120,274 +120,449 @@
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
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
8DXKzC//H2KEQtrPXsHrAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
</root> </root>

View File

@@ -35,5 +35,16 @@ Namespace My
Protected Overrides Sub OnCreateMainForm() Protected Overrides Sub OnCreateMainForm()
Me.MainForm = Global.RPST.FormMain Me.MainForm = Global.RPST.FormMain
End Sub End Sub
<Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
Protected Overrides Sub OnCreateSplashScreen()
Me.SplashScreen = Global.RPST.SplashScreen
End Sub
<Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
Protected Overrides Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean
Me.MinimumSplashScreenDisplayTime = 2000
Return MyBase.OnInitialize(commandLineArgs)
End Function
End Class End Class
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-16"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MySubMain>true</MySubMain> <MySubMain>true</MySubMain>
<MainForm>Form1</MainForm> <MainForm>Form1</MainForm>
@@ -7,4 +7,6 @@
<EnableVisualStyles>true</EnableVisualStyles> <EnableVisualStyles>true</EnableVisualStyles>
<AuthenticationMode>0</AuthenticationMode> <AuthenticationMode>0</AuthenticationMode>
<SaveMySettingsOnExit>true</SaveMySettingsOnExit> <SaveMySettingsOnExit>true</SaveMySettingsOnExit>
<SplashScreen>SplashScreen</SplashScreen>
<MinimumSplashScreenDisplayTime>2000</MinimumSplashScreenDisplayTime>
</MyApplicationData> </MyApplicationData>

View File

@@ -4,15 +4,15 @@ Public Class PostsProcessor
Private ReadOnly ApiHandler As New ApiHandler Private ReadOnly ApiHandler As New ApiHandler
''' <summary> ''' <summary>
''' Fetches Reddit posts based on the given parameters and returns them as a JObject. ''' Asyncronously fetches Reddit posts based on the given parameters and returns them as a JObject.
''' </summary> ''' </summary>
''' <param name="subreddit">The subreddit to fetch posts from.</param> ''' <param name="subreddit">The subreddit to fetch posts from.</param>
''' <param name="listing">The type of listing (e.g., "new", "top", etc.).</param> ''' <param name="listing">The type of listing (e.g., "new", "top", etc.).</param>
''' <param name="limit">The maximum number of posts to fetch.</param> ''' <param name="limit">The maximum number of posts to fetch.</param>
''' <param name="timeframe">The timeframe to consider for the posts (e.g., "day", "week", "month", "year", "all").</param> ''' <param name="timeframe">The timeframe to consider for the posts (e.g., "day", "week", "month", "year", "all").</param>
''' <returns>A JObject containing the fetched Reddit posts.</returns> ''' <returns>A JObject containing the fetched Reddit posts.</returns>
Public Function FetchPosts(subreddit As String, listing As String, limit As Integer, timeframe As String) As JObject Public Async Function FetchPostsAsync(subreddit As String, listing As String, limit As Integer, timeframe As String) As Task(Of JObject)
Dim posts As JObject = ApiHandler.ScrapeReddit(subreddit, listing, limit, timeframe) Dim posts As JObject = Await ApiHandler.ScrapeRedditAsync(subreddit, listing, limit, timeframe)
Return posts Return posts
End Function End Function
@@ -35,7 +35,7 @@ Public Class PostsProcessor
''' This function initializes the DataGridView, iterates over each post, adds the posts containing the keyword to the DataGridView and updates the UI. ''' 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. ''' It also shows a message if the keyword was not found in any of the posts or if the inputs are empty.
''' </remarks> ''' </remarks>
Public Shared Sub ProcessRedditPosts(settings) Public Shared Async Sub ProcessRedditPosts(settings)
' Collect inputs from the user. ' Collect inputs from the user.
Dim inputs = Utilities.CollectInputs() Dim inputs = Utilities.CollectInputs()
@@ -45,7 +45,7 @@ Public Class PostsProcessor
' Fetch Reddit posts based on the inputs. ' Fetch Reddit posts based on the inputs.
Dim processor As New PostsProcessor() Dim processor As New PostsProcessor()
Dim posts As JObject = processor.FetchPosts(inputs.Value.Subreddit, inputs.Value.Listing, inputs.Value.Limit, inputs.Value.Timeframe) Dim posts As JObject = Await processor.FetchPostsAsync(subreddit:=inputs.Value.Subreddit, listing:=inputs.Value.Listing, limit:=inputs.Value.Limit, timeframe:=inputs.Value.Timeframe)
Dim totalPosts As Integer = 0 Dim totalPosts As Integer = 0
Dim keywordFound As Boolean = False Dim keywordFound As Boolean = False
Dim foundPosts As Integer = 0 Dim foundPosts As Integer = 0
@@ -74,12 +74,12 @@ Public Class PostsProcessor
If settings.SaveToJson Then If settings.SaveToJson Then
' Save posts to a JSON file if SaveToJson is True. ' Save posts to a JSON file if SaveToJson is True.
Utilities.SavePostsToJson(foundPostsList) Utilities.SavePostsToJson(posts:=foundPostsList)
End If End If
If settings.SaveToCsv Then If settings.SaveToCsv Then
' Save posts to a CSV file if SaveToCsv is True. ' Save posts to a CSV file if SaveToCsv is True.
Utilities.SavePostsToCSV(foundPostsList) Utilities.SavePostsToCSV(posts:=foundPostsList)
End If End If
Else Else
End If End If

View File

@@ -4,39 +4,41 @@ Given a subreddit name and a keyword, RPST will return all posts from a specifie
[![Upload Python Package](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/python-publish.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/python-publish.yml) [![CodeQL](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/codeql.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/codeql.yml) ![.Net](https://img.shields.io/badge/.NET-5C2D91?style=flat&logo=.net&logoColor=white) ![Python](https://img.shields.io/badge/python-3670A0?style=flat&logo=python&logoColor=ffdd54) [![Upload Python Package](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/python-publish.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/python-publish.yml) [![CodeQL](https://github.com/bellingcat/reddit-post-scraping-tool/actions/workflows/codeql.yml/badge.svg)](https://github.com/rly0nheart/reddit-post-scraping-tool/actions/workflows/codeql.yml) ![.Net](https://img.shields.io/badge/.NET-5C2D91?style=flat&logo=.net&logoColor=white) ![Python](https://img.shields.io/badge/python-3670A0?style=flat&logo=python&logoColor=ffdd54)
# ✅ Features # ✅ Features
## *GUI* ## GUI
- [x] Dark mode (*Right-click*). - [x] Dark mode (*Right-click*)
- [x] Saves results to a JSON file (*Right-click*). - [x] Saves results to a JSON file (*Right-click*)
- [x] Logs errors to a file. - [x] Logs errors to a file
- [x] In-App feature to check for Updates.
## *CLI* ## CLI
- [x] Saves results to JSON (*specifiy* `--json`). - [x] Saves results to JSON (*specifiy* `--json`)
- [x] Saves results to CSV (*specify* `--csv`). - [x] Saves results to CSV (*specify* `--csv`)
- [x] Automatically checks for new updates, and notifies user if updates were found. - [x] Automatically checks for new updates, and notifies user if updates were found.
# 📃 TODO # 📃 TODO
## *GUI* ## GUI
- [ ] Make it installable with a setup.exe/setup.msi file. - [ ] Make it installable with a setup.exe/setup.msi file.
- [x] Add manual dark mode option, that will be persistent in all sessions. - [x] Add manual dark mode option, that will be persistent in all sessions
- [x] Make settings persistent in all sessions. - [ ] Make it save results to a CSV file
- [x] Make it save results to a CSV file.
# Images & Screenshots
## GUI
* ![2023-08-09_04-05](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/d8917a35-3eac-44ce-aa96-1f9685095254)
* ![2023-08-09_04-05_1](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/d2fe7269-91d4-49ad-87fb-44282c5637a7)
## CLI
* ![2023-08-25_15-39](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/4bca09b3-271f-452d-81a7-39c9986539f2)
* ![2023-08-25_15-30](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/2b39bdfa-87d0-4038-90cd-14e7d3b6a84b)
* ![2023-08-25_15-35](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/47ba23ad-8d32-49c5-8c16-34a903fbc581)
# 🖥️ Tested environments
## *GUI*
- [x] Microsoft Windows 11
## *CLI*
- [x] Android Termux
- [x] Microsoft Windows 11
- [x] Ubuntu 22.04 - latest versions
# 📖 Wiki # 📖 Wiki
[Refer to the Wiki](https://github.com/bellingcat/reddit-post-scraping-tool/wiki) for installation instructions, in addition to all other documentation. [Refer to the Wiki](https://github.com/bellingcat/reddit-post-scraping-tool/wiki) for installation instructions, in addition to all other documentation.
# 🖼️ Screenshots # 😁 Donations
You can view a collection of screenshots for both the *CLI* and *GUI* [here](https://github.com/bellingcat/reddit-post-scraping-tool/tree/master/images) If you like `RPST` and would like to show support, you can Buy A Coffee for the developer using the button below
***
<a href="https://www.buymeacoffee.com/_rly0nheart"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=_rly0nheart&button_colour=40DCA5&font_colour=ffffff&font_family=Comic&outline_colour=000000&coffee_colour=FFDD00" /></a>
![me](https://github.com/bellingcat/reddit-post-scraping-tool/assets/74001397/21e0bb33-7a84-45d6-92ba-00e40891ba31) <a href="https://www.buymeacoffee.com/_rly0nheart" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
Your support will be much appreciated😊

View File

@@ -8,16 +8,16 @@
<MyType>WindowsForms</MyType> <MyType>WindowsForms</MyType>
<ApplicationIcon>icon.ico</ApplicationIcon> <ApplicationIcon>icon.ico</ApplicationIcon>
<Company>Bellingcat</Company> <Company>Bellingcat</Company>
<Description>Given a subreddit name and a keyword, RPST (Reddit Post Scraping Tool) returns all top (by default) posts that contain the specified keyword. </Description> <Description>Retrieve Reddit posts that contain the specified keyword from a specified subreddit. </Description>
<Copyright>Copyright (c) 2023 Richard Mwewa</Copyright> <Copyright>© 2023 Richard Mwewa. All rights reserved.</Copyright>
<PackageProjectUrl>https://github.com/bellingcat/reddit-post-scraping-tool</PackageProjectUrl> <PackageProjectUrl>https://github.com/bellingcat/reddit-post-scraping-tool</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/bellingcat/reddit-post-scraping-tool</RepositoryUrl> <RepositoryUrl>https://github.com/bellingcat/reddit-post-scraping-tool</RepositoryUrl>
<AssemblyVersion>1.8.0.0</AssemblyVersion> <AssemblyVersion>1.9.0.0</AssemblyVersion>
<FileVersion>1.8.0.0</FileVersion> <FileVersion>1.9.0.0</FileVersion>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<Version>1.8.0</Version> <Version>1.9.0</Version>
<PackageTags>reddit;scraper;reddit-scraper;osint</PackageTags> <PackageTags>reddit;scraper;reddit-scraper;osint</PackageTags>
<PackageReleaseNotes></PackageReleaseNotes> <PackageReleaseNotes></PackageReleaseNotes>
<AnalysisLevel>6.0-recommended</AnalysisLevel> <AnalysisLevel>6.0-recommended</AnalysisLevel>
@@ -26,6 +26,7 @@
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>
<Product>$(AssemblyName) (Reddit Post Scraping Tool)</Product> <Product>$(AssemblyName) (Reddit Post Scraping Tool)</Product>
<AssemblyName>RPST</AssemblyName> <AssemblyName>RPST</AssemblyName>
<Title>Reddit Post Scraping Tool.</Title>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -4,14 +4,14 @@
<Compile Update="AboutBox.vb"> <Compile Update="AboutBox.vb">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Update="DeveloperBox.vb">
<SubType>Form</SubType>
</Compile>
<Compile Update="FormMain.vb"> <Compile Update="FormMain.vb">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Update="FormPosts.vb"> <Compile Update="FormPosts.vb">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Update="SplashScreen.vb">
<SubType>Form</SubType>
</Compile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -99,131 +99,99 @@ Public Class SettingsManager
' Apply the SaveToCsv setting to the menu item checkbox ' Apply the SaveToCsv setting to the menu item checkbox
FormMain.ToCSVToolStripMenuItem.Checked = CBool(settings("SaveToCsv")) FormMain.ToCSVToolStripMenuItem.Checked = CBool(settings("SaveToCsv"))
If CBool(settings("DarkMode")) Then ' Apply the color scheme based on the Dark Mode setting
' Enable dark mode for the Main form ApplyColorScheme(isDarkMode:=CBool(settings("DarkMode")))
' Background colours (I know 'Colours'/'Colors'😆) End Sub
FormMain.BackColor = ColorTranslator.FromHtml("#FF121212")
FormMain.TextBoxSubreddit.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.TextBoxKeyword.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.NumericUpDownLimit.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.NumericUpDownLimit.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.ComboBoxListing.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.ComboBoxTimeframe.BackColor = ColorTranslator.FromHtml("#FF2E2E2E")
' Foreground colours
FormMain.TextBoxKeyword.ForeColor = SystemColors.Control
FormMain.TextBoxSubreddit.ForeColor = SystemColors.Control
FormMain.NumericUpDownLimit.ForeColor = SystemColors.Control
FormMain.NumericUpDownLimit.ForeColor = SystemColors.Control
FormMain.ComboBoxListing.ForeColor = SystemColors.Control
FormMain.ComboBoxTimeframe.ForeColor = SystemColors.Control
FormMain.LabelKeyword.ForeColor = SystemColors.Control
FormMain.LabelSubreddit.ForeColor = SystemColors.Control
FormMain.LabelLimit.ForeColor = SystemColors.Control
FormMain.LabelListing.ForeColor = SystemColors.Control
FormMain.LabelTimeframe.ForeColor = SystemColors.Control
''' <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)
' Enable dark mode on 'Right Click Menu' items If isDarkMode Then
' Background colours ' Dark Mode colors
FormMain.SettingsToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("MainBackground") = ColorTranslator.FromHtml("#FF121212")
FormMain.DarkModeToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("TextBoxBackground") = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.SavePostsToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("Foreground") = SystemColors.Control
FormMain.ToJSONToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("MenuBackground") = ColorTranslator.FromHtml("#FF121212")
FormMain.ToCSVToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("AboutBackground") = ColorTranslator.FromHtml("#FF121212")
FormMain.AboutToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("AboutForeground") = SystemColors.Control
FormMain.DeveloperToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("TabPageBackground") = ColorTranslator.FromHtml("#FF2E2E2E")
FormMain.CheckForUpdatesToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("TabPageForeground") = SystemColors.Control
FormMain.QuitToolStripMenuItem.BackColor = ColorTranslator.FromHtml("#FF121212") colorMap("ButtonForeground") = Color.Black
' Foreground colours Else
FormMain.SettingsToolStripMenuItem.ForeColor = SystemColors.Control ' Light Mode colors
FormMain.DarkModeToolStripMenuItem.ForeColor = SystemColors.Control colorMap("MainBackground") = Color.Gainsboro
FormMain.SavePostsToolStripMenuItem.ForeColor = SystemColors.Control colorMap("TextBoxBackground") = SystemColors.Control
FormMain.ToJSONToolStripMenuItem.ForeColor = SystemColors.Control colorMap("Foreground") = ColorTranslator.FromHtml("#FF121212")
FormMain.ToCSVToolStripMenuItem.ForeColor = SystemColors.Control colorMap("MenuBackground") = Color.Gainsboro
FormMain.AboutToolStripMenuItem.ForeColor = SystemColors.Control colorMap("AboutBackground") = Color.Gainsboro
FormMain.DeveloperToolStripMenuItem.ForeColor = SystemColors.Control colorMap("AboutForeground") = SystemColors.WindowText
FormMain.CheckForUpdatesToolStripMenuItem.ForeColor = SystemColors.Control colorMap("TabPageBackground") = SystemColors.Control
FormMain.QuitToolStripMenuItem.ForeColor = SystemColors.Control colorMap("TabPageForeground") = SystemColors.WindowText
colorMap("ButtonForeground") = Color.Black
End If
' Applying Main Form colors
FormMain.BackColor = colorMap("MainBackground")
FormMain.TextBoxKeyword.BackColor = colorMap("TextBoxBackground")
FormMain.TextBoxSubreddit.BackColor = colorMap("TextBoxBackground")
FormMain.NumericUpDownLimit.BackColor = colorMap("TextBoxBackground")
FormMain.ComboBoxListing.BackColor = colorMap("TextBoxBackground")
FormMain.ComboBoxTimeframe.BackColor = colorMap("TextBoxBackground")
FormMain.TextBoxKeyword.ForeColor = colorMap("Foreground")
FormMain.TextBoxSubreddit.ForeColor = colorMap("Foreground")
FormMain.NumericUpDownLimit.ForeColor = colorMap("Foreground")
FormMain.ComboBoxListing.ForeColor = colorMap("Foreground")
FormMain.ComboBoxTimeframe.ForeColor = colorMap("Foreground")
FormMain.LabelKeyword.ForeColor = colorMap("Foreground")
FormMain.LabelSubreddit.ForeColor = colorMap("Foreground")
FormMain.LabelLimit.ForeColor = colorMap("Foreground")
FormMain.LabelListing.ForeColor = colorMap("Foreground")
FormMain.LabelTimeframe.ForeColor = colorMap("Foreground")
' Enable dark mode for the About box ' Applying Right-Click Menu colors
' Background colours FormMain.SettingsToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.BackColor = ColorTranslator.FromHtml("#FF121212") FormMain.DarkModeToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.LicenseRichTextBox.BackColor = ColorTranslator.FromHtml("#FF2E2E2E") FormMain.SavePostsToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.Panel1.BackColor = ColorTranslator.FromHtml("#FF121212") FormMain.ToJSONToolStripMenuItem.BackColor = colorMap("MenuBackground")
' Foreground colours FormMain.ToCSVToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.ForeColor = SystemColors.Control FormMain.AboutToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.LicenseRichTextBox.ForeColor = SystemColors.Control FormMain.CheckForUpdatesToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.LabelProgramName.ForeColor = SystemColors.Control FormMain.QuitToolStripMenuItem.BackColor = colorMap("MenuBackground")
AboutBox.LabelProgramDescription.ForeColor = SystemColors.Control FormMain.SettingsToolStripMenuItem.ForeColor = colorMap("Foreground")
AboutBox.LinkLabelVersion.ForeColor = SystemColors.Control FormMain.DarkModeToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.SavePostsToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.ToJSONToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.ToCSVToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.AboutToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.CheckForUpdatesToolStripMenuItem.ForeColor = colorMap("Foreground")
FormMain.QuitToolStripMenuItem.ForeColor = colorMap("Foreground")
' If dark mode is enabled, set the 'Dark Mode' text value to 'Light mode' ' 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
FormMain.DarkModeToolStripMenuItem.Text = "Dark Mode: Enabled" FormMain.DarkModeToolStripMenuItem.Text = "Dark Mode: Enabled"
Else Else
' Disable dark mode for the Main Form
' Background colours
FormMain.BackColor = Color.Gainsboro
FormMain.TextBoxKeyword.BackColor = SystemColors.Control
FormMain.TextBoxSubreddit.BackColor = SystemColors.Control
FormMain.NumericUpDownLimit.BackColor = SystemColors.Control
FormMain.NumericUpDownLimit.BackColor = SystemColors.Control
FormMain.ComboBoxTimeframe.BackColor = SystemColors.Control
FormMain.ComboBoxListing.BackColor = SystemColors.Control
' Foreground colours
FormMain.TextBoxKeyword.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.TextBoxSubreddit.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.NumericUpDownLimit.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.NumericUpDownLimit.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.ComboBoxListing.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.ComboBoxTimeframe.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.LabelKeyword.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.LabelSubreddit.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.LabelLimit.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.LabelListing.ForeColor = ColorTranslator.FromHtml("#FF121212")
FormMain.LabelTimeframe.ForeColor = ColorTranslator.FromHtml("#FF121212")
' Disable dark mode on 'Right Click Menu' items
' Background colours
FormMain.SettingsToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.DarkModeToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.SavePostsToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.ToJSONToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.ToCSVToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.AboutToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.DeveloperToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.CheckForUpdatesToolStripMenuItem.BackColor = Color.Gainsboro
FormMain.QuitToolStripMenuItem.BackColor = Color.Gainsboro
' Foreground colours
FormMain.SettingsToolStripMenuItem.ForeColor = Color.Black
FormMain.DarkModeToolStripMenuItem.ForeColor = Color.Black
FormMain.SavePostsToolStripMenuItem.ForeColor = Color.Black
FormMain.ToJSONToolStripMenuItem.ForeColor = Color.Black
FormMain.ToCSVToolStripMenuItem.ForeColor = Color.Black
FormMain.AboutToolStripMenuItem.ForeColor = Color.Black
FormMain.DeveloperToolStripMenuItem.ForeColor = Color.Black
FormMain.CheckForUpdatesToolStripMenuItem.ForeColor = Color.Black
FormMain.QuitToolStripMenuItem.ForeColor = Color.Black
' Disable dark mode for the About box
' Background colours
AboutBox.BackColor = Color.Gainsboro
AboutBox.ForeColor = SystemColors.WindowText
AboutBox.LicenseRichTextBox.BackColor = SystemColors.Control
AboutBox.LicenseRichTextBox.ForeColor = SystemColors.WindowText
AboutBox.Panel1.BackColor = Color.Gainsboro
' Foreground colours
AboutBox.Panel1.ForeColor = SystemColors.WindowText
AboutBox.LabelProgramName.ForeColor = SystemColors.WindowText
AboutBox.LabelProgramDescription.ForeColor = SystemColors.WindowText
AboutBox.LinkLabelVersion.ForeColor = SystemColors.WindowText
' If dark mode is disabled, set the 'Light Mode' text value to 'Dark Mode'
FormMain.DarkModeToolStripMenuItem.Text = "Dark Mode: Disabled" FormMain.DarkModeToolStripMenuItem.Text = "Dark Mode: Disabled"
End If End If
End Sub End Sub
''' <summary> ''' <summary>
''' Toggles specific settings on or off based on the provided parameters. ''' Toggles specific settings on or off based on the provided parameters.
''' </summary> ''' </summary>

127
RPST GUI/RPST/SplashScreen.Designer.vb generated Normal file
View File

@@ -0,0 +1,127 @@
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class SplashScreen
Inherits System.Windows.Forms.Form
'Form overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Friend WithEvents ApplicationTitle As Label
Friend WithEvents Version As Label
Friend WithEvents Copyright As Label
Friend WithEvents MainLayoutPanel As TableLayoutPanel
Friend WithEvents DetailsLayoutPanel As TableLayoutPanel
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Dim resources As ComponentModel.ComponentResourceManager = New ComponentModel.ComponentResourceManager(GetType(SplashScreen))
MainLayoutPanel = New TableLayoutPanel()
DetailsLayoutPanel = New TableLayoutPanel()
Copyright = New Label()
Version = New Label()
ApplicationTitle = New Label()
MainLayoutPanel.SuspendLayout()
DetailsLayoutPanel.SuspendLayout()
SuspendLayout()
'
' MainLayoutPanel
'
MainLayoutPanel.BackColor = Color.White
MainLayoutPanel.BackgroundImage = CType(resources.GetObject("MainLayoutPanel.BackgroundImage"), Image)
MainLayoutPanel.BackgroundImageLayout = ImageLayout.Stretch
MainLayoutPanel.ColumnCount = 2
MainLayoutPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 270F))
MainLayoutPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 73F))
MainLayoutPanel.Controls.Add(DetailsLayoutPanel, 1, 1)
MainLayoutPanel.Controls.Add(ApplicationTitle, 1, 0)
MainLayoutPanel.Dock = DockStyle.Fill
MainLayoutPanel.Location = New Point(0, 0)
MainLayoutPanel.Name = "MainLayoutPanel"
MainLayoutPanel.RowStyles.Add(New RowStyle(SizeType.Absolute, 223F))
MainLayoutPanel.RowStyles.Add(New RowStyle(SizeType.Absolute, 33F))
MainLayoutPanel.Size = New Size(483, 313)
MainLayoutPanel.TabIndex = 0
'
' DetailsLayoutPanel
'
DetailsLayoutPanel.Anchor = AnchorStyles.None
DetailsLayoutPanel.BackColor = Color.Transparent
DetailsLayoutPanel.ColumnCount = 1
DetailsLayoutPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 142F))
DetailsLayoutPanel.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 142F))
DetailsLayoutPanel.Controls.Add(Copyright, 0, 1)
DetailsLayoutPanel.Controls.Add(Version, 0, 0)
DetailsLayoutPanel.Location = New Point(273, 226)
DetailsLayoutPanel.Name = "DetailsLayoutPanel"
DetailsLayoutPanel.RowCount = 2
DetailsLayoutPanel.RowStyles.Add(New RowStyle(SizeType.Percent, 61.70213F))
DetailsLayoutPanel.RowStyles.Add(New RowStyle(SizeType.Percent, 38.29787F))
DetailsLayoutPanel.Size = New Size(207, 84)
DetailsLayoutPanel.TabIndex = 1
'
' Copyright
'
Copyright.Anchor = AnchorStyles.None
Copyright.BackColor = Color.Transparent
Copyright.Font = New Font("Segoe UI", 8.25F, FontStyle.Regular, GraphicsUnit.Point)
Copyright.Location = New Point(3, 51)
Copyright.Name = "Copyright"
Copyright.Size = New Size(201, 33)
Copyright.TabIndex = 2
Copyright.Text = "Copyright"
'
' Version
'
Version.Anchor = AnchorStyles.None
Version.BackColor = Color.Transparent
Version.Font = New Font("Segoe UI Semibold", 9F, FontStyle.Bold Or FontStyle.Underline, GraphicsUnit.Point)
Version.Location = New Point(3, 4)
Version.Name = "Version"
Version.Size = New Size(201, 43)
Version.TabIndex = 1
Version.Text = "Version"
'
' ApplicationTitle
'
ApplicationTitle.Anchor = AnchorStyles.None
ApplicationTitle.BackColor = Color.Transparent
ApplicationTitle.Font = New Font("Segoe UI", 18F, FontStyle.Bold, GraphicsUnit.Point)
ApplicationTitle.Location = New Point(273, 0)
ApplicationTitle.Name = "ApplicationTitle"
ApplicationTitle.Size = New Size(207, 223)
ApplicationTitle.TabIndex = 0
ApplicationTitle.Text = "Reddit Post ScrapingTool."
ApplicationTitle.TextAlign = ContentAlignment.BottomLeft
'
' SplashScreen
'
AutoScaleDimensions = New SizeF(7F, 15F)
AutoScaleMode = AutoScaleMode.Font
ClientSize = New Size(483, 313)
ControlBox = False
Controls.Add(MainLayoutPanel)
FormBorderStyle = FormBorderStyle.FixedSingle
MaximizeBox = False
MinimizeBox = False
Name = "SplashScreen"
ShowInTaskbar = False
StartPosition = FormStartPosition.CenterScreen
MainLayoutPanel.ResumeLayout(False)
DetailsLayoutPanel.ResumeLayout(False)
ResumeLayout(False)
End Sub
End Class

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
Public NotInheritable Class SplashScreen
Private Sub SplashScreen_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Version info
Version.Text = $"Version {My.Application.Info.Version}"
'Copyright info
Copyright.Text = My.Application.Info.Copyright
End Sub
End Class

View File

@@ -3,6 +3,36 @@ Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq Imports Newtonsoft.Json.Linq
Public Class Utilities Public Class Utilities
''' <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($"MIT License
{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.", "License Agreement", MessageBoxButtons.YesNo, MessageBoxIcon.Information)
Return result
End Function
''' <summary> ''' <summary>
''' Checks for the existence of the 'logs' directory under the 'RPST' directory within the user's AppData\Roaming folder. ''' Checks for the existence of the 'logs' directory under the 'RPST' directory within the user's AppData\Roaming folder.
@@ -39,24 +69,20 @@ Public Class Utilities
Public Shared Function CollectInputs() As (Keyword As String, Subreddit As String, Listing As String, Limit As Integer, Timeframe As String)? Public Shared Function CollectInputs() As (Keyword As String, Subreddit As String, Listing As String, Limit As Integer, Timeframe As String)?
Dim keyword As String = FormMain.TextBoxKeyword.Text.Trim() Dim keyword As String = FormMain.TextBoxKeyword.Text.Trim()
Dim subreddit As String = FormMain.TextBoxSubreddit.Text.Trim() Dim subreddit As String = FormMain.TextBoxSubreddit.Text.Trim()
''' <summary> ' Convert the Listing and Subreddit to lowercase using InvariantCulture.
''' Convert the Listing and Subreddit to lowercase using InvariantCulture.
''' <summary>
Dim listing As String = If(String.IsNullOrEmpty(FormMain.ComboBoxListing.Text), "top", FormMain.ComboBoxListing.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim()) Dim listing As String = If(String.IsNullOrEmpty(FormMain.ComboBoxListing.Text), "top", FormMain.ComboBoxListing.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim())
Dim timeframe As String = If(String.IsNullOrEmpty(FormMain.ComboBoxTimeframe.Text), "all", FormMain.ComboBoxTimeframe.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim()) Dim timeframe As String = If(String.IsNullOrEmpty(FormMain.ComboBoxTimeframe.Text), "all", FormMain.ComboBoxTimeframe.Text.ToLower(Globalization.CultureInfo.InvariantCulture).Trim())
Dim limit As Integer = FormMain.NumericUpDownLimit.Value Dim limit As Integer = FormMain.NumericUpDownLimit.Value
''' <summary> ' Validate inputs.
''' Validate inputs.
''' <summary>
If String.IsNullOrEmpty(keyword) AndAlso String.IsNullOrEmpty(subreddit) Then If String.IsNullOrEmpty(keyword) AndAlso String.IsNullOrEmpty(subreddit) Then
MessageBox.Show("Keyword and Subreddit should not be empty.", "Invalid Inputs", MessageBoxButtons.OK, MessageBoxIcon.Warning) MessageBox.Show("Keyword and Subreddit should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing Return Nothing
ElseIf String.IsNullOrEmpty(keyword) Then ElseIf String.IsNullOrEmpty(keyword) Then
MessageBox.Show("Keyword field should not be empty.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) MessageBox.Show("Keyword field should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing Return Nothing
ElseIf String.IsNullOrEmpty(subreddit) Then ElseIf String.IsNullOrEmpty(subreddit) Then
MessageBox.Show("Subreddit field should not be empty.", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Warning) MessageBox.Show("Subreddit field should not be empty.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
Return Nothing Return Nothing
End If End If
Return (keyword, subreddit, listing, limit, timeframe) Return (keyword, subreddit, listing, limit, timeframe)
@@ -73,7 +99,7 @@ Public Class Utilities
''' to JSON with an indented format and written to the chosen file. ''' to JSON with an indented format and written to the chosen file.
''' A success message will be displayed to the user upon successful save. ''' A success message will be displayed to the user upon successful save.
''' </remarks> ''' </remarks>
Public Shared Sub SavePostsToJson(Posts As Object) Public Shared Sub SavePostsToJson(posts As Object)
Dim saveFileDialog As New SaveFileDialog With { Dim saveFileDialog As New SaveFileDialog With {
.Filter = "JSON files (*.json)|*.json", .Filter = "JSON files (*.json)|*.json",
.Title = "Save posts to JSON" .Title = "Save posts to JSON"
@@ -84,11 +110,11 @@ Public Class Utilities
Dim serializerSettings As New JsonSerializerSettings With { Dim serializerSettings As New JsonSerializerSettings With {
.Formatting = Formatting.Indented .Formatting = Formatting.Indented
} }
Dim json As String = JsonConvert.SerializeObject(Posts, serializerSettings) Dim json As String = JsonConvert.SerializeObject(posts, serializerSettings)
File.WriteAllText(fileName, json) File.WriteAllText(fileName, json)
MessageBox.Show($"Posts saved to {fileName}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information) MessageBox.Show($"Posts saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If End If
End Sub End Sub
@@ -110,9 +136,7 @@ Public Class Utilities
If saveFileDialog.ShowDialog() = DialogResult.OK Then If saveFileDialog.ShowDialog() = DialogResult.OK Then
Dim fileName As String = saveFileDialog.FileName Dim fileName As String = saveFileDialog.FileName
Using csvWriter As New StreamWriter(fileName) Using csvWriter As New StreamWriter(fileName)
''' <summary> ' Write the header.
''' Write the header.
''' <summary>
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") 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 Dim postCount As Integer = 0
@@ -122,32 +146,21 @@ Public Class Utilities
Next Next
End Using End Using
MessageBox.Show($"Posts saved to {fileName}", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information) MessageBox.Show($"Posts saved to {fileName}", "Saved", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If End If
End Sub End Sub
''' <summary> ''' <summary>
''' Shows the license notice in a messagebox. ''' Checks if the "launch.log" file exists in the directory: C:\Users\<username>\AppData\Roaming\RPSTl\logs.
''' </summary> ''' </summary>
''' <remarks> ''' <remarks>
''' The license text is retrieved from the AboutBox.LicenseText property. ''' If the file doesn't exist, it shows a MessageBox with the License Agreement Notice with buttons Yes and No.
''' The messagebox is displayed with the title "License" and an information icon. ''' If the user clicks on the Yes button, it creates one the launch.log file, otherwise assume the user did not agree to the License and close the program.
''' The launc.log file is used to determine whether the program has been run before.
''' </remarks> ''' </remarks>
Public Shared Sub LicenseNotice()
MessageBox.Show(AboutBox.LicenseText, "License", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
''' <summary>
''' Checks if the "first-launch.log" file exists in the directory: C:\Users\<username>\AppData\Roaming\RedditPostScrapingTool\logs.
''' If the file doesn't exist, it creates one. This file is used to determine whether the program has been run before.
''' If the program is being run for the first time, a license notice will be displayed.
''' </summary>
Public Shared Sub LogFirstTimeLaunch() Public Shared Sub LogFirstTimeLaunch()
Dim filePath As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RPST", "logs", "first-launch.log") Dim filePath As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RPST", "logs", "launch.log")
Dim textToWrite As String = $" Dim textToWrite As String = $"
{My.Application.Info.AssemblyName} {My.Application.Info.AssemblyName}
------------------------- -------------------------
@@ -158,10 +171,16 @@ Host: {Environment.MachineName}
OS: {Environment.OSVersion} OS: {Environment.OSVersion}
x64: {Environment.Is64BitOperatingSystem} x64: {Environment.Is64BitOperatingSystem}
First launched on: {DateTime.Now}" First launched on: {DateTime.Now}"
If Not File.Exists(filePath) Then If Not File.Exists(filePath) Then
LicenseNotice() Dim result As DialogResult = LicenseAgreement()
File.WriteAllText(filePath, textToWrite) If result = DialogResult.Yes Then
Else File.WriteAllText(filePath, textToWrite)
Else
FormMain.Close()
End If
End If End If
End Sub End Sub
End Class End Class

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -7,8 +7,8 @@ packages = ["rpst"]
[project] [project]
name = "reddit-post-scraping-tool" name = "reddit-post-scraping-tool"
version = "1.8.0.0" version = "1.9.0.0"
description = "Given a subreddit name and a keyword, RPST returns all top (by default) posts that contain the specified keyword." description = "Retrieve Reddit posts that contain the specified keyword from a specified subreddit."
readme = "README.md" readme = "README.md"
requires-python = ">=3.8" requires-python = ">=3.8"
license = {file = "LICENSE"} license = {file = "LICENSE"}

View File

@@ -21,7 +21,7 @@ def run():
try: try:
# Check for updates # Check for updates
check_updates(version_tag="1.8.0.0") check_updates(version_tag="1.9.0.0")
# Get posts with the provided/parsed arguments # Get posts with the provided/parsed arguments
get_posts(args=args) get_posts(args=args)

View File

@@ -34,8 +34,7 @@ def create_parser():
""" """
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="RPST (Reddit Post Scraping Tool) —by Richard Mwewa | https://about.me/rly0nheart", description="RPST (Reddit Post Scraping Tool) —by Richard Mwewa | https://about.me/rly0nheart",
epilog="Given a subreddit name and a keyword, " epilog="Retrieve Reddit posts that contain the specified keyword from a specified subreddit."
"RPST returns all top (by default) posts that contain the specified keyword.",
) )
parser.add_argument( parser.add_argument(