From 6bbe1e1a9a91be78be76e98c86f642693a1e878e Mon Sep 17 00:00:00 2001 From: Julian V Date: Sun, 29 Mar 2026 14:43:19 -0400 Subject: [PATCH 01/36] Created Basic Dialogue Box Functionality for config.ini editing --- src/dialogueBox.py | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/dialogueBox.py diff --git a/src/dialogueBox.py b/src/dialogueBox.py new file mode 100644 index 0000000..0a442a6 --- /dev/null +++ b/src/dialogueBox.py @@ -0,0 +1,106 @@ +import tkinter as tk +import configparser +import tracemalloc + +def Save_Button(): + + #Get new values from the user entries + newTo = inpTo.get() + newFrom = inpFrom.get() + newSubject = inpSubject.get() + newMessage = inpMessage.get("1.0", "end-1c") + newSendTime = inpSendTime.get() + newFreq = freqVar.get() + + #Updates config values + co["Email"]["EMAIL_TO"] = newTo + co["Email"]["EMAIL_FROM"] = newFrom + co["Email"]["EMAIL_SUBJECT"] = newSubject + co["Email"]["EMAIL_MESSAGE"] = newMessage + co["Email"]["EMAIL_SEND_TIME"] = newSendTime + co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + + #Writes new config values to file + with open("config.ini", "w") as f: + co.write(f) + + #Successful save message + successfulSave = tk.Label(root, text="Saved Successfully!") + successfulSave.grid(row = 11, column = 0, columnspan = 2, pady = 5) + root.after(2000, successfulSave.destroy) + +co = configparser.ConfigParser() +co.read("config.ini") + +root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) +frame = tk.Frame(root) + +root.geometry("") + +#Title +title = tk.Label(root, text="Welcome to VerityBot!") + +#Email_TO +toLabel = tk.Label(root, text="Email Receiver").grid(row=1, column=0) +inpTo = tk.Entry(root, width=50) + +#EMAIL_FROM +fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) +inpFrom = tk.Entry(root, width=50) + +#EMAIL_SUBJECT +subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) +inpSubject = tk.Entry(root, width=50) + +#EMAIL_MESSAGE +messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) +inpMessage = tk.Text(root, height=10, width=50) + +#EMAIL_SEND_TIME +sendTimeLabel = tk.Label(root, text="Email Send Time").grid(row=5, column=0) +inpSendTime = tk.Entry(root) + + +#TIME_DELTA_RECURRING +freqLabel1 = tk.Label(root, text="Update Verkada data every") +freqVar = tk.StringVar() +verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") +freqVar.set(verkadaTimeRecurring) +freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60) +freqLabel2 = tk.Label(frame, text="day(s)") + +#Title Insertion +title.grid(row=0, column=0, columnspan=2, pady=10) + +#Email Entries +inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') +inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') +inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') +inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') +inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') + +#Verkada Entries +freqLabel1.grid(row=6, column=0, padx=5, pady=5) +frame.grid(row=6, column=1, padx=5, pady=5, sticky='w') +freqSpinbox.grid(row=0, column=0) +freqLabel2.grid(row=0, column=1) + +#Getting existing config values +emailTo = co.get("Email", "EMAIL_TO") +emailFrom = co.get("Email", "EMAIL_FROM") +emailSubject = co.get("Email", "EMAIL_SUBJECT") +emailMessage = co.get("Email", "EMAIL_MESSAGE") +emailSendTime = co.get("Email", "EMAIL_SEND_TIME") + +#Inserting existing config values +inpTo.insert(0, emailTo) +inpFrom.insert(0, emailFrom) +inpSubject.insert(0, emailSubject) +inpMessage.insert("1.0", emailMessage) +inpSendTime.insert(0, emailSendTime) + +#Save Button +button = tk.Button(root, text="Save", width=25, command=Save_Button) +button.grid(row = 7, column = 0, columnspan = 2, pady = 5) + +root.mainloop() \ No newline at end of file From 3cc34918f7cfc4cdf3d7ab6914b3e5808a578274 Mon Sep 17 00:00:00 2001 From: Julian V Date: Mon, 30 Mar 2026 16:52:19 -0400 Subject: [PATCH 02/36] Removed tracemalloc import statement --- src/dialogueBox.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 0a442a6..c333200 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -1,6 +1,5 @@ import tkinter as tk import configparser -import tracemalloc def Save_Button(): From 9f31d6295bb78567753542119a70c7d89beffafb Mon Sep 17 00:00:00 2001 From: Julian V Date: Sun, 29 Mar 2026 14:43:19 -0400 Subject: [PATCH 03/36] Created Basic Dialogue Box Functionality for config.ini editing --- src/dialogueBox.py | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 src/dialogueBox.py diff --git a/src/dialogueBox.py b/src/dialogueBox.py new file mode 100644 index 0000000..0a442a6 --- /dev/null +++ b/src/dialogueBox.py @@ -0,0 +1,106 @@ +import tkinter as tk +import configparser +import tracemalloc + +def Save_Button(): + + #Get new values from the user entries + newTo = inpTo.get() + newFrom = inpFrom.get() + newSubject = inpSubject.get() + newMessage = inpMessage.get("1.0", "end-1c") + newSendTime = inpSendTime.get() + newFreq = freqVar.get() + + #Updates config values + co["Email"]["EMAIL_TO"] = newTo + co["Email"]["EMAIL_FROM"] = newFrom + co["Email"]["EMAIL_SUBJECT"] = newSubject + co["Email"]["EMAIL_MESSAGE"] = newMessage + co["Email"]["EMAIL_SEND_TIME"] = newSendTime + co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + + #Writes new config values to file + with open("config.ini", "w") as f: + co.write(f) + + #Successful save message + successfulSave = tk.Label(root, text="Saved Successfully!") + successfulSave.grid(row = 11, column = 0, columnspan = 2, pady = 5) + root.after(2000, successfulSave.destroy) + +co = configparser.ConfigParser() +co.read("config.ini") + +root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) +frame = tk.Frame(root) + +root.geometry("") + +#Title +title = tk.Label(root, text="Welcome to VerityBot!") + +#Email_TO +toLabel = tk.Label(root, text="Email Receiver").grid(row=1, column=0) +inpTo = tk.Entry(root, width=50) + +#EMAIL_FROM +fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) +inpFrom = tk.Entry(root, width=50) + +#EMAIL_SUBJECT +subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) +inpSubject = tk.Entry(root, width=50) + +#EMAIL_MESSAGE +messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) +inpMessage = tk.Text(root, height=10, width=50) + +#EMAIL_SEND_TIME +sendTimeLabel = tk.Label(root, text="Email Send Time").grid(row=5, column=0) +inpSendTime = tk.Entry(root) + + +#TIME_DELTA_RECURRING +freqLabel1 = tk.Label(root, text="Update Verkada data every") +freqVar = tk.StringVar() +verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") +freqVar.set(verkadaTimeRecurring) +freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60) +freqLabel2 = tk.Label(frame, text="day(s)") + +#Title Insertion +title.grid(row=0, column=0, columnspan=2, pady=10) + +#Email Entries +inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') +inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') +inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') +inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') +inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') + +#Verkada Entries +freqLabel1.grid(row=6, column=0, padx=5, pady=5) +frame.grid(row=6, column=1, padx=5, pady=5, sticky='w') +freqSpinbox.grid(row=0, column=0) +freqLabel2.grid(row=0, column=1) + +#Getting existing config values +emailTo = co.get("Email", "EMAIL_TO") +emailFrom = co.get("Email", "EMAIL_FROM") +emailSubject = co.get("Email", "EMAIL_SUBJECT") +emailMessage = co.get("Email", "EMAIL_MESSAGE") +emailSendTime = co.get("Email", "EMAIL_SEND_TIME") + +#Inserting existing config values +inpTo.insert(0, emailTo) +inpFrom.insert(0, emailFrom) +inpSubject.insert(0, emailSubject) +inpMessage.insert("1.0", emailMessage) +inpSendTime.insert(0, emailSendTime) + +#Save Button +button = tk.Button(root, text="Save", width=25, command=Save_Button) +button.grid(row = 7, column = 0, columnspan = 2, pady = 5) + +root.mainloop() \ No newline at end of file From b4c3c7289f1c5bd4472d6b4018d881a7024f57b5 Mon Sep 17 00:00:00 2001 From: Julian V Date: Mon, 30 Mar 2026 16:52:19 -0400 Subject: [PATCH 04/36] Removed tracemalloc import statement --- src/dialogueBox.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 0a442a6..c333200 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -1,6 +1,5 @@ import tkinter as tk import configparser -import tracemalloc def Save_Button(): From 9af0231418442b3d36c81a95164ffceaa2c6bdd8 Mon Sep 17 00:00:00 2001 From: Julian V Date: Mon, 30 Mar 2026 18:39:49 -0400 Subject: [PATCH 05/36] Added .env values to the Dialogue Box Added more field for the user to input to make all unique values easy to change through the dialogue box. --- src/dialogueBox.py | 74 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index c333200..3ee3aa9 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -1,5 +1,8 @@ +import os import tkinter as tk import configparser +import dotenv + def Save_Button(): @@ -10,6 +13,11 @@ def Save_Button(): newMessage = inpMessage.get("1.0", "end-1c") newSendTime = inpSendTime.get() newFreq = freqVar.get() + newEmailPass = inpEmailPass.get() + newVerkAPI = inpApiKey.get("1.0", "end-1c") + newElastPass = inpElastPass.get() + newKibPass =inpKibPass.get() + newKibEnc = inpKibEnc.get("1.0", "end-1c") #Updates config values co["Email"]["EMAIL_TO"] = newTo @@ -18,6 +26,11 @@ def Save_Button(): co["Email"]["EMAIL_MESSAGE"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) + dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) + dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) + dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) + dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file with open("config.ini", "w") as f: @@ -25,11 +38,13 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 11, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) co = configparser.ConfigParser() co.read("config.ini") +dotenv.load_dotenv() +dotenv_file = ".env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -40,7 +55,7 @@ def Save_Button(): title = tk.Label(root, text="Welcome to VerityBot!") #Email_TO -toLabel = tk.Label(root, text="Email Receiver").grid(row=1, column=0) +toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) inpTo = tk.Entry(root, width=50) #EMAIL_FROM @@ -53,20 +68,40 @@ def Save_Button(): #EMAIL_MESSAGE messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) -inpMessage = tk.Text(root, height=10, width=50) +inpMessage = tk.Text(root, height=10, width=38) #EMAIL_SEND_TIME -sendTimeLabel = tk.Label(root, text="Email Send Time").grid(row=5, column=0) -inpSendTime = tk.Entry(root) +sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) +inpSendTime = tk.Entry(root, width = 7) +#Email Password +emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) +inpEmailPass = tk.Entry(root, width=50) #TIME_DELTA_RECURRING -freqLabel1 = tk.Label(root, text="Update Verkada data every") +freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") freqVar.set(verkadaTimeRecurring) -freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60) -freqLabel2 = tk.Label(frame, text="day(s)") +freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) +freqLabel2 = tk.Label(frame, text="time(s) per day") + +#Verkada API +apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) +inpApiKey = tk.Text(root, height = 3, width=38) + +#Elastic Password +elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) +inpElastPass = tk.Entry(root, width=50) + +#Kibana System Password +kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) +inpKibPass = tk.Entry(root, width=50) + +#Kibana Encryption Key +kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) +#Add functionality +inpKibEnc = tk.Text(root, height = 2, width = 38) #Title Insertion title.grid(row=0, column=0, columnspan=2, pady=10) @@ -77,19 +112,31 @@ def Save_Button(): inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') +inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') #Verkada Entries -freqLabel1.grid(row=6, column=0, padx=5, pady=5) -frame.grid(row=6, column=1, padx=5, pady=5, sticky='w') +freqLabel1.grid(row=7, column=0, padx=5, pady=5) +frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') freqSpinbox.grid(row=0, column=0) freqLabel2.grid(row=0, column=1) +#ENV Entries +inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') +inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') +inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') +inpKibEnc.grid(row=11, column=1, padx=5, pady=5, sticky='w') + #Getting existing config values emailTo = co.get("Email", "EMAIL_TO") emailFrom = co.get("Email", "EMAIL_FROM") emailSubject = co.get("Email", "EMAIL_SUBJECT") emailMessage = co.get("Email", "EMAIL_MESSAGE") emailSendTime = co.get("Email", "EMAIL_SEND_TIME") +emailPass = os.getenv("EMAIL_PASSWORD") +verkAPIKey = os.getenv("VERKADA_API_KEY") +elasticPass = os.getenv("ELASTIC_PASSWORD") +kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") +kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") #Inserting existing config values inpTo.insert(0, emailTo) @@ -97,9 +144,14 @@ def Save_Button(): inpSubject.insert(0, emailSubject) inpMessage.insert("1.0", emailMessage) inpSendTime.insert(0, emailSendTime) +inpEmailPass.insert(0, emailPass or "") +inpApiKey.insert("1.0", verkAPIKey or "") +inpElastPass.insert(0, elasticPass or "") +inpKibPass.insert(0, kibanaPass or "") +inpKibEnc.insert("1.0", kibanaEncKey or "") #Save Button button = tk.Button(root, text="Save", width=25, command=Save_Button) -button.grid(row = 7, column = 0, columnspan = 2, pady = 5) +button.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.mainloop() \ No newline at end of file From c129d2ea4254792d96c0f98502f6764c578ae53e Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 1 Apr 2026 15:29:48 -0400 Subject: [PATCH 06/36] Updated Dialogue Box Values Updated the variable names for many of the .env and config edits. --- src/dialogueBox.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 3ee3aa9..56fa29c 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -25,7 +25,7 @@ def Save_Button(): co["Email"]["EMAIL_SUBJECT"] = newSubject co["Email"]["EMAIL_MESSAGE"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) @@ -78,11 +78,11 @@ def Save_Button(): emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) inpEmailPass = tk.Entry(root, width=50) -#TIME_DELTA_RECURRING +#TIME_DELTA_INSTALLATION freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() -verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") -freqVar.set(verkadaTimeRecurring) +verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") +freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) freqLabel2 = tk.Label(frame, text="time(s) per day") From 331137b7b9c7226db6853c80d317185049e963dd Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 13:50:49 -0400 Subject: [PATCH 07/36] Added Variable Email Message Capabilities to DB --- config.ini.example | 1 + src/dialogueBox.py | 6 +++--- src/email_sender.py | 15 +++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/config.ini.example b/config.ini.example index 8dcb3b8..8fbb144 100644 --- a/config.ini.example +++ b/config.ini.example @@ -1,6 +1,7 @@ [Email] EMAIL_TO=receiver_email@example.com EMAIL_FROM=your_email@example.com +EMAIL_BODY_PREFIX=Click the link below to access the Kibana dashboard: EMAIL_SUBJECT=Subject for the Email EMAIL_SEND_TIME=09:00 diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 56fa29c..0970993 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -23,7 +23,7 @@ def Save_Button(): co["Email"]["EMAIL_TO"] = newTo co["Email"]["EMAIL_FROM"] = newFrom co["Email"]["EMAIL_SUBJECT"] = newSubject - co["Email"]["EMAIL_MESSAGE"] = newMessage + co["Email"]["EMAIL_BODY_PREFIX"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) @@ -66,7 +66,7 @@ def Save_Button(): subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) inpSubject = tk.Entry(root, width=50) -#EMAIL_MESSAGE +#EMAIL_BODY_PREFIX messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) inpMessage = tk.Text(root, height=10, width=38) @@ -130,7 +130,7 @@ def Save_Button(): emailTo = co.get("Email", "EMAIL_TO") emailFrom = co.get("Email", "EMAIL_FROM") emailSubject = co.get("Email", "EMAIL_SUBJECT") -emailMessage = co.get("Email", "EMAIL_MESSAGE") +emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") emailSendTime = co.get("Email", "EMAIL_SEND_TIME") emailPass = os.getenv("EMAIL_PASSWORD") verkAPIKey = os.getenv("VERKADA_API_KEY") diff --git a/src/email_sender.py b/src/email_sender.py index 7810316..47f2df3 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -45,14 +45,21 @@ def get_ip(): return IP IP = get_ip() + +# read prefix only from config (no env fallback) +body_prefix = config['Email'].get( + 'EMAIL_BODY_PREFIX', 'Click the link below to access the Kibana dashboard:' +) + html = f""" - -

Click the link below to access the Kibana dashboard:

- Kibana Dashboard - + +

{body_prefix}

+ Kibana Dashboard + """ + message.attach(MIMEText(html, "html")) load_dotenv() From 9e66d314e29997246dd1a478aa7059d2dac58825 Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 20:50:07 -0400 Subject: [PATCH 08/36] Removed "/" to make the config parser find ini in the same folder the example is. --- src/ConfigReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 99633a2..08b3ff1 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("/config.ini") +config.read("config.ini") From 61679409772c894888d7d83b75ad82f35254a61a Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 21:41:34 -0400 Subject: [PATCH 09/36] Added Kibana Encryption Key Generator Button Added a button that allows the user to generate a unique Kibana Encryption key without entering the terminal. --- src/dialogueBox.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 0970993..f46297d 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -2,6 +2,8 @@ import tkinter as tk import configparser import dotenv +import secrets +import base64 def Save_Button(): @@ -38,9 +40,15 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) +def generate_kibana_key(): + key = base64.b64encode(secrets.token_bytes(32)).decode() + inpKibEnc.delete("1.0", "end") + inpKibEnc.insert("1.0", key or "") + + co = configparser.ConfigParser() co.read("config.ini") dotenv.load_dotenv() @@ -48,6 +56,7 @@ def Save_Button(): root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) +frame2 = tk.Frame(root) root.geometry("") @@ -100,8 +109,11 @@ def Save_Button(): #Kibana Encryption Key kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) -#Add functionality -inpKibEnc = tk.Text(root, height = 2, width = 38) +inpKibEnc = tk.Text(frame2, height = 2, width = 23) +#Generate Encryption Key Button +encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) + + #Title Insertion title.grid(row=0, column=0, columnspan=2, pady=10) @@ -124,7 +136,9 @@ def Save_Button(): inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') -inpKibEnc.grid(row=11, column=1, padx=5, pady=5, sticky='w') +frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') +inpKibEnc.grid(row=0, column=0) +encButton.grid(row=0, column=1, padx=5) #Getting existing config values emailTo = co.get("Email", "EMAIL_TO") @@ -151,7 +165,7 @@ def Save_Button(): inpKibEnc.insert("1.0", kibanaEncKey or "") #Save Button -button = tk.Button(root, text="Save", width=25, command=Save_Button) +button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.mainloop() \ No newline at end of file From 0340a6fee7b17211639ae09cef69d423a96ab906 Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 02:26:40 -0400 Subject: [PATCH 10/36] Made Dialogue Box pop up when program is run When the program begins running it will now run the dialogue box initialization function. --- src/Application.py | 3 + src/dialogueBox.py | 327 +++++++++++++++++++++++---------------------- 2 files changed, 167 insertions(+), 163 deletions(-) diff --git a/src/Application.py b/src/Application.py index 3609ff6..8571fa8 100644 --- a/src/Application.py +++ b/src/Application.py @@ -2,11 +2,14 @@ import CLI import ElasticSearch import os +import dialogueBox from Verkada import VerkadaContext from ConfigReader import config from datetime import datetime, date +dialogueBox.init() + ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: print("ERROR: ELASTIC_PASSWORD has not been set, exiting...") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index f46297d..a8400ea 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -6,166 +6,167 @@ import base64 -def Save_Button(): - - #Get new values from the user entries - newTo = inpTo.get() - newFrom = inpFrom.get() - newSubject = inpSubject.get() - newMessage = inpMessage.get("1.0", "end-1c") - newSendTime = inpSendTime.get() - newFreq = freqVar.get() - newEmailPass = inpEmailPass.get() - newVerkAPI = inpApiKey.get("1.0", "end-1c") - newElastPass = inpElastPass.get() - newKibPass =inpKibPass.get() - newKibEnc = inpKibEnc.get("1.0", "end-1c") - - #Updates config values - co["Email"]["EMAIL_TO"] = newTo - co["Email"]["EMAIL_FROM"] = newFrom - co["Email"]["EMAIL_SUBJECT"] = newSubject - co["Email"]["EMAIL_BODY_PREFIX"] = newMessage - co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq - dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) - dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) - dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) - dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) - dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) - - #Writes new config values to file - with open("config.ini", "w") as f: - co.write(f) - - #Successful save message - successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) - root.after(2000, successfulSave.destroy) - -def generate_kibana_key(): - key = base64.b64encode(secrets.token_bytes(32)).decode() - inpKibEnc.delete("1.0", "end") - inpKibEnc.insert("1.0", key or "") - - -co = configparser.ConfigParser() -co.read("config.ini") -dotenv.load_dotenv() -dotenv_file = ".env" - -root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) -frame = tk.Frame(root) -frame2 = tk.Frame(root) - -root.geometry("") - -#Title -title = tk.Label(root, text="Welcome to VerityBot!") - -#Email_TO -toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) -inpTo = tk.Entry(root, width=50) - -#EMAIL_FROM -fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) -inpFrom = tk.Entry(root, width=50) - -#EMAIL_SUBJECT -subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) -inpSubject = tk.Entry(root, width=50) - -#EMAIL_BODY_PREFIX -messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) -inpMessage = tk.Text(root, height=10, width=38) - -#EMAIL_SEND_TIME -sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) -inpSendTime = tk.Entry(root, width = 7) - -#Email Password -emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) -inpEmailPass = tk.Entry(root, width=50) - -#TIME_DELTA_INSTALLATION -freqLabel1 = tk.Label(root, text="Update Verkada") -freqVar = tk.StringVar() -verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") -freqVar.set(verkadaTimeInstallation) -freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) -freqLabel2 = tk.Label(frame, text="time(s) per day") - -#Verkada API -apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) -inpApiKey = tk.Text(root, height = 3, width=38) - -#Elastic Password -elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) -inpElastPass = tk.Entry(root, width=50) - -#Kibana System Password -kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) -inpKibPass = tk.Entry(root, width=50) - -#Kibana Encryption Key -kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) -inpKibEnc = tk.Text(frame2, height = 2, width = 23) -#Generate Encryption Key Button -encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) - - - -#Title Insertion -title.grid(row=0, column=0, columnspan=2, pady=10) - -#Email Entries -inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') -inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') -inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') -inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') -inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') -inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') - -#Verkada Entries -freqLabel1.grid(row=7, column=0, padx=5, pady=5) -frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') -freqSpinbox.grid(row=0, column=0) -freqLabel2.grid(row=0, column=1) - -#ENV Entries -inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') -inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') -inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') -frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') -inpKibEnc.grid(row=0, column=0) -encButton.grid(row=0, column=1, padx=5) - -#Getting existing config values -emailTo = co.get("Email", "EMAIL_TO") -emailFrom = co.get("Email", "EMAIL_FROM") -emailSubject = co.get("Email", "EMAIL_SUBJECT") -emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") -emailSendTime = co.get("Email", "EMAIL_SEND_TIME") -emailPass = os.getenv("EMAIL_PASSWORD") -verkAPIKey = os.getenv("VERKADA_API_KEY") -elasticPass = os.getenv("ELASTIC_PASSWORD") -kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") -kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") - -#Inserting existing config values -inpTo.insert(0, emailTo) -inpFrom.insert(0, emailFrom) -inpSubject.insert(0, emailSubject) -inpMessage.insert("1.0", emailMessage) -inpSendTime.insert(0, emailSendTime) -inpEmailPass.insert(0, emailPass or "") -inpApiKey.insert("1.0", verkAPIKey or "") -inpElastPass.insert(0, elasticPass or "") -inpKibPass.insert(0, kibanaPass or "") -inpKibEnc.insert("1.0", kibanaEncKey or "") - -#Save Button -button = tk.Button(root, text="Save", width=20, command=Save_Button) -button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - -root.mainloop() \ No newline at end of file + + +def init(): + + def Save_Button(): + + #Get new values from the user entries + newTo = inpTo.get() + newFrom = inpFrom.get() + newSubject = inpSubject.get() + newMessage = inpMessage.get("1.0", "end-1c") + newSendTime = inpSendTime.get() + newFreq = freqVar.get() + newEmailPass = inpEmailPass.get() + newVerkAPI = inpApiKey.get("1.0", "end-1c") + newElastPass = inpElastPass.get() + newKibPass =inpKibPass.get() + newKibEnc = inpKibEnc.get("1.0", "end-1c") + + #Updates config values + co["Email"]["EMAIL_TO"] = newTo + co["Email"]["EMAIL_FROM"] = newFrom + co["Email"]["EMAIL_SUBJECT"] = newSubject + co["Email"]["EMAIL_BODY_PREFIX"] = newMessage + co["Email"]["EMAIL_SEND_TIME"] = newSendTime + co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq + dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) + dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) + dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) + dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) + dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) + + #Writes new config values to file + with open("config.ini", "w") as f: + co.write(f) + + #Successful save message + successfulSave = tk.Label(root, text="Saved Successfully!") + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + root.after(2000, successfulSave.destroy) + + def generate_kibana_key(): + key = base64.b64encode(secrets.token_bytes(32)).decode() + inpKibEnc.delete("1.0", "end") + inpKibEnc.insert("1.0", key or "") + + co = configparser.ConfigParser() + co.read("config.ini") + dotenv.load_dotenv() + dotenv_file = ".env" + + root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) + frame = tk.Frame(root) + frame2 = tk.Frame(root) + + root.geometry("") + + #Title + title = tk.Label(root, text="Welcome to VerityBot!") + + #Email_TO + toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) + inpTo = tk.Entry(root, width=50) + + #EMAIL_FROM + fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) + inpFrom = tk.Entry(root, width=50) + + #EMAIL_SUBJECT + subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) + inpSubject = tk.Entry(root, width=50) + + #EMAIL_BODY_PREFIX + messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) + inpMessage = tk.Text(root, height=10, width=38) + + #EMAIL_SEND_TIME + sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) + inpSendTime = tk.Entry(root, width = 7) + + #Email Password + emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) + inpEmailPass = tk.Entry(root, width=50) + + #TIME_DELTA_INSTALLATION + freqLabel1 = tk.Label(root, text="Update Verkada") + freqVar = tk.StringVar() + verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") + freqVar.set(verkadaTimeInstallation) + freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) + freqLabel2 = tk.Label(frame, text="time(s) per day") + + #Verkada API + apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) + inpApiKey = tk.Text(root, height = 3, width=38) + + #Elastic Password + elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) + inpElastPass = tk.Entry(root, width=50) + + #Kibana System Password + kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) + inpKibPass = tk.Entry(root, width=50) + + #Kibana Encryption Key + kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) + inpKibEnc = tk.Text(frame2, height = 2, width = 23) + #Generate Encryption Key Button + encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) + + #Title Insertion + title.grid(row=0, column=0, columnspan=2, pady=10) + + #Email Entries + inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') + inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') + inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') + inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') + inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') + inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') + + #Verkada Entries + freqLabel1.grid(row=7, column=0, padx=5, pady=5) + frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') + freqSpinbox.grid(row=0, column=0) + freqLabel2.grid(row=0, column=1) + + #ENV Entries + inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') + inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') + inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') + frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') + inpKibEnc.grid(row=0, column=0) + encButton.grid(row=0, column=1, padx=5) + + #Getting existing config values + emailTo = co.get("Email", "EMAIL_TO") + emailFrom = co.get("Email", "EMAIL_FROM") + emailSubject = co.get("Email", "EMAIL_SUBJECT") + emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") + emailSendTime = co.get("Email", "EMAIL_SEND_TIME") + emailPass = os.getenv("EMAIL_PASSWORD") + verkAPIKey = os.getenv("VERKADA_API_KEY") + elasticPass = os.getenv("ELASTIC_PASSWORD") + kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") + kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") + + #Inserting existing config values + inpTo.insert(0, emailTo) + inpFrom.insert(0, emailFrom) + inpSubject.insert(0, emailSubject) + inpMessage.insert("1.0", emailMessage) + inpSendTime.insert(0, emailSendTime) + inpEmailPass.insert(0, emailPass or "") + inpApiKey.insert("1.0", verkAPIKey or "") + inpElastPass.insert(0, elasticPass or "") + inpKibPass.insert(0, kibanaPass or "") + inpKibEnc.insert("1.0", kibanaEncKey or "") + + #Save Button + button = tk.Button(root, text="Save", width=20, command=Save_Button) + button.grid(row = 12, column = 0, columnspan = 2, pady = 5) + + root.mainloop() \ No newline at end of file From 219086532e9884b3af70ded753584cacf0fb2e4b Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 02:32:46 -0400 Subject: [PATCH 11/36] Added tkinter to requirements document --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index f10b6f0..c47d4a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ urllib3==2.6.3 pytest pytest-mock elasticsearch +tkinter From 7ef998e88ce1a54c92ae4c2783d24d3df2dd8267 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 15:25:47 -0400 Subject: [PATCH 12/36] Added WSL Functionality Made many changes that allow the dialogue box to run in WSL. It now requires users to have XcSrv to use the visual interface, but if no display is detected, it will run headless in the traditional manner. Most of the changes pertained to setting up the virtual display. --- Dockerfile | 9 +++++++++ compose.yaml | 7 ++++++- requirements.txt | 1 - scripts/initial_setup.sh | 18 +++++++++++++++++- src/Application.py | 14 +++++++++++++- src/ConfigReader.py | 2 +- src/dialogueBox.py | 10 ++++++---- 7 files changed, 52 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 687f357..53c5293 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,15 @@ FROM python:3.11 WORKDIR /veritybot +# Install system dependencies for tkinter +USER root +RUN apt-get update && \ + apt-get install -y python3-tk libffi-dev gcc && \ + rm -rf /var/lib/apt/lists/* + +# Copy config.ini to container +COPY config.ini ./ + # Install the application dependencies COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/compose.yaml b/compose.yaml index 9252782..66cf05f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -61,12 +61,17 @@ services: container_name: etl_json env_file: - .env + environment: + - DISPLAY=host.docker.internal:0.0 + extra_hosts: + - "host.docker.internal:host-gateway" volumes: - ./certs/ca/ca.crt:/certs/ca.crt:ro - ./config.ini:/config.ini + - ./.env:/.env depends_on: elasticsearch: condition: service_healthy volumes: - esdata: \ No newline at end of file + esdata: diff --git a/requirements.txt b/requirements.txt index c47d4a3..f10b6f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ urllib3==2.6.3 pytest pytest-mock elasticsearch -tkinter diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index 65826b2..d9ca223 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -18,6 +18,22 @@ fail() { exit 1 } +# Set DISPLAY for Tkinter GUI through XcXsrv +echo "Configuring display for GUI..." +WINDOWS_IP=$(ip route show default | awk '{print $3}') + +if [ -z "$WINDOWS_IP" ]; then + fail "Could not determine Windows host IP. Are you running in WSL?" +fi + +export DISPLAY=$WINDOWS_IP:0.0 +echo "DISPLAY set to $DISPLAY" + +# Verify XcXsrv is reachable +if ! xset q &>/dev/null; then + fail "Cannot connect to XcXsrv at $DISPLAY. Make sure XcXsrv is running on Windows with 'Disable access control' checked." +fi +echo "X server connection verified." echo "Make sure your .env file already contains the kibana_system password before continuing." echo "Starting initial stack..." @@ -69,4 +85,4 @@ echo "Starting normal secure stack..." docker compose up -d echo -echo "Setup complete." \ No newline at end of file +echo "Setup complete." diff --git a/src/Application.py b/src/Application.py index 8571fa8..84f0363 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,7 +8,19 @@ from ConfigReader import config from datetime import datetime, date -dialogueBox.init() +def is_display_available(): + try: + import tkinter as tk + root = tk.Tk() + root.destroy() + return True + except Exception: + return False + +if is_display_available(): + dialogueBox.init() +else: + print("Running in headless mode (no GUI)") ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 08b3ff1..99633a2 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("config.ini") +config.read("/config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index a8400ea..96a9f55 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -39,7 +39,7 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("config.ini", "w") as f: + with open("/config.ini", "w") as f: co.write(f) #Successful save message @@ -53,9 +53,9 @@ def generate_kibana_key(): inpKibEnc.insert("1.0", key or "") co = configparser.ConfigParser() - co.read("config.ini") + co.read("/config.ini") dotenv.load_dotenv() - dotenv_file = ".env" + dotenv_file = "/.env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -169,4 +169,6 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() \ No newline at end of file + root.mainloop() +if __name__ == "__main__": + init() From de1d3df7ee204164a3fe57610f286515a3c79edb Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 15:41:58 -0400 Subject: [PATCH 13/36] Fixed Save Button Confirmation Placement --- src/dialogueBox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 96a9f55..24dcafa 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -44,7 +44,7 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): From 1666175caef1239e353b9d444c70e457184ea9a7 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 17:30:10 -0400 Subject: [PATCH 14/36] Revert "Fixed Save Button Confirmation Placement" This reverts commit de1d3df7ee204164a3fe57610f286515a3c79edb. --- src/dialogueBox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 24dcafa..96a9f55 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -44,7 +44,7 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): From e77537fab9d609597549edb36a6566e9c126a258 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 18:06:24 -0400 Subject: [PATCH 15/36] Revert "Added tkinter to requirements document" This reverts commit 219086532e9884b3af70ded753584cacf0fb2e4b. This commit also makes it so that tkinter can run in base WSL, outsid of Docker. --- Dockerfile | 9 --------- compose.yaml | 7 +------ scripts/initial_setup.sh | 18 +----------------- src/Application.py | 14 +------------- src/ConfigReader.py | 2 +- src/dialogueBox.py | 10 ++++------ 6 files changed, 8 insertions(+), 52 deletions(-) diff --git a/Dockerfile b/Dockerfile index 53c5293..687f357 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,6 @@ FROM python:3.11 WORKDIR /veritybot -# Install system dependencies for tkinter -USER root -RUN apt-get update && \ - apt-get install -y python3-tk libffi-dev gcc && \ - rm -rf /var/lib/apt/lists/* - -# Copy config.ini to container -COPY config.ini ./ - # Install the application dependencies COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/compose.yaml b/compose.yaml index 66cf05f..9252782 100644 --- a/compose.yaml +++ b/compose.yaml @@ -61,17 +61,12 @@ services: container_name: etl_json env_file: - .env - environment: - - DISPLAY=host.docker.internal:0.0 - extra_hosts: - - "host.docker.internal:host-gateway" volumes: - ./certs/ca/ca.crt:/certs/ca.crt:ro - ./config.ini:/config.ini - - ./.env:/.env depends_on: elasticsearch: condition: service_healthy volumes: - esdata: + esdata: \ No newline at end of file diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index d9ca223..65826b2 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -18,22 +18,6 @@ fail() { exit 1 } -# Set DISPLAY for Tkinter GUI through XcXsrv -echo "Configuring display for GUI..." -WINDOWS_IP=$(ip route show default | awk '{print $3}') - -if [ -z "$WINDOWS_IP" ]; then - fail "Could not determine Windows host IP. Are you running in WSL?" -fi - -export DISPLAY=$WINDOWS_IP:0.0 -echo "DISPLAY set to $DISPLAY" - -# Verify XcXsrv is reachable -if ! xset q &>/dev/null; then - fail "Cannot connect to XcXsrv at $DISPLAY. Make sure XcXsrv is running on Windows with 'Disable access control' checked." -fi -echo "X server connection verified." echo "Make sure your .env file already contains the kibana_system password before continuing." echo "Starting initial stack..." @@ -85,4 +69,4 @@ echo "Starting normal secure stack..." docker compose up -d echo -echo "Setup complete." +echo "Setup complete." \ No newline at end of file diff --git a/src/Application.py b/src/Application.py index 84f0363..8571fa8 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,19 +8,7 @@ from ConfigReader import config from datetime import datetime, date -def is_display_available(): - try: - import tkinter as tk - root = tk.Tk() - root.destroy() - return True - except Exception: - return False - -if is_display_available(): - dialogueBox.init() -else: - print("Running in headless mode (no GUI)") +dialogueBox.init() ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 99633a2..08b3ff1 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("/config.ini") +config.read("config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 96a9f55..a8400ea 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -39,7 +39,7 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("/config.ini", "w") as f: + with open("config.ini", "w") as f: co.write(f) #Successful save message @@ -53,9 +53,9 @@ def generate_kibana_key(): inpKibEnc.insert("1.0", key or "") co = configparser.ConfigParser() - co.read("/config.ini") + co.read("config.ini") dotenv.load_dotenv() - dotenv_file = "/.env" + dotenv_file = ".env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -169,6 +169,4 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() -if __name__ == "__main__": - init() + root.mainloop() \ No newline at end of file From 4236799e185db54c16c7cf4b58cf6f285b58893b Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 18:09:07 -0400 Subject: [PATCH 16/36] Fixed WSL capabilities Dialogue box will now run in base WSL. --- src/ConfigReader.py | 2 +- src/dialogueBox.py | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 08b3ff1..99633a2 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("config.ini") +config.read("/config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index a8400ea..11ba5a7 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -12,6 +12,8 @@ def init(): def Save_Button(): + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + #Get new values from the user entries newTo = inpTo.get() newFrom = inpFrom.get() @@ -39,21 +41,23 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("config.ini", "w") as f: + with open(os.path.join(BASE_DIR, '..', 'config.ini'), "w") as f: co.write(f) #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): key = base64.b64encode(secrets.token_bytes(32)).decode() inpKibEnc.delete("1.0", "end") inpKibEnc.insert("1.0", key or "") - + + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + co = configparser.ConfigParser() - co.read("config.ini") + co.read(os.path.join(BASE_DIR, '..', 'config.ini')) dotenv.load_dotenv() dotenv_file = ".env" @@ -169,4 +173,7 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() \ No newline at end of file + root.mainloop() + +if __name__ == "__main__": + init() From 0fe12d4b3d7664d9171e64fb7543317f448d242c Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 18:20:44 -0400 Subject: [PATCH 17/36] Updated Elastic Update Interval Entry System --- src/dialogueBox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 11ba5a7..31b4af6 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -33,7 +33,7 @@ def Save_Button(): co["Email"]["EMAIL_SUBJECT"] = newSubject co["Email"]["EMAIL_BODY_PREFIX"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq + co["Verkada"]["ELASTIC_UPDATE_INTERVAL"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) @@ -94,10 +94,10 @@ def generate_kibana_key(): emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) inpEmailPass = tk.Entry(root, width=50) - #TIME_DELTA_INSTALLATION + #ELASTIC_UPDATE_INTERVAL freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() - verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") + verkadaTimeInstallation = co.get("Verkada", "ELASTIC_UPDATE_INTERVAL") freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) freqLabel2 = tk.Label(frame, text="time(s) per day") From d706a6b4f324056f7e42886793c796195f93f29b Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 19:18:03 -0400 Subject: [PATCH 18/36] Minor Fix --- src/Application.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Application.py b/src/Application.py index 8571fa8..981d741 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,8 +8,6 @@ from ConfigReader import config from datetime import datetime, date -dialogueBox.init() - ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: print("ERROR: ELASTIC_PASSWORD has not been set, exiting...") From 023bf2ea9bde3a5dbaf72ca5414394d9e34c68d0 Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 19:18:39 -0400 Subject: [PATCH 19/36] Removed Redundant import statement --- src/Application.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Application.py b/src/Application.py index 981d741..3609ff6 100644 --- a/src/Application.py +++ b/src/Application.py @@ -2,7 +2,6 @@ import CLI import ElasticSearch import os -import dialogueBox from Verkada import VerkadaContext from ConfigReader import config From e5af31b4c1bddfe437e42d1ce6ce9b9df87533c2 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Sun, 12 Apr 2026 18:20:12 -0400 Subject: [PATCH 20/36] Updated Dialogue Box Labeling for Verkada Pull Frequency --- src/dialogueBox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 31b4af6..bbf3b40 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -95,12 +95,12 @@ def generate_kibana_key(): inpEmailPass = tk.Entry(root, width=50) #ELASTIC_UPDATE_INTERVAL - freqLabel1 = tk.Label(root, text="Update Verkada") + freqLabel1 = tk.Label(root, text="Update Verkada every") freqVar = tk.StringVar() verkadaTimeInstallation = co.get("Verkada", "ELASTIC_UPDATE_INTERVAL") freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) - freqLabel2 = tk.Label(frame, text="time(s) per day") + freqLabel2 = tk.Label(frame, text="minute(s)") #Verkada API apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) From 7d2ffba177cb52766563606cb57794fea8af72fe Mon Sep 17 00:00:00 2001 From: Julian V Date: Mon, 30 Mar 2026 18:39:49 -0400 Subject: [PATCH 21/36] Added .env values to the Dialogue Box Added more field for the user to input to make all unique values easy to change through the dialogue box. --- src/dialogueBox.py | 74 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index c333200..3ee3aa9 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -1,5 +1,8 @@ +import os import tkinter as tk import configparser +import dotenv + def Save_Button(): @@ -10,6 +13,11 @@ def Save_Button(): newMessage = inpMessage.get("1.0", "end-1c") newSendTime = inpSendTime.get() newFreq = freqVar.get() + newEmailPass = inpEmailPass.get() + newVerkAPI = inpApiKey.get("1.0", "end-1c") + newElastPass = inpElastPass.get() + newKibPass =inpKibPass.get() + newKibEnc = inpKibEnc.get("1.0", "end-1c") #Updates config values co["Email"]["EMAIL_TO"] = newTo @@ -18,6 +26,11 @@ def Save_Button(): co["Email"]["EMAIL_MESSAGE"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) + dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) + dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) + dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) + dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file with open("config.ini", "w") as f: @@ -25,11 +38,13 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 11, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) co = configparser.ConfigParser() co.read("config.ini") +dotenv.load_dotenv() +dotenv_file = ".env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -40,7 +55,7 @@ def Save_Button(): title = tk.Label(root, text="Welcome to VerityBot!") #Email_TO -toLabel = tk.Label(root, text="Email Receiver").grid(row=1, column=0) +toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) inpTo = tk.Entry(root, width=50) #EMAIL_FROM @@ -53,20 +68,40 @@ def Save_Button(): #EMAIL_MESSAGE messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) -inpMessage = tk.Text(root, height=10, width=50) +inpMessage = tk.Text(root, height=10, width=38) #EMAIL_SEND_TIME -sendTimeLabel = tk.Label(root, text="Email Send Time").grid(row=5, column=0) -inpSendTime = tk.Entry(root) +sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) +inpSendTime = tk.Entry(root, width = 7) +#Email Password +emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) +inpEmailPass = tk.Entry(root, width=50) #TIME_DELTA_RECURRING -freqLabel1 = tk.Label(root, text="Update Verkada data every") +freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") freqVar.set(verkadaTimeRecurring) -freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60) -freqLabel2 = tk.Label(frame, text="day(s)") +freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) +freqLabel2 = tk.Label(frame, text="time(s) per day") + +#Verkada API +apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) +inpApiKey = tk.Text(root, height = 3, width=38) + +#Elastic Password +elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) +inpElastPass = tk.Entry(root, width=50) + +#Kibana System Password +kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) +inpKibPass = tk.Entry(root, width=50) + +#Kibana Encryption Key +kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) +#Add functionality +inpKibEnc = tk.Text(root, height = 2, width = 38) #Title Insertion title.grid(row=0, column=0, columnspan=2, pady=10) @@ -77,19 +112,31 @@ def Save_Button(): inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') +inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') #Verkada Entries -freqLabel1.grid(row=6, column=0, padx=5, pady=5) -frame.grid(row=6, column=1, padx=5, pady=5, sticky='w') +freqLabel1.grid(row=7, column=0, padx=5, pady=5) +frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') freqSpinbox.grid(row=0, column=0) freqLabel2.grid(row=0, column=1) +#ENV Entries +inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') +inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') +inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') +inpKibEnc.grid(row=11, column=1, padx=5, pady=5, sticky='w') + #Getting existing config values emailTo = co.get("Email", "EMAIL_TO") emailFrom = co.get("Email", "EMAIL_FROM") emailSubject = co.get("Email", "EMAIL_SUBJECT") emailMessage = co.get("Email", "EMAIL_MESSAGE") emailSendTime = co.get("Email", "EMAIL_SEND_TIME") +emailPass = os.getenv("EMAIL_PASSWORD") +verkAPIKey = os.getenv("VERKADA_API_KEY") +elasticPass = os.getenv("ELASTIC_PASSWORD") +kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") +kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") #Inserting existing config values inpTo.insert(0, emailTo) @@ -97,9 +144,14 @@ def Save_Button(): inpSubject.insert(0, emailSubject) inpMessage.insert("1.0", emailMessage) inpSendTime.insert(0, emailSendTime) +inpEmailPass.insert(0, emailPass or "") +inpApiKey.insert("1.0", verkAPIKey or "") +inpElastPass.insert(0, elasticPass or "") +inpKibPass.insert(0, kibanaPass or "") +inpKibEnc.insert("1.0", kibanaEncKey or "") #Save Button button = tk.Button(root, text="Save", width=25, command=Save_Button) -button.grid(row = 7, column = 0, columnspan = 2, pady = 5) +button.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.mainloop() \ No newline at end of file From 34a6fb6734d4f755a840b1302724bfbaf4375154 Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 1 Apr 2026 15:29:48 -0400 Subject: [PATCH 22/36] Updated Dialogue Box Values Updated the variable names for many of the .env and config edits. --- src/dialogueBox.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 3ee3aa9..56fa29c 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -25,7 +25,7 @@ def Save_Button(): co["Email"]["EMAIL_SUBJECT"] = newSubject co["Email"]["EMAIL_MESSAGE"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_RECURRING"] = newFreq + co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) @@ -78,11 +78,11 @@ def Save_Button(): emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) inpEmailPass = tk.Entry(root, width=50) -#TIME_DELTA_RECURRING +#TIME_DELTA_INSTALLATION freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() -verkadaTimeRecurring = co.get("Verkada", "TIME_DELTA_RECURRING") -freqVar.set(verkadaTimeRecurring) +verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") +freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) freqLabel2 = tk.Label(frame, text="time(s) per day") From d2d703dc6309a0934c1bf7ec17a47fc0f1b42dd3 Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 13:50:49 -0400 Subject: [PATCH 23/36] Added Variable Email Message Capabilities to DB --- config.ini.example | 1 + src/dialogueBox.py | 6 +++--- src/email_sender.py | 15 +++++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/config.ini.example b/config.ini.example index 8dcb3b8..8fbb144 100644 --- a/config.ini.example +++ b/config.ini.example @@ -1,6 +1,7 @@ [Email] EMAIL_TO=receiver_email@example.com EMAIL_FROM=your_email@example.com +EMAIL_BODY_PREFIX=Click the link below to access the Kibana dashboard: EMAIL_SUBJECT=Subject for the Email EMAIL_SEND_TIME=09:00 diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 56fa29c..0970993 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -23,7 +23,7 @@ def Save_Button(): co["Email"]["EMAIL_TO"] = newTo co["Email"]["EMAIL_FROM"] = newFrom co["Email"]["EMAIL_SUBJECT"] = newSubject - co["Email"]["EMAIL_MESSAGE"] = newMessage + co["Email"]["EMAIL_BODY_PREFIX"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) @@ -66,7 +66,7 @@ def Save_Button(): subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) inpSubject = tk.Entry(root, width=50) -#EMAIL_MESSAGE +#EMAIL_BODY_PREFIX messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) inpMessage = tk.Text(root, height=10, width=38) @@ -130,7 +130,7 @@ def Save_Button(): emailTo = co.get("Email", "EMAIL_TO") emailFrom = co.get("Email", "EMAIL_FROM") emailSubject = co.get("Email", "EMAIL_SUBJECT") -emailMessage = co.get("Email", "EMAIL_MESSAGE") +emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") emailSendTime = co.get("Email", "EMAIL_SEND_TIME") emailPass = os.getenv("EMAIL_PASSWORD") verkAPIKey = os.getenv("VERKADA_API_KEY") diff --git a/src/email_sender.py b/src/email_sender.py index 7810316..47f2df3 100644 --- a/src/email_sender.py +++ b/src/email_sender.py @@ -45,14 +45,21 @@ def get_ip(): return IP IP = get_ip() + +# read prefix only from config (no env fallback) +body_prefix = config['Email'].get( + 'EMAIL_BODY_PREFIX', 'Click the link below to access the Kibana dashboard:' +) + html = f""" - -

Click the link below to access the Kibana dashboard:

- Kibana Dashboard - + +

{body_prefix}

+ Kibana Dashboard + """ + message.attach(MIMEText(html, "html")) load_dotenv() From 1e20743bf32920cfd92067a7ddefbbb3cc10da94 Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 20:50:07 -0400 Subject: [PATCH 24/36] Removed "/" to make the config parser find ini in the same folder the example is. --- src/ConfigReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 99633a2..08b3ff1 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("/config.ini") +config.read("config.ini") From b7a91aa467c4a853abdd83b9f57e0aea1f50a6f0 Mon Sep 17 00:00:00 2001 From: Julian V Date: Wed, 8 Apr 2026 21:41:34 -0400 Subject: [PATCH 25/36] Added Kibana Encryption Key Generator Button Added a button that allows the user to generate a unique Kibana Encryption key without entering the terminal. --- src/dialogueBox.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 0970993..f46297d 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -2,6 +2,8 @@ import tkinter as tk import configparser import dotenv +import secrets +import base64 def Save_Button(): @@ -38,9 +40,15 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) +def generate_kibana_key(): + key = base64.b64encode(secrets.token_bytes(32)).decode() + inpKibEnc.delete("1.0", "end") + inpKibEnc.insert("1.0", key or "") + + co = configparser.ConfigParser() co.read("config.ini") dotenv.load_dotenv() @@ -48,6 +56,7 @@ def Save_Button(): root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) +frame2 = tk.Frame(root) root.geometry("") @@ -100,8 +109,11 @@ def Save_Button(): #Kibana Encryption Key kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) -#Add functionality -inpKibEnc = tk.Text(root, height = 2, width = 38) +inpKibEnc = tk.Text(frame2, height = 2, width = 23) +#Generate Encryption Key Button +encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) + + #Title Insertion title.grid(row=0, column=0, columnspan=2, pady=10) @@ -124,7 +136,9 @@ def Save_Button(): inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') -inpKibEnc.grid(row=11, column=1, padx=5, pady=5, sticky='w') +frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') +inpKibEnc.grid(row=0, column=0) +encButton.grid(row=0, column=1, padx=5) #Getting existing config values emailTo = co.get("Email", "EMAIL_TO") @@ -151,7 +165,7 @@ def Save_Button(): inpKibEnc.insert("1.0", kibanaEncKey or "") #Save Button -button = tk.Button(root, text="Save", width=25, command=Save_Button) +button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.mainloop() \ No newline at end of file From b0a0cc9df19e72b37b9ad7f0664694be871705fc Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 02:26:40 -0400 Subject: [PATCH 26/36] Made Dialogue Box pop up when program is run When the program begins running it will now run the dialogue box initialization function. --- src/Application.py | 3 + src/dialogueBox.py | 327 +++++++++++++++++++++++---------------------- 2 files changed, 167 insertions(+), 163 deletions(-) diff --git a/src/Application.py b/src/Application.py index 3609ff6..8571fa8 100644 --- a/src/Application.py +++ b/src/Application.py @@ -2,11 +2,14 @@ import CLI import ElasticSearch import os +import dialogueBox from Verkada import VerkadaContext from ConfigReader import config from datetime import datetime, date +dialogueBox.init() + ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: print("ERROR: ELASTIC_PASSWORD has not been set, exiting...") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index f46297d..a8400ea 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -6,166 +6,167 @@ import base64 -def Save_Button(): - - #Get new values from the user entries - newTo = inpTo.get() - newFrom = inpFrom.get() - newSubject = inpSubject.get() - newMessage = inpMessage.get("1.0", "end-1c") - newSendTime = inpSendTime.get() - newFreq = freqVar.get() - newEmailPass = inpEmailPass.get() - newVerkAPI = inpApiKey.get("1.0", "end-1c") - newElastPass = inpElastPass.get() - newKibPass =inpKibPass.get() - newKibEnc = inpKibEnc.get("1.0", "end-1c") - - #Updates config values - co["Email"]["EMAIL_TO"] = newTo - co["Email"]["EMAIL_FROM"] = newFrom - co["Email"]["EMAIL_SUBJECT"] = newSubject - co["Email"]["EMAIL_BODY_PREFIX"] = newMessage - co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq - dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) - dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) - dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) - dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) - dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) - - #Writes new config values to file - with open("config.ini", "w") as f: - co.write(f) - - #Successful save message - successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) - root.after(2000, successfulSave.destroy) - -def generate_kibana_key(): - key = base64.b64encode(secrets.token_bytes(32)).decode() - inpKibEnc.delete("1.0", "end") - inpKibEnc.insert("1.0", key or "") - - -co = configparser.ConfigParser() -co.read("config.ini") -dotenv.load_dotenv() -dotenv_file = ".env" - -root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) -frame = tk.Frame(root) -frame2 = tk.Frame(root) - -root.geometry("") - -#Title -title = tk.Label(root, text="Welcome to VerityBot!") - -#Email_TO -toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) -inpTo = tk.Entry(root, width=50) - -#EMAIL_FROM -fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) -inpFrom = tk.Entry(root, width=50) - -#EMAIL_SUBJECT -subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) -inpSubject = tk.Entry(root, width=50) - -#EMAIL_BODY_PREFIX -messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) -inpMessage = tk.Text(root, height=10, width=38) - -#EMAIL_SEND_TIME -sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) -inpSendTime = tk.Entry(root, width = 7) - -#Email Password -emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) -inpEmailPass = tk.Entry(root, width=50) - -#TIME_DELTA_INSTALLATION -freqLabel1 = tk.Label(root, text="Update Verkada") -freqVar = tk.StringVar() -verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") -freqVar.set(verkadaTimeInstallation) -freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) -freqLabel2 = tk.Label(frame, text="time(s) per day") - -#Verkada API -apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) -inpApiKey = tk.Text(root, height = 3, width=38) - -#Elastic Password -elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) -inpElastPass = tk.Entry(root, width=50) - -#Kibana System Password -kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) -inpKibPass = tk.Entry(root, width=50) - -#Kibana Encryption Key -kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) -inpKibEnc = tk.Text(frame2, height = 2, width = 23) -#Generate Encryption Key Button -encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) - - - -#Title Insertion -title.grid(row=0, column=0, columnspan=2, pady=10) - -#Email Entries -inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') -inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') -inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') -inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') -inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') -inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') - -#Verkada Entries -freqLabel1.grid(row=7, column=0, padx=5, pady=5) -frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') -freqSpinbox.grid(row=0, column=0) -freqLabel2.grid(row=0, column=1) - -#ENV Entries -inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') -inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') -inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') -frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') -inpKibEnc.grid(row=0, column=0) -encButton.grid(row=0, column=1, padx=5) - -#Getting existing config values -emailTo = co.get("Email", "EMAIL_TO") -emailFrom = co.get("Email", "EMAIL_FROM") -emailSubject = co.get("Email", "EMAIL_SUBJECT") -emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") -emailSendTime = co.get("Email", "EMAIL_SEND_TIME") -emailPass = os.getenv("EMAIL_PASSWORD") -verkAPIKey = os.getenv("VERKADA_API_KEY") -elasticPass = os.getenv("ELASTIC_PASSWORD") -kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") -kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") - -#Inserting existing config values -inpTo.insert(0, emailTo) -inpFrom.insert(0, emailFrom) -inpSubject.insert(0, emailSubject) -inpMessage.insert("1.0", emailMessage) -inpSendTime.insert(0, emailSendTime) -inpEmailPass.insert(0, emailPass or "") -inpApiKey.insert("1.0", verkAPIKey or "") -inpElastPass.insert(0, elasticPass or "") -inpKibPass.insert(0, kibanaPass or "") -inpKibEnc.insert("1.0", kibanaEncKey or "") - -#Save Button -button = tk.Button(root, text="Save", width=20, command=Save_Button) -button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - -root.mainloop() \ No newline at end of file + + +def init(): + + def Save_Button(): + + #Get new values from the user entries + newTo = inpTo.get() + newFrom = inpFrom.get() + newSubject = inpSubject.get() + newMessage = inpMessage.get("1.0", "end-1c") + newSendTime = inpSendTime.get() + newFreq = freqVar.get() + newEmailPass = inpEmailPass.get() + newVerkAPI = inpApiKey.get("1.0", "end-1c") + newElastPass = inpElastPass.get() + newKibPass =inpKibPass.get() + newKibEnc = inpKibEnc.get("1.0", "end-1c") + + #Updates config values + co["Email"]["EMAIL_TO"] = newTo + co["Email"]["EMAIL_FROM"] = newFrom + co["Email"]["EMAIL_SUBJECT"] = newSubject + co["Email"]["EMAIL_BODY_PREFIX"] = newMessage + co["Email"]["EMAIL_SEND_TIME"] = newSendTime + co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq + dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) + dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) + dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) + dotenv.set_key(dotenv_file,"KIBANA_SYSTEM_PASSWORD", newKibPass) + dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) + + #Writes new config values to file + with open("config.ini", "w") as f: + co.write(f) + + #Successful save message + successfulSave = tk.Label(root, text="Saved Successfully!") + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + root.after(2000, successfulSave.destroy) + + def generate_kibana_key(): + key = base64.b64encode(secrets.token_bytes(32)).decode() + inpKibEnc.delete("1.0", "end") + inpKibEnc.insert("1.0", key or "") + + co = configparser.ConfigParser() + co.read("config.ini") + dotenv.load_dotenv() + dotenv_file = ".env" + + root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) + frame = tk.Frame(root) + frame2 = tk.Frame(root) + + root.geometry("") + + #Title + title = tk.Label(root, text="Welcome to VerityBot!") + + #Email_TO + toLabel = tk.Label(root, text="Email Destination").grid(row=1, column=0) + inpTo = tk.Entry(root, width=50) + + #EMAIL_FROM + fromLabel = tk.Label(root, text="Email Sender").grid(row=2, column=0) + inpFrom = tk.Entry(root, width=50) + + #EMAIL_SUBJECT + subLabel = tk.Label(root, text="Email Subject").grid(row=3, column=0) + inpSubject = tk.Entry(root, width=50) + + #EMAIL_BODY_PREFIX + messageLabel = tk.Label(root, text="Email Message").grid(row=4, column=0) + inpMessage = tk.Text(root, height=10, width=38) + + #EMAIL_SEND_TIME + sendTimeLabel = tk.Label(root, text="Email Send Time (24HR)").grid(row=5, column=0) + inpSendTime = tk.Entry(root, width = 7) + + #Email Password + emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) + inpEmailPass = tk.Entry(root, width=50) + + #TIME_DELTA_INSTALLATION + freqLabel1 = tk.Label(root, text="Update Verkada") + freqVar = tk.StringVar() + verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") + freqVar.set(verkadaTimeInstallation) + freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) + freqLabel2 = tk.Label(frame, text="time(s) per day") + + #Verkada API + apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0) + inpApiKey = tk.Text(root, height = 3, width=38) + + #Elastic Password + elastPassLabel = tk.Label(root, text="Elastic Password").grid(row=9, column=0) + inpElastPass = tk.Entry(root, width=50) + + #Kibana System Password + kibPassLabel = tk.Label(root, text="Kibana Password").grid(row=10, column=0) + inpKibPass = tk.Entry(root, width=50) + + #Kibana Encryption Key + kibEncLabel = tk.Label(root, text="Kibana Encryption Key").grid(row=11, column=0) + inpKibEnc = tk.Text(frame2, height = 2, width = 23) + #Generate Encryption Key Button + encButton = tk.Button(frame2, text="Generate Key", width=14, command=generate_kibana_key) + + #Title Insertion + title.grid(row=0, column=0, columnspan=2, pady=10) + + #Email Entries + inpTo.grid(row=1, column=1, padx=5, pady=5, sticky='w') + inpFrom.grid(row=2, column=1, padx=5, pady=5, sticky='w') + inpSubject.grid(row=3, column=1, padx=5, pady=5, sticky='w') + inpMessage.grid(row=4, column=1, padx=(5,15), pady=5, sticky='w') + inpSendTime.grid(row=5, column=1, padx=5, pady=5, sticky='w') + inpEmailPass.grid(row=6, column=1, padx=5, pady=5, sticky='w') + + #Verkada Entries + freqLabel1.grid(row=7, column=0, padx=5, pady=5) + frame.grid(row=7, column=1, padx=5, pady=5, sticky='w') + freqSpinbox.grid(row=0, column=0) + freqLabel2.grid(row=0, column=1) + + #ENV Entries + inpApiKey.grid(row=8, column=1, padx=5, pady=5, sticky='w') + inpElastPass.grid(row=9, column=1, padx=5, pady=5, sticky='w') + inpKibPass.grid(row=10, column=1, padx=5, pady=5, sticky='w') + frame2.grid(row=11, column=1, padx=5, pady=5, sticky='w') + inpKibEnc.grid(row=0, column=0) + encButton.grid(row=0, column=1, padx=5) + + #Getting existing config values + emailTo = co.get("Email", "EMAIL_TO") + emailFrom = co.get("Email", "EMAIL_FROM") + emailSubject = co.get("Email", "EMAIL_SUBJECT") + emailMessage = co.get("Email", "EMAIL_BODY_PREFIX") + emailSendTime = co.get("Email", "EMAIL_SEND_TIME") + emailPass = os.getenv("EMAIL_PASSWORD") + verkAPIKey = os.getenv("VERKADA_API_KEY") + elasticPass = os.getenv("ELASTIC_PASSWORD") + kibanaPass = os.getenv("KIBANA_SYSTEM_PASSWORD") + kibanaEncKey = os.getenv("KIBANA_ENCRYPTION_KEY") + + #Inserting existing config values + inpTo.insert(0, emailTo) + inpFrom.insert(0, emailFrom) + inpSubject.insert(0, emailSubject) + inpMessage.insert("1.0", emailMessage) + inpSendTime.insert(0, emailSendTime) + inpEmailPass.insert(0, emailPass or "") + inpApiKey.insert("1.0", verkAPIKey or "") + inpElastPass.insert(0, elasticPass or "") + inpKibPass.insert(0, kibanaPass or "") + inpKibEnc.insert("1.0", kibanaEncKey or "") + + #Save Button + button = tk.Button(root, text="Save", width=20, command=Save_Button) + button.grid(row = 12, column = 0, columnspan = 2, pady = 5) + + root.mainloop() \ No newline at end of file From da13eb07ceac8c3450f882f7e3aa050f97fde2d8 Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 02:32:46 -0400 Subject: [PATCH 27/36] Added tkinter to requirements document --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index f10b6f0..c47d4a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ urllib3==2.6.3 pytest pytest-mock elasticsearch +tkinter From d0fe961b629cc60d696f79cf581b9a4668e9ea9f Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 15:25:47 -0400 Subject: [PATCH 28/36] Added WSL Functionality Made many changes that allow the dialogue box to run in WSL. It now requires users to have XcSrv to use the visual interface, but if no display is detected, it will run headless in the traditional manner. Most of the changes pertained to setting up the virtual display. --- Dockerfile | 9 +++++++++ compose.yaml | 7 ++++++- requirements.txt | 1 - scripts/initial_setup.sh | 18 +++++++++++++++++- src/Application.py | 14 +++++++++++++- src/ConfigReader.py | 2 +- src/dialogueBox.py | 10 ++++++---- 7 files changed, 52 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 687f357..53c5293 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,15 @@ FROM python:3.11 WORKDIR /veritybot +# Install system dependencies for tkinter +USER root +RUN apt-get update && \ + apt-get install -y python3-tk libffi-dev gcc && \ + rm -rf /var/lib/apt/lists/* + +# Copy config.ini to container +COPY config.ini ./ + # Install the application dependencies COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/compose.yaml b/compose.yaml index 9252782..66cf05f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -61,12 +61,17 @@ services: container_name: etl_json env_file: - .env + environment: + - DISPLAY=host.docker.internal:0.0 + extra_hosts: + - "host.docker.internal:host-gateway" volumes: - ./certs/ca/ca.crt:/certs/ca.crt:ro - ./config.ini:/config.ini + - ./.env:/.env depends_on: elasticsearch: condition: service_healthy volumes: - esdata: \ No newline at end of file + esdata: diff --git a/requirements.txt b/requirements.txt index c47d4a3..f10b6f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ urllib3==2.6.3 pytest pytest-mock elasticsearch -tkinter diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index 65826b2..d9ca223 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -18,6 +18,22 @@ fail() { exit 1 } +# Set DISPLAY for Tkinter GUI through XcXsrv +echo "Configuring display for GUI..." +WINDOWS_IP=$(ip route show default | awk '{print $3}') + +if [ -z "$WINDOWS_IP" ]; then + fail "Could not determine Windows host IP. Are you running in WSL?" +fi + +export DISPLAY=$WINDOWS_IP:0.0 +echo "DISPLAY set to $DISPLAY" + +# Verify XcXsrv is reachable +if ! xset q &>/dev/null; then + fail "Cannot connect to XcXsrv at $DISPLAY. Make sure XcXsrv is running on Windows with 'Disable access control' checked." +fi +echo "X server connection verified." echo "Make sure your .env file already contains the kibana_system password before continuing." echo "Starting initial stack..." @@ -69,4 +85,4 @@ echo "Starting normal secure stack..." docker compose up -d echo -echo "Setup complete." \ No newline at end of file +echo "Setup complete." diff --git a/src/Application.py b/src/Application.py index 8571fa8..84f0363 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,7 +8,19 @@ from ConfigReader import config from datetime import datetime, date -dialogueBox.init() +def is_display_available(): + try: + import tkinter as tk + root = tk.Tk() + root.destroy() + return True + except Exception: + return False + +if is_display_available(): + dialogueBox.init() +else: + print("Running in headless mode (no GUI)") ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 08b3ff1..99633a2 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("config.ini") +config.read("/config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index a8400ea..96a9f55 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -39,7 +39,7 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("config.ini", "w") as f: + with open("/config.ini", "w") as f: co.write(f) #Successful save message @@ -53,9 +53,9 @@ def generate_kibana_key(): inpKibEnc.insert("1.0", key or "") co = configparser.ConfigParser() - co.read("config.ini") + co.read("/config.ini") dotenv.load_dotenv() - dotenv_file = ".env" + dotenv_file = "/.env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -169,4 +169,6 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() \ No newline at end of file + root.mainloop() +if __name__ == "__main__": + init() From c5e2fb1638464744dcc383b00ea688295432c3df Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 15:41:58 -0400 Subject: [PATCH 29/36] Fixed Save Button Confirmation Placement --- src/dialogueBox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 96a9f55..24dcafa 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -44,7 +44,7 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): From cdc104b861874f7c1ad4d97e2b223f51a875b8bc Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 17:30:10 -0400 Subject: [PATCH 30/36] Revert "Fixed Save Button Confirmation Placement" This reverts commit de1d3df7ee204164a3fe57610f286515a3c79edb. --- src/dialogueBox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 24dcafa..96a9f55 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -44,7 +44,7 @@ def Save_Button(): #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): From 5d87a628b45cffac243dbc09fee9042c9651ac2f Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 18:06:24 -0400 Subject: [PATCH 31/36] Revert "Added tkinter to requirements document" This reverts commit 219086532e9884b3af70ded753584cacf0fb2e4b. This commit also makes it so that tkinter can run in base WSL, outsid of Docker. --- Dockerfile | 9 --------- compose.yaml | 7 +------ scripts/initial_setup.sh | 18 +----------------- src/Application.py | 14 +------------- src/ConfigReader.py | 2 +- src/dialogueBox.py | 10 ++++------ 6 files changed, 8 insertions(+), 52 deletions(-) diff --git a/Dockerfile b/Dockerfile index 53c5293..687f357 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,6 @@ FROM python:3.11 WORKDIR /veritybot -# Install system dependencies for tkinter -USER root -RUN apt-get update && \ - apt-get install -y python3-tk libffi-dev gcc && \ - rm -rf /var/lib/apt/lists/* - -# Copy config.ini to container -COPY config.ini ./ - # Install the application dependencies COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt diff --git a/compose.yaml b/compose.yaml index 66cf05f..9252782 100644 --- a/compose.yaml +++ b/compose.yaml @@ -61,17 +61,12 @@ services: container_name: etl_json env_file: - .env - environment: - - DISPLAY=host.docker.internal:0.0 - extra_hosts: - - "host.docker.internal:host-gateway" volumes: - ./certs/ca/ca.crt:/certs/ca.crt:ro - ./config.ini:/config.ini - - ./.env:/.env depends_on: elasticsearch: condition: service_healthy volumes: - esdata: + esdata: \ No newline at end of file diff --git a/scripts/initial_setup.sh b/scripts/initial_setup.sh index d9ca223..65826b2 100755 --- a/scripts/initial_setup.sh +++ b/scripts/initial_setup.sh @@ -18,22 +18,6 @@ fail() { exit 1 } -# Set DISPLAY for Tkinter GUI through XcXsrv -echo "Configuring display for GUI..." -WINDOWS_IP=$(ip route show default | awk '{print $3}') - -if [ -z "$WINDOWS_IP" ]; then - fail "Could not determine Windows host IP. Are you running in WSL?" -fi - -export DISPLAY=$WINDOWS_IP:0.0 -echo "DISPLAY set to $DISPLAY" - -# Verify XcXsrv is reachable -if ! xset q &>/dev/null; then - fail "Cannot connect to XcXsrv at $DISPLAY. Make sure XcXsrv is running on Windows with 'Disable access control' checked." -fi -echo "X server connection verified." echo "Make sure your .env file already contains the kibana_system password before continuing." echo "Starting initial stack..." @@ -85,4 +69,4 @@ echo "Starting normal secure stack..." docker compose up -d echo -echo "Setup complete." +echo "Setup complete." \ No newline at end of file diff --git a/src/Application.py b/src/Application.py index 84f0363..8571fa8 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,19 +8,7 @@ from ConfigReader import config from datetime import datetime, date -def is_display_available(): - try: - import tkinter as tk - root = tk.Tk() - root.destroy() - return True - except Exception: - return False - -if is_display_available(): - dialogueBox.init() -else: - print("Running in headless mode (no GUI)") +dialogueBox.init() ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 99633a2..08b3ff1 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("/config.ini") +config.read("config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 96a9f55..a8400ea 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -39,7 +39,7 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("/config.ini", "w") as f: + with open("config.ini", "w") as f: co.write(f) #Successful save message @@ -53,9 +53,9 @@ def generate_kibana_key(): inpKibEnc.insert("1.0", key or "") co = configparser.ConfigParser() - co.read("/config.ini") + co.read("config.ini") dotenv.load_dotenv() - dotenv_file = "/.env" + dotenv_file = ".env" root = tk.Tk(screenName=None, baseName='VerityBot', className='VerityBot', useTk=1) frame = tk.Frame(root) @@ -169,6 +169,4 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() -if __name__ == "__main__": - init() + root.mainloop() \ No newline at end of file From ef9df9f14ee7269360129f6869b41f8de3412988 Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Thu, 9 Apr 2026 18:09:07 -0400 Subject: [PATCH 32/36] Fixed WSL capabilities Dialogue box will now run in base WSL. --- src/ConfigReader.py | 2 +- src/dialogueBox.py | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ConfigReader.py b/src/ConfigReader.py index 08b3ff1..99633a2 100644 --- a/src/ConfigReader.py +++ b/src/ConfigReader.py @@ -1,4 +1,4 @@ import configparser config = configparser.ConfigParser() -config.read("config.ini") +config.read("/config.ini") diff --git a/src/dialogueBox.py b/src/dialogueBox.py index a8400ea..11ba5a7 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -12,6 +12,8 @@ def init(): def Save_Button(): + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + #Get new values from the user entries newTo = inpTo.get() newFrom = inpFrom.get() @@ -39,21 +41,23 @@ def Save_Button(): dotenv.set_key(dotenv_file,"KIBANA_ENCRYPTION_KEY", newKibEnc) #Writes new config values to file - with open("config.ini", "w") as f: + with open(os.path.join(BASE_DIR, '..', 'config.ini'), "w") as f: co.write(f) #Successful save message successfulSave = tk.Label(root, text="Saved Successfully!") - successfulSave.grid(row = 13, column = 0, columnspan = 2, pady = 5) + successfulSave.grid(row = 12, column = 0, columnspan = 2, pady = 5) root.after(2000, successfulSave.destroy) def generate_kibana_key(): key = base64.b64encode(secrets.token_bytes(32)).decode() inpKibEnc.delete("1.0", "end") inpKibEnc.insert("1.0", key or "") - + + BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + co = configparser.ConfigParser() - co.read("config.ini") + co.read(os.path.join(BASE_DIR, '..', 'config.ini')) dotenv.load_dotenv() dotenv_file = ".env" @@ -169,4 +173,7 @@ def generate_kibana_key(): button = tk.Button(root, text="Save", width=20, command=Save_Button) button.grid(row = 12, column = 0, columnspan = 2, pady = 5) - root.mainloop() \ No newline at end of file + root.mainloop() + +if __name__ == "__main__": + init() From b3e5be40073d813f24d351a1e7d527ff5604cd23 Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 18:20:44 -0400 Subject: [PATCH 33/36] Updated Elastic Update Interval Entry System --- src/dialogueBox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 11ba5a7..31b4af6 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -33,7 +33,7 @@ def Save_Button(): co["Email"]["EMAIL_SUBJECT"] = newSubject co["Email"]["EMAIL_BODY_PREFIX"] = newMessage co["Email"]["EMAIL_SEND_TIME"] = newSendTime - co["Verkada"]["TIME_DELTA_INSTALLATION"] = newFreq + co["Verkada"]["ELASTIC_UPDATE_INTERVAL"] = newFreq dotenv.set_key(dotenv_file, "EMAIL_PASSWORD", newEmailPass) dotenv.set_key(dotenv_file, "VERKADA_API_KEY", newVerkAPI) dotenv.set_key(dotenv_file, "ELASTIC_PASSWORD", newElastPass) @@ -94,10 +94,10 @@ def generate_kibana_key(): emailPassLabel = tk.Label(root, text="Email Password").grid(row=6, column=0) inpEmailPass = tk.Entry(root, width=50) - #TIME_DELTA_INSTALLATION + #ELASTIC_UPDATE_INTERVAL freqLabel1 = tk.Label(root, text="Update Verkada") freqVar = tk.StringVar() - verkadaTimeInstallation = co.get("Verkada", "TIME_DELTA_INSTALLATION") + verkadaTimeInstallation = co.get("Verkada", "ELASTIC_UPDATE_INTERVAL") freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) freqLabel2 = tk.Label(frame, text="time(s) per day") From 3ae1c923c7520ee89b462c5bb77ce92cd5d6404c Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 19:18:03 -0400 Subject: [PATCH 34/36] Minor Fix --- src/Application.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Application.py b/src/Application.py index 8571fa8..981d741 100644 --- a/src/Application.py +++ b/src/Application.py @@ -8,8 +8,6 @@ from ConfigReader import config from datetime import datetime, date -dialogueBox.init() - ELASTIC_PASSWORD = os.environ.get("ELASTIC_PASSWORD") if not ELASTIC_PASSWORD: print("ERROR: ELASTIC_PASSWORD has not been set, exiting...") From a00a415e55effedde317f4c1e4b5624ff90a401f Mon Sep 17 00:00:00 2001 From: Julian V Date: Thu, 9 Apr 2026 19:18:39 -0400 Subject: [PATCH 35/36] Removed Redundant import statement --- src/Application.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Application.py b/src/Application.py index 981d741..3609ff6 100644 --- a/src/Application.py +++ b/src/Application.py @@ -2,7 +2,6 @@ import CLI import ElasticSearch import os -import dialogueBox from Verkada import VerkadaContext from ConfigReader import config From a89058bb15b583cec6d93248047fa1acd0af776a Mon Sep 17 00:00:00 2001 From: Julian Valleroy Date: Sun, 12 Apr 2026 18:20:12 -0400 Subject: [PATCH 36/36] Updated Dialogue Box Labeling for Verkada Pull Frequency --- src/dialogueBox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dialogueBox.py b/src/dialogueBox.py index 31b4af6..bbf3b40 100644 --- a/src/dialogueBox.py +++ b/src/dialogueBox.py @@ -95,12 +95,12 @@ def generate_kibana_key(): inpEmailPass = tk.Entry(root, width=50) #ELASTIC_UPDATE_INTERVAL - freqLabel1 = tk.Label(root, text="Update Verkada") + freqLabel1 = tk.Label(root, text="Update Verkada every") freqVar = tk.StringVar() verkadaTimeInstallation = co.get("Verkada", "ELASTIC_UPDATE_INTERVAL") freqVar.set(verkadaTimeInstallation) freqSpinbox = tk.Spinbox(frame, textvariable=freqVar, from_=1, to=60, width = 4) - freqLabel2 = tk.Label(frame, text="time(s) per day") + freqLabel2 = tk.Label(frame, text="minute(s)") #Verkada API apiKeyLabel = tk.Label(root, text="Verkada API Key").grid(row=8, column=0)