The code behind:


WARNING : NOTHING HERE CHANGED FROM 2.7.0. This is because modifications were made on a separate QuickPreset file.

# All library imports
from tkinter import *
from tkinter import messagebox
from pypresence import Presence
import time
import datetime
import requests
import urllib
import os
import os.path
from os import path
import sys
import linecache
import SystemTray
import psutil
from plyer.utils import platform
from plyer import notification



# Window setting up
window = Tk() # Main window init
window.title("RichPresence") # Window title init
window.geometry("800x500") # Window size setting

# Global variables (used for inter-function variables not passed as argument)
global info
global log
info = 0
log = 0

# Confirm button
def confirm(ID_Entry):
    """First setting up, asks user info (Discord application ID) and saves it in Identity.txt file"""
    global info
    global log
    Client_ID = ID_Entry.get()

    log.write("["+getTime()+"]   "+ "Making sure Identity info is valid\n")
    log.flush()

    info.write(Client_ID+'\n') # Save Client ID (Discord application number string)
    log.write("["+getTime()+"]   "+ "Finished writing first-time info to identity file\n")
    log.flush()

    messagebox.showinfo(title = "Success", message = "Correctly saved info! Restart program to use.") # Indicates success of saved data

    try:
        log.write("["+getTime()+"]   "+ "Closing identity file access to remove danger of corruption\n")
        log.flush()
        info.close() # Close txt file
    except:
        PrintException()

        leave() # Exit application, restart program

def Timer():
    """Timer function, asks user whatever time they want (hours, minutes, seconds), then calls createTimer"""
    global log
    try:
        MainFrame.destroy() # Destroy previous window (deletes buttons, labels of main menu)
        log.write("["+getTime()+"]   "+ "Destroyed Main frame window\n")
        log.flush()
        Time = Label(window, text = "Choose your time!").grid(column = 0, row = 0) # Main label
        Hours = Label(window, text = "Hours:").grid(column=0, row=2) # Hours label
        HoursEntry = Entry(window, width = 10) # Hour entry (user input area)
        HoursEntry.grid(column=1, row=2) # add to grid
        Minutes = Label(window, text = "Minutes:").grid(column=2, row=2) # Minutes label
        MinutesEntry = Entry(window, width = 10) # Minutes entry (user input area)
        MinutesEntry.grid(column=3, row=2) # add to grid
        Seconds = Label(window, text = "Seconds:").grid(column=4, row=2) # Seconds label
        SecondsEntry = Entry(window, width = 10) # Seconds entry (user input area)
        SecondsEntry.grid(column=5, row=2) # add to grid
        label = Label(window, text= "Choose the top text:").grid(column=0, row = 3) # Text label
        TextEntry = Entry(window, width = 20) # Entry box (user input area) for text you want to add
        TextEntry.grid(column = 1, row = 3) # add to grid
        Confirm = Button(window, text="Confirm", command = lambda: createTimer(HoursEntry, MinutesEntry, SecondsEntry, TextEntry)).grid(row = 4, sticky = W) # Confirm button
        log.write("["+getTime()+"]   "+ "Created Timer window\n")
        log.flush()
    except:
        PrintException()

def createTimer(HoursEntry, MinutesEntry, SecondsEntry, Text):
    """Gets all timer values, and converts to correct value, then if all is correct, start the timer with info and display to Discord profile"""
    global log
    log.write("["+getTime()+"]   "+ "Confirmed data entry\n")
    log.flush()
    try:
        hour = int(HoursEntry.get()) # Gets Entry Hour Information
        minutes = int(MinutesEntry.get()) # Gets Minutes Entry Information
        seconds = int(SecondsEntry.get()) # Gets Seconds Entry Information
        text = str(Text.get()) # Gets text Entry Information
        log.write("["+getTime()+"]   "+ "Timer values approved with profile: \nSelected hour: "+str(hour)+"\nSelected minutes: "+str(minutes)+"\nSelected seconds:"+str(seconds)+"\nSelected additional text: "+text+"\n")
        log.flush()
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Closing UI - leaving Python\n")
    log.flush()
    window.destroy()
    print("Your code is running! Keep this window open to display your status!\nCheck the *System Tray* to hide / show this window")

    minute_counter = 0 # Initialize variable
    counter=0
    for i in text:
        counter += 1

    log.write("["+getTime()+"]   "+ "Opening Identity file\n")
    log.flush()
    info = open("Identity.txt", "r+")
    client_id = info.readline()
    RPC = Presence(client_id, pipe=0)
    RPC.connect()

    try:
        log.write("["+getTime()+"]   "+ "Attempting to create system notification\n")
        log.flush()
        notification.notify(
        title='Tray Rich Presence',
        message='Rich Presence is still running in tray!\nUse it to hide / show the CMD window!',
        app_name='RichPresence',
        app_icon='DiscordLogo.' + ('ico' if platform == 'win' else 'png')
        )
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Notification Success!\n")
    log.flush()

    if text == "" or text == " " or counter<=1: # If no text was selected
        time_left = {'hours': hour, 'minutes': minutes, 'seconds': seconds} # Time left at start

        log.write("["+getTime()+"]   "+ "Total timer look: "+str(time_left)+"\n")
        log.flush()

        try:
            # Main timer loop
            log.write("["+getTime()+"]   "+ "Created loop\n")
            log.flush()
            log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
            log.flush()
            while hour + minutes + seconds >= 0:
                time_left['seconds'] -=15
                minute_counter += 1
                if minute_counter >= 4:
                    minute_counter = 0
                    time_left['seconds'] = 60
                    time_left['minutes'] -=1
                    if time_left['minutes'] <= 0:
                        time_left['minutes'] = 60
                        time_left['hours'] -= 1
                RPC.update(details='Time left: ' + str(time_left['hours']) + ' hours, ' + str(time_left['minutes']) + ' minutes, ' + str(time_left['seconds']) + ' seconds.')
                time.sleep(15)
        except:
            PrintException()
    else: # If text was selected
        time_left = {'hours': hour, 'minutes': minutes, 'seconds': seconds} # Time left at start

        log.write("["+getTime()+"]   "+ "Total timer look: "+str(time_left)+"\n")
        log.flush()

        try:
            # Main timer loop
            log.write("["+getTime()+"]   "+ "Created loop\n")
            log.flush()
            log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
            log.flush()
            while hour + minutes + seconds >= 0:
                time_left['seconds'] -=15
                minute_counter += 1
                if minute_counter >= 4:
                    minute_counter = 0
                    time_left['seconds'] = 60
                    time_left['minutes'] -=1
                    if time_left['minutes'] <= 0:
                        time_left['minutes'] = 60
                        time_left['hours'] -= 1
                RPC.update(details=text,state='Time left: ' + str(time_left['hours']) + ' hours, ' + str(time_left['minutes']) + ' minutes, ' + str(time_left['seconds']) + ' seconds.')
                time.sleep(15)
        except:
            PrintException()

def Local():
    """Display local time for user (uses computer local time settings), then updates user discord status"""
    global log
    log.write("["+getTime()+"]   "+ "Closing UI - Keeping Python console\n")
    log.flush()
    MainFrame.destroy() # Destroy main menu window
    window.destroy()
    print("Your code is running! Keep this window open to have your status shown!\nCheck the *System Tray* to hide / show this window")

    info = open("Identity.txt", "r+")
    client_id = info.readline()
    RPC = Presence(client_id, pipe=0)
    RPC.connect()

    try:
        log.write("["+getTime()+"]   "+ "Attempting to create system notification\n")
        log.flush()
        notification.notify(
        title='Tray Rich Presence',
        message='Rich Presence is still running in tray!\nUse it to hide / show the CMD window!',
        app_name='RichPresence',
        app_icon='DiscordLogo.' + ('ico' if platform == 'win' else 'png')
        )
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Notification success!\n")
    log.flush()

    try:
        log.write("["+getTime()+"]   "+ "Created loop\n")
        log.flush()
        log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
        log.flush()
        while True:
            RPC.update(details='Current time: ' + str(datetime.datetime.now().replace(microsecond=0))) # Update loop for discord profile
            time.sleep(1)
    except:
        PrintException()

def HWINFO():
    """Displays user's hardware information"""
    global log
    try:
        MainFrame.destroy()
        log.write("["+getTime()+"]   "+ "Main menu window destroyed\n")
        log.flush()
        Hard = Label(window, text = "Welcome to the Hardware Information area!").grid(column = 0, row = 0) # Main presentation label
        showCPU = BooleanVar() # Show user balance question (boolean value returned)
        Checkbutton(window, text="Show CPU usage?", variable = showCPU).grid(row = 1, sticky = W) # Checkbutton grid (add to window)
        showRAM = BooleanVar() # Show user XP question (boolean value returned)
        Checkbutton(window, text = "Show RAM usage?", variable = showRAM).grid(row = 2, sticky = W) # Checkbutton grid (add to window)
        Confirm = Button(window, text="Confirm", command = lambda: ShowHWINFO(showCPU, showRAM)).grid(row = 3, sticky = W) # Confirm button add to grid (add to window)
        log.write("["+getTime()+"]   "+ "Hardware information window created!\n")
        log.flush()
    except:
        PrintException()

def ShowHWINFO(showCPU, showRAM):
    """Area for hardware info loop."""
    global log
    try:
        showCPU = showCPU.get() # Gets Balance Boolean info (True or False) to indicate if a user wants to show balance or not
        showRAM = showRAM.get() # Gets XP Boolean info (True or False) to indicate if a user wants to show xp or not
        log.write("["+getTime()+"]   "+ "Hardware choice was approved as: \nShow CPU: "+str(showCPU)+"\nShow RAM: "+str(showRAM)+"\n")
        log.flush()
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Closing UI - Keeping Python console\n")
    log.flush()
    window.destroy()
    print("Your code is running! Keep this window open to keep your status shown!\nCheck the *System Tray* to hide / show this window")

    try:
        log.write("["+getTime()+"]   "+ "Attempting to create system notification\n")
        log.flush()
        notification.notify(
        title='Tray Rich Presence',
        message='Rich Presence is still running in tray!\nUse it to hide / show the CMD window!',
        app_name='RichPresence',
        app_icon='DiscordLogo.' + ('ico' if platform == 'win' else 'png')
        )
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Notification success!\n")
    log.flush()

    info = open("Identity.txt", "r+")
    client_id = info.readline()
    RPC = Presence(client_id, pipe=0)
    RPC.connect()

    while True:
        if showCPU == True and showRAM == True: # If user wants both to show:
            RPC.update(details='CPU usage: '+str(psutil.cpu_percent())+'%',state='And RAM usage: ' + str(psutil.virtual_memory().used/1000000000)[:-7] +' / '+str(psutil.virtual_memory().total/1000000000)[:-7]+'GB') # Show balance and XP
            time.sleep(15)
        elif showCPU == True and showRAM == False:
            RPC.update(details='CPU usage: '+str(psutil.cpu_percent())+'%') # Show Balance only
            time.sleep(15)
        elif showCPU == False and showRAM == True:
            RPC.update(details='RAM usage: '+str(psutil.virtual_memory().used/1000000000)[:-7]+' / '+str(psutil.virtual_memory().total/1000000000)[:-7]+'GB') # Show XP only
            time.sleep(15)
        else:
            messagebox.showerror(title = 'Error!', message = 'No info selected! Revert to choice menu') # If user hasn't selected any info to display, revert to menu
            HWINFO() # Call main menu function

    

def Custom():
    """Displays a custom user profile"""
    global log
    try:
        MainFrame.destroy() # Delete main menu window
        log.write("["+getTime()+"]   "+ "Main menu window destroyed\n")
        log.flush()
        Cust = Label (window, text= "Welcome to your custom area!").grid(column = 0, row = 0) # Main presentation label
        Details = Label(window, text = "What details do you want ?(details is the top part of the status)").grid(column = 0, row = 1) # Details label
        DetailsEntry = Entry(window, width = 50) # Details Entry (user input area)
        DetailsEntry.grid(column = 1, row = 1) # Add to grid (add to window)
        State = Label(window, text = "What state do you want ?(state is the bottom of the status)").grid(column = 0, row = 2) # State label
        StateEntry = Entry(window, width = 50) # State entry (user input area)
        StateEntry.grid(column = 1, row = 2) # Add to grid (add to window)
        useButtons = BooleanVar() # Boolean variable, fo the use or not of buttons.
        Checkbutton(window, text = "Do you want any buttons?", variable = useButtons).grid(row = 3, sticky = W) # Checkbutton grid (add to window)
        But1NameLbl = Label(window, text = "What do you want as button name ?(what is shown on the button)").grid(column = 0, row = 4) # First button name label
        But1NameEntry = Entry(window, width = 20) # Button 1 entry (user input area)
        But1NameEntry.grid(column=1, row = 4) # Add to grid (add to window)
        But1LinkLbl = Label(window, text = "What do you want as button link ?(what will the button link to?)").grid(column = 0, row = 5) # First button link label
        But1LinkEntry = Entry(window, width = 20) # First button entry (user input area)
        But1LinkEntry.grid(column=1, row = 5) # Add to grid (add to window)
        But2NameLbl = Label(window, text = "What do you want as button second button name ?(what is shown on the button)").grid(column = 0, row = 6) # Second button name label
        But2NameEntry = Entry(window, width = 20) # Button 2 entry (user input area)
        But2NameEntry.grid(column=1, row = 6) # Add to grid (add to window)
        But2LinkLbl = Label(window, text = "What do you want as button second button link ?(what will the button link to?)").grid(column = 0, row = 7) # Second button link label
        But2LinkEntry = Entry(window, width = 20) # Second button entry (user input area)
        But2LinkEntry.grid(column=1, row = 7) # Add to grid (add to window)
        ImgLbl = Label(window, text = "What is your image name (needs to be uploaded to Discord application)").grid(column = 0, row = 8) # Status Image Name
        ImgEntry = Entry(window, width = 20) # Image name entry (user input area)
        ImgEntry.grid(column = 1, row = 8) # Add to grid (add to window)
        SmallImgLbl = Label(window, text = "What is your small image name (needs to be uploaded to Discord application)").grid(column = 0, row = 9) # Status Image Name
        SmallImgEntry = Entry(window, width = 20) # Image name entry (user input area)
        SmallImgEntry.grid(column = 1, row = 9) # Add to grid (add to window)
        ImgTextLbl = Label(window, text = "What is the text you want for the image (appears on image hover)").grid(column = 0, row = 10) # Image text label
        ImgTextEntry = Entry(window, width = 20) # Image text entry (user input area)
        ImgTextEntry.grid(column = 1, row = 10) # Add to grid (add to window)
        showRunningTime = BooleanVar() # Show elapsed time question (boolean value returned)
        Checkbutton(window, text = "Show elapsed time?", variable = showRunningTime).grid(row = 11, sticky = W) # Checkbutton grid (add to window)

        Confirm = Button(window, text="Confirm", command = lambda: createCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, False, 0)).grid(row = 12, sticky = W) # Confirm button

        if path.exists('preset1.txt'): # If preset 1 file exists
            Preset1 = Button(window, text=" Use Preset 1 ", command = lambda: catchCustom(1)).grid(row=14, sticky=W) # Option for user to use the preset 1
        else:
            # Preset 1 doesn't exist
            Preset1 = Button(window, text="Save Preset 1", command = lambda: saveCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, 1)).grid(row=14, sticky=W) # Save to Preset 1 file
        
        if path.exists('preset2.txt'): # If preset 2 file exists
            Preset2 = Button(window, text=" Use Preset 2 ", command = lambda: catchCustom(2)).grid(row=15, sticky=W) # Option for user to use the preset 2
        else:
            # Preset 2 doesn't exist
            Preset2 = Button(window, text="Save Preset 2", command = lambda: saveCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, 2)).grid(row=15, sticky=W) # Save to Preset 2 file option

        if path.exists('preset3.txt'): # If preset 3 file exists
            Preset3 = Button(window, text=" Use Preset 3 ", command = lambda: catchCustom(3)).grid(row=16, sticky=W) # Option for user to use the preset 3
        else:
            # Preset 3 doesn't exist
            Preset3 = Button(window, text="Save Preset 3", command = lambda: saveCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, 3)).grid(row=16, sticky=W) # Save the Preset 3 file option

        Warn = Label(window, text = "!Warning! Leave Blank if you don't want an item").grid(row = 13) # Leave blank if you don't want an item displayed
        log.write("["+getTime()+"]   "+ "Successfully created custom status window\n") # Write to log
        log.flush()
    except:
        PrintException()

def saveCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, n):
    """Function used to write user-selected info to a file, so he can use it as a preset whenever he loads app"""
    global log
    log.write("["+getTime()+"]   "+ "User save Preset "+str(n)+"\n") # Write to log
    log.flush()
    try:
        preset = open("preset"+str(n)+".txt","w+") # Create new Preset file (n being the preset number, from 1 to 3)
        DetailsEntry = DetailsEntry.get() # Get details
        StateEntry = StateEntry.get() # Get State
        useButtons = useButtons.get() # Get the usage of buttons
        But1NameEntry = But1NameEntry.get() # Get Button 1 name
        But1LinkEntry = But1LinkEntry.get() # Get button 1 link
        But2NameEntry = But2NameEntry.get() # Get button 2 name
        But2LinkEntry = But2LinkEntry.get() # Get button 2 link
        ImgEntry = ImgEntry.get() # Get image name
        SmallImgEntry = SmallImgEntry.get() # get small image name
        ImgTextEntry = ImgTextEntry.get() # Get image text
        showRunningTime = showRunningTime.get() # Get the running time option
        log.write("["+getTime()+"]   "+ "Got all info added by user in window\n") # Write to log
        log.flush()
        preset.write(str(DetailsEntry)+"\n") # Save details info to preset
        preset.write(str(StateEntry)+"\n") # Save state info to preset
        preset.write(str(useButtons)+"\n") # Save the usage of buttons info to preset
        preset.write(str(But1NameEntry)+"\n") # Save button 1 name info to preset
        preset.write(str(But1LinkEntry)+"\n") # Save button 1 link info to preset
        preset.write(str(But2NameEntry)+"\n") # Save button 2 name info to preset
        preset.write(str(But2LinkEntry)+"\n") # Save button 2 link info to preset
        preset.write(str(ImgEntry)+"\n") # Save image name info to preset
        preset.write(str(SmallImgEntry)+"\n") # Save small image info to preset
        preset.write(str(ImgTextEntry)+"\n") # Save image text info to preset
        preset.write(str(showRunningTime)) # Save usage of elapsed time info to preset
        log.write("["+getTime()+"]   "+ "Saved user info choice to preset "+str(n)+"\n") # Save to log
        log.flush()
        Custom()
    except:
        PrintException()

def catchCustom(n):
    """When a user chooses a preset, get the info from preset file"""
    global log
    log.write("["+getTime()+"]   "+ "User chose Preset "+str(n)+"\n") # Write to log
    log.flush()
    try:
        f = open("preset"+str(n)+".txt", "r+") # Open the preset choice (n ranging from 1 to 3)
        DetailsEntry = f.readline()[:-1] # Read first line of preset file, Details
        StateEntry = f.readline()[:-1] # Read second line of preset file, State
        useButtons = f.readline()[:-1] # Read third line of preset file, want to use buttons?
        But1NameEntry = f.readline()[:-1] # Read fourth line of preset file, Button 1 Name
        But1LinkEntry = f.readline()[:-1] # Read fith line of preset file, Button 1 Link
        But2NameEntry = f.readline()[:-1] # Read sixth line of preset file, Button 2 Name
        But2LinkEntry = f.readline()[:-1] # Read seventh line of preset file, Button 2 Link
        ImgEntry = f.readline()[:-1] # Read eighth line of preset file, Image name
        SmallImgEntry = f.readline()[:-1] # Read ninth line of the preset file, small image text
        ImgTextEntry = f.readline()[:-1] # Read tenth line of preset file, Image text
        showRunningTime = f.readline() # Read the eleventh line of the preset file, usage of elapsed time
        log.write("["+getTime()+"]   "+ "Accessed preset "+str(n)+" information\n") # Write to log
        log.flush()
        createCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, True, n) # Call the activation of the main custom profile usage
    except:
        PrintException()


def createCustom(DetailsEntry, StateEntry, useButtons, But1NameEntry, But1LinkEntry, But2NameEntry, But2LinkEntry, ImgEntry, SmallImgEntry, ImgTextEntry, showRunningTime, state, n):
    """Creates the custom discord status loop, using user-provided info"""
    global log
    constant = time.time() # Time from launch of the command (acts as elapsed game time)

    try:
        log.write("["+getTime()+"]   "+ "Attempting to create system notification\n")
        log.flush()
        notification.notify(
        title='Tray Rich Presence',
        message='Rich Presence is still running in tray!\nUse it to hide / show the CMD window!',
        app_name='RichPresence',
        app_icon='DiscordLogo.' + ('ico' if platform == 'win' else 'png')
        )
    except:
        PrintException()

    log.write("["+getTime()+"]   "+ "Notification success!\n")
    log.flush()

    info = open("Identity.txt", "r+")
    client_id = info.readline()
    RPC = Presence(client_id, pipe=0)
    RPC.connect()

    # Get all of the info from entries
    try:
        if state == False: # If presets aren't used
            DetailsEntry = str(DetailsEntry.get()) # Get details
            StateEntry = str(StateEntry.get()) # Get State
            useButtons = useButtons.get() # Get the usage of buttons
            But1NameEntry = str(But1NameEntry.get()) # Get Button 1 name
            But1LinkEntry = str(But1LinkEntry.get()) # Get button 1 link
            But2NameEntry = str(But2NameEntry.get()) # Get button 2 name
            But2LinkEntry = str(But2LinkEntry.get()) # Get button 2 link
            ImgEntry = str(ImgEntry.get()) # Get image name
            SmallImgEntry = str(SmallImgEntry.get()) # Get Small Image name
            ImgTextEntry = str(ImgTextEntry.get()) # Get image text
            showRunningTime = showRunningTime.get() # Get the running time option
            log.write("["+getTime()+"]   "+ "Custom values approved as: \nDetails: "+DetailsEntry+"\nState: "+StateEntry+"\nShow Buttons?: "+str(useButtons)+"\nButton 1 Name: "+But1NameEntry+"\nButton 1 Link: "+But1LinkEntry+"\nButton 2 Name: "+But2NameEntry+"\nButton 2 Link: "+But2LinkEntry+"\nImage Name: "+ImgEntry+"\nSmall Image Name: "+SmallImgEntry+"\nImage Text on hover: "+ImgTextEntry+"\nShow elapsed time?: "+str(showRunningTime)+"\n") # Write to log
            log.flush()
        else: # Presets are used
            f = open("preset"+str(n)+".txt", "r+") # Open the preset choice (n ranging from 1 to 3)
            DetailsEntry = str(f.readline()[:-1]) # Read first line of preset file, Details
            StateEntry = str(f.readline()[:-1]) # Read second line of preset file, State
            useButtons = f.readline()[:-1] # Read third line of preset file, want to use buttons?
            But1NameEntry = str(f.readline()[:-1]) # Read fourth line of preset file, Button 1 Name
            But1LinkEntry = str(f.readline()[:-1]) # Read fith line of preset file, Button 1 Link
            But2NameEntry = str(f.readline()[:-1]) # Read sixth line of preset file, Button 2 Name
            But2LinkEntry = str(f.readline()[:-1]) # Read seventh line of preset file, Button 2 Link
            ImgEntry = str(f.readline()[:-1]) # Read eighth line of preset file, Image name
            SmallImgEntry = str(f.readline()[:-1]) # Read ninth line of preset file, Small Image name
            ImgTextEntry = str(f.readline()[:-1]) # Read tenth line of preset file, Image text
            showRunningTime = f.readline() # Read the eleventh line of the preset file, usage of elapsed time
            log.write("["+getTime()+"]   "+ "Custom values approved as: \nDetails: "+DetailsEntry+"\nState: "+StateEntry+"\nShow Buttons?: "+str(useButtons)+"\nButton 1 Name: "+But1NameEntry+"\nButton 1 Link: "+But1LinkEntry+"\nButton 2 Name: "+But2NameEntry+"\nButton 2 Link: "+But2LinkEntry+"\nImage Name: "+ImgEntry+"\nSmall Image Name: "+SmallImgEntry+"\nImage Text on hover: "+ImgTextEntry+"\nShow elapsed time?: "+str(showRunningTime)+"\n") # Write to log
    except:
        PrintException()

    window.destroy()
    print("Your code is running! Keep this window open to have your status shown!\nCheck the *System Tray* to hide / show this window")

    # Check if details were added
    if DetailsEntry == "" or DetailsEntry == " " or DetailsEntry == None:
        DetailsEntry = None
        log.write("["+getTime()+"]   "+ "Couldn't find custom details entry - No details will be displayed\n")
        log.flush()

    # Check if a State was added
    if StateEntry == "" or StateEntry == " " or StateEntry == None:
        StateEntry = None
        log.write("["+getTime()+"]   "+ "Couldn't find custom state entry - No state will be displayed\n")
        log.flush()

    # Check if there is an Image added
    if ImgEntry == "" or ImgEntry == " " or ImgEntry == None:
        ImgEntry = None
        log.write("["+getTime()+"]   "+ "Couldn't find custom image entry - No Image will be displayed\n")
        log.flush()

    elif len(ImgEntry) > 32:
        messagebox.showwarning(title = 'Warning', message = "Your Image name is too long! Continuing would break the process.\nMake sure it is equal to or smaller than 32 characters!")
        log.write("["+getTime()+"]   "+ "Image name was too long (exceeded 32 characters). Process interupted.\n")
        log.flush()
        leave()

    if SmallImgEntry == "" or SmallImgEntry == " " or SmallImgEntry == None:
        SmallImgEntry = None
        log.write("["+getTime()+"]   "+ "Couldn't find custom small image entry - No small image will be displayed\n")
        log.flush()

    elif len(SmallImgEntry) > 32:
        messagebox.showwarning(title = 'Warning', message = "Your Small Image name is too long! Continuing would break the process.\nMake sure it is equal to or smaller than 32 characters!")
        log.write("["+getTime()+"]   "+ "Small Image name was too long (exceeded 32 characters). Process interupted.\n")
        log.flush()
        leave()

    # Check if there is Image Text added
    if ImgTextEntry == "" or ImgTextEntry == " " or ImgTextEntry == None:
        ImgTextEntry = None
        log.write("["+getTime()+"]   "+ "Couldn't find custom image text entry - No text on image will be displayed\n")
        log.flush()

    # Does the user want to display elapsed time
    if showRunningTime == False:
        showRunningTime = None
        log.write("["+getTime()+"]   "+ "Running time None - No elapsed time will be shown\n")
        log.flush()

    isButton1 = True # Variable used to determine usage of button 1
    isButton2 = True # Variable used to determine usage of button 2
    try:
        if useButtons == True or useButtons == "True" or useButtons == "True " or useButtons == "True\n": # Does the user want to use buttons?
            if But1NameEntry == "" or But1NameEntry == " " or But1LinkEntry == "" or But1LinkEntry == " ": # Is anything blank (determines user intentions for button use)
                log.write("["+getTime()+"]   "+ "Exception found: Button 1 name or Button 1 Link were empty, but user selected buttons, see next line for issue.\n")
                log.flush()
                isButton1 = False # Did not want to use first button
                # Used for extended logging (where is the issue)
                if But1LinkEntry == "" or But1LinkEntry == " ": 
                    # Link 1 was empty, but a name was still given
                    log.write("["+getTime()+"]   "+ "Button 1 Name given - No link found (Error)\n")
                    log.flush()
                if But1NameEntry == "" or But1NameEntry == " ":
                    # Name 1 was empty, but a link was still given
                    log.write("["+getTime()+"]   "+ "Button 1 Link given - No name found (Error)\n")
                    log.flush()
            else:
                # Make sure that the link is valid before being added to status
                if But1LinkEntry[0:7] == 'http://' or But1LinkEntry[0:8] == 'https://': # Does it start with correct information
                    url = But1LinkEntry 
                    request = requests.get(url)
                    if request.status_code == 200: # Test if the link is valid
                        log.write("["+getTime()+"]   "+ "Button 1 Link is valid\n")
                        log.flush()
                    else: # Link is not valid, return error
                        messagebox.showerror(title = 'Error!', message = "Failed to validate link! Use format 'http://{website}.com' or 'https://{website}.com'")
                        log.write("["+getTime()+"]   "+ "Provided Button 1 Link is invalid\n")
                        log.flush()
                else: # http or https was not specified, add https:// to check if it works after
                    log.write("["+getTime()+"]   "+ "Button 1 Link did not include http or https, trying automatic conversion to https\n")
                    log.flush()
                    try:
                        But1LinkEntry = 'https://'+But1LinkEntry
                        url = But1LinkEntry 
                        request = requests.get(url)
                        if request.status_code == 200: # Test if the link is valid
                            log.write("["+getTime()+"]   "+ "Link modified and validated!\n")
                            log.flush()
                        else: # Link is not valid, return error
                            messagebox.showerror(title = 'Error!', message = "Failed to validate link! Use format 'http://{website}.com' or 'https://{website}.com'")
                            log.write("["+getTime()+"]   "+ "Error while parsing https: link still invalid - change link.\n")
                            log.flush()
                    except:
                        PrintException()

            if But2NameEntry == "" or But2NameEntry == " " or But2LinkEntry == "" or But2LinkEntry == " ": # Is anything blank (determines user intentions)
                log.write("["+getTime()+"]   "+ "Exception found: Button 2 name or Button 2 Link were empty, but user selected buttons, see next line for issue.\n")
                log.flush()
                isButton2 = False # Did not want to use second button
                # Extended logging for issues
                if But2LinkEntry == "" or But2LinkEntry == " ": # Link was given, but no name was specified
                    log.write("["+getTime()+"]   "+ "Button 2 Name given - No Link found (Error)\n")
                    log.flush()
                if But2NameEntry == "" or But2NameEntry == " ": # Name was given, but no link was specified
                    log.write("["+getTime()+"]   "+ "Button 2 Link given - No Name found (Error)\n")
                    log.flush()
            else:
                # Make sure link is valid before adding to status
                if But2LinkEntry[0:7] == 'http://' or But2LinkEntry[0:8] == 'https://': # if it starts with http:// or https://
                    url = But2LinkEntry
                    request = requests.get(url)
                    if request.status_code == 200: # Check if a page with that is found and valid
                        log.write("["+getTime()+"]   "+ "Button 2 Link is valid\n")
                        log.flush()
                    else:
                        # Print error if that link is invalid
                        log.write("["+getTime()+"]   "+ "Provided Button 2 Link is invalid\n")
                        log.flush()
                        messagebox.showerror(title = 'Error!', message = "Failed to validate link! Use format 'http://{website}.com' or 'https://{website}.com'")
                else:
                    # http or https was not specified, add https:// to check if it works better
                    log.write("["+getTime()+"]   "+ "Button 2 Link did not include http or https, trying automatic conversion to https\n")
                    log.flush()
                    try:
                        But2LinkEntry = 'https://'+But2LinkEntry
                        url = But2LinkEntry 
                        request = requests.get(url)
                        if request.status_code == 200: # Test if the link is valid
                            log.write("["+getTime()+"]   "+ "Link modified and validated!\n")
                            log.flush()
                        else: # Link is not valid, return error
                            log.write("["+getTime()+"]   "+ "Error while parsing https: link still invalid - change link.\n")
                            log.flush()
                            messagebox.showerror(title = 'Error!', message = "Failed to validate link! Use format 'http://{website}.com' or 'https://{website}.com'")
                    except:
                        PrintException()

            # Both buttons want to be used
            if isButton1 == True and isButton2 == True:
                log.write("["+getTime()+"]   "+ "Choice of Button 1 and Button 2 approved\n")
                log.flush()
                if showRunningTime == None:
                    log.write("["+getTime()+"]   "+ "Choice no elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Created loop\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Both buttons want to be used, but not elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But1NameEntry, "url": But1LinkEntry}, {"label": But2NameEntry, "url": But2LinkEntry}])
                        time.sleep(20)
                else:
                    log.write("["+getTime()+"]   "+ "Choice elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Both buttons want to be used, and show elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        start = constant,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But1NameEntry, "url": But1LinkEntry}, {"label": But2NameEntry, "url": But2LinkEntry}])
                        time.sleep(20)
            
            # User wants a button 1, but not a button 2
            if isButton1 == True and isButton2 == False:
                log.write("["+getTime()+"]   "+ "Choice of Button 1 approved, Button 2 - disabled\n")
                log.flush()
                if showRunningTime == None:
                    log.write("["+getTime()+"]   "+ "Choice no elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Show button 1, but not elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But1NameEntry, "url": But1LinkEntry}])
                        time.sleep(20)
                else:
                    log.write("["+getTime()+"]   "+ "Choice elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Show button 1, with elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        start=constant,
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But1NameEntry, "url": But1LinkEntry}])
                        time.sleep(20)

            # User wants second button, but not first, which reverts to having button 1 enabled but not the second :)
            if isButton1 == False and isButton2 == True:
                log.write("["+getTime()+"]   "+ "Choice Button 1 - disabled, choice Button 2 approved\n")
                log.flush()
                if showRunningTime == None:
                    log.write("["+getTime()+"]   "+ "Choice no elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Show button 2 (1), but not elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But2NameEntry, "url": But2LinkEntry}])
                        time.sleep(20)
                else:
                    log.write("["+getTime()+"]   "+ "Choice elapsed time approved\n")
                    log.flush()
                    log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                    log.flush()
                    # Show button 2 (1), with elapsed time
                    while True:
                        RPC.update(
                        details=DetailsEntry, 
                        state=StateEntry, 
                        start=constant,
                        large_image = ImgEntry,
                        small_image = SmallImgEntry,
                        large_text = ImgTextEntry, 
                        buttons = [{"label": But1NameEntry, "url": But1LinkEntry}])
                        time.sleep(20)
        # If the user does not want to use any buttons
        else:
            log.write("["+getTime()+"]   "+ "Choice Button 1 and Button 2 disabled\n")
            log.flush()
            if showRunningTime == None:
                log.write("["+getTime()+"]   "+ "Choice no elapsed time approved\n")
                log.flush()
                log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                log.flush()
                # Does not show buttons, but also removes elapsed time
                while True:
                    RPC.update(
                    details=DetailsEntry, 
                    state=StateEntry, 
                    large_image = ImgEntry,
                    small_image = SmallImgEntry,
                    large_text = ImgTextEntry)
                    time.sleep(20)
            else:
                log.write("["+getTime()+"]   "+ "Choice elapsed time approved\n")
                log.flush()
                log.write("["+getTime()+"]   "+ "Started loop - closing log\n")
                log.flush()
                # Does not show buttons, but includes elapsed time
                while True:
                    RPC.update(
                    details=DetailsEntry, 
                    state=StateEntry, 
                    start = constant,
                    large_image = ImgEntry,
                    small_image = SmallImgEntry,
                    large_text = ImgTextEntry)
                    time.sleep(20)
    except:
        PrintException()

def leave():
    """Exits the program, used for the exit button"""
    global log
    log.write("["+getTime()+"]   "+ "Call exit\n")
    log.flush()
    sys.exit() # Calls exit function

def close():
    """Function called to close process py.exe when Tray Icon is active"""
    os.system("TASKKILL /F /IM py.exe") 
    for process in (process for process in psutil.process_iter() if process.name()=="py.exe"):
        process.kill()
    sys.exit()


def getTime():
    """Get local user time, used for the logs."""
    global log
    try:
        return str(datetime.datetime.now()) # Get local time
    except:
        PrintException()

def checkUpdate():
    """Function used to check if an update is available for RichPresence, by trying to get a file name on the RichPresence website."""
    global log
    try:
        localVer = "2.7.0" # Manually set by code author (Angaros) each new variation
        headers = {
        "User-Agent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'
        }
        updateCheck = requests.get(url = 'https://rich-presence-website.vercel.app/'+localVer+'.txt', headers = headers) # Request the version file located on website. 
        if updateCheck.status_code != 200: # If the file isn't found, client version is not the latest version
            messagebox.showwarning(title = 'Warning', message = "You are on version "+localVer+", which is not the latest. You can still use the app, but new functionalities will not be added.")
            log.write("["+getTime()+"]   "+ "Found user on version "+localVer+". Please consider upgrading (use website).\n") # Write to log
            log.flush()
        else: # If  file is found from website, then user is on latest version
            log.write("["+getTime()+"]   "+ "Found user on version "+localVer+". You are on the latest version.\n") # Write to log
            log.flush()
    except:
        PrintException()

def checkInternet():
    """Function that checks if user is connected to internet - RichPresence can't work if user is not connected."""
    global log
    try:
        url = "http://www.google.com" # url we will connect to (google has a very high uptime, so if it fails it will usually be user fault.)
        timeout = 5 # timeout settings to retry connection
        request = requests.get(url, timeout=timeout) # Try to connect to url (google.com) using timeout
    except:
        messagebox.showerror(title = 'Error', message = "You are not connected to internet, Rich Presence will not work without an active internet connection.") # Tell user he need to be connected
        log.write("["+getTime()+"]   "+ "User not connected to internet - RichPresence will not work\n") # Write to log 
        log.flush()
        PrintException() # Call error
        
def PrintException():
    """Function used to save the error whenever an exception is caught, and saves it in logs. Useful for debugging on client platform"""
    global log
    log = open("mainLog.txt","a") # Re-open log, in case it was previously closed. Won't do anything if already open
    exc_type, exc_obj, tb = sys.exc_info()
    f = tb.tb_frame
    lineno = tb.tb_lineno # Get line number
    filename = f.f_code.co_filename # Get file name
    linecache.checkcache(filename) # Get line cache
    line = linecache.getline(filename, lineno, f.f_globals) # Get globals running (variables are captured)
    log.write("["+getTime()+"]   "+ 'Error caught in: ({}, LINE {} "{}"): {}, type: {}'.format(filename, lineno, line.strip(), exc_obj, exc_type)) # Get info and write it to log.
    log.flush()
    leave() # Exceptions indicate fatal issues, leave function must be called.

def NewLog():
    """Checks if mainLog is too large, and have oldLog to contain that info. If more are made, constantly replace the oldLog, and create a blank mainLog. Allows to only have 2 log files, with a max size of 100 KB."""
    global log
    try:
        filesize = os.path.getsize("mainLog.txt") # Get mainLog size
        if filesize >= 100000: # If larger than 100 KB.
            if os.path.isfile('oldLog.txt'): # oldLog exists
                log.close() # close log, to start the conversion (operations can't be made on open file)
                os.remove("oldLog.txt") # remove oldLog
                os.rename("mainLog.txt", "oldLog.txt") # Move mainLog into oldLog
                log = open("mainLog.txt","a") # Make a blank mainLog
            else:
                log.close() # close log, to start the conversion (operations can't be made on open file)
                os.rename("mainLog.txt", "oldLog.txt") # change mainLog to oldLog
                log = open("mainLog.txt","a") # create blank mainLog
    except:
        PrintException()

def start():
    global log
    try:
        log.write("["+getTime()+"]   "+ "Starting System Tray\n")
        log.flush()
        SystemTray.start()
    except:
        PrintException()



## Program Start


log = open("mainLog.txt","a") # Opens Log file, contains logs (updated in real time)
NewLog() # Check log file size
log.write("\n---------------------------------------New Session started----------------------------------------\n")
log.flush()
try:
    info = open("Identity.txt","r+")  # Open Identity file, contains user information
    log.write("["+getTime()+"]   "+ "Accessed Identity file\n")
    log.flush()
except:
    log.write("["+getTime()+"]   "+ "Identity File not found or removed!\n")
    log.flush()
    PrintException()

filesize = os.path.getsize("Identity.txt") # Gets Identity file size

log.write("["+getTime()+"]   "+ "Accessed log file\n")
log.flush()

# Internet connection test
try:
    log.write("["+getTime()+"]   "+ "Trying to connect to internet\n")
    log.flush()
    checkInternet()
    log.write("["+getTime()+"]   "+ "User connected to internet - may proceed\n")
    log.flush()
except:
    PrintException()

# Try to check for a latest update
try:
    log.write("["+getTime()+"]   "+ "Checking for latest update!\n")
    log.flush()
    checkUpdate()
except:
    PrintException()


try:
    start()
    log.write("["+getTime()+"]   "+ "Tray mode activated\n")
    log.flush()
except:
    PrintException()

try:
    window.protocol('WM_DELETE_WINDOW', leave) # Remap the "X" button of the tkinter to the leave function
except:
    PrintException()

log.write("["+getTime()+"]   "+ "Remapped close button\n")
log.flush()

if filesize == 0: # Is Identity empty, if so, ask for User information
    log.write("["+getTime()+"]   "+ "Identity file is empty - launching first-time setup!\n")
    log.flush()
    try:
        ID = Label(window, text = "What is the application ID?").grid(column = 0, row = 1) # Main label
        ID_Entry = Entry(window, width=30) # Enter user application ID (user input area)
        ID_Entry.grid(column = 1, row = 1) # add to grid (show on window)
        Confirm = Button(window, text="Confirm", command = lambda: confirm(ID_Entry)).grid(column = 0, row = 4) # Create confirm button and add to window
        log.write("["+getTime()+"]   "+ "Initialized initial setup Menu\n")
        log.flush()
    except:
        PrintException()
else:
    # Create main menu pannel selection
    MainFrame = Frame(window)
    client_id = info.readline()
    log.write("["+getTime()+"]   "+ "Accessed identity info: \nDiscord Application ID: " + client_id + ".")
    log.flush()
    try:
        Choice = Label(MainFrame, text = "What do you want?").pack(side=TOP) # Main Label
        timer = Button(MainFrame, text = "Make a Timer!", command = Timer).pack(side=TOP) # Main Label
        local = Button(MainFrame, text = "Show local time!", command = Local).pack(side=TOP) # Local time button
        hardware = Button(MainFrame, text = "Show my hardware info!", command = HWINFO).pack(side=TOP) # Hardware Info button
        custom = Button(MainFrame, text= "Show a custom status!", command = Custom).pack(side=TOP) # Custom info button
        Warn = Label(MainFrame, text = "Warning! Setting any of the previous will crash the program. It will still work though.").pack(side=TOP) # Warning with usage
        Exit = Button(MainFrame,text='Exit', command = leave).pack(side=TOP) # Exit button command
        Credit = Label(MainFrame, text = "Made by Angaros#1263").pack(side=TOP) # Credits for me
        MainFrame.pack(padx=10,pady=10)
        log.write("["+getTime()+"]   "+ "Initialized Main Menu\n")
        log.flush()
    except:
        PrintException()

# Mainloop for the window frame
window.mainloop()