見出し画像

SleepGuard: A Script for Health-Conscious Coders


Introduction

In today's fast-paced digital age, where lines between work and personal time blur, maintaining a healthy balance can be challenging. For many dedicated coders, the allure of programming often overshadows the importance of self-care, leading to compromised health and well-being. Recognizing this common dilemma, SleepGuard emerges as a beacon of support, offering a solution crafted by one such coder who embarked on a quest for equilibrium.

Meet the author: a passionate coder whose fervor for crafting intricate algorithms and solving complex problems knows no bounds. Yet, amidst the exhilarating world of code, sleep often takes a backseat, relegated to the periphery as lines of code blur into the night. Concerned about the toll this nocturnal lifestyle was taking on their health, our intrepid coder sought a remedy—one that would prioritize both their passion for coding and their fundamental need for rest.

Thus, SleepGuard was born—a testament to ingenuity and self-awareness. At its core, SleepGuard is not just a script; it's a lifeline, meticulously designed to safeguard the coder's well-being while nurturing their creative fervor. Its mission? To vigilantly monitor nocturnal coding sessions and ensure that sleep, the cornerstone of good health, remains untarnished.

But how does SleepGuard accomplish this feat? Through a seamless blend of automation and accountability. By harnessing the power of system logs and email notifications, SleepGuard stands sentinel, ready to intervene at the first sign of excessive coding or neglect of rest. It's a silent guardian, tirelessly processing data, detecting anomalies, and sounding the alarm when necessary.

With SleepGuard by their side, our coder can finally reclaim control over their health and habits. No longer shackled by the relentless pursuit of code, they can embrace a more balanced lifestyle, one where productivity coexists harmoniously with self-care. And in the ever-watchful gaze of SleepGuard, they find not just a guardian, but a companion—a reminder that even in the vast expanse of cyberspace, the human element remains paramount.

So, to all the dreamers and coders alike, SleepGuard extends an invitation: join us on this journey towards equilibrium, where passion and well-being converge in perfect harmony. With SleepGuard, a restful night's sleep is no longer a distant dream—it's a reality waiting to be embraced.

Directory structure

.
|   .gitignore
|   EmailFunctions.ps1
|   ExtractLogFunction.ps1
|   LoadConf.ps1
|   LogProcessingFunctions.ps1
|   main.ps1
|   setting.conf
|
---log

Each scripts


# SettingsFunctions.ps1

# Function to read configuration settings from a file
function Read-SettingsFile {
    param(
        [string]$FilePath
    )

    # Initialize an empty hash table to store settings
    $params = @{}

    # Read content of the configuration file specified by $FilePath
    Get-Content $FilePath | ForEach-Object { 
        # Convert each line of the file into key-value pairs
        $params += ConvertFrom-StringData $_.Replace('\','\\')
    }

    # Return the populated hash table containing the configuration settings
    return $params
}
  • Function Name: Read-SettingsFile

    • This function is responsible for reading a configuration file.

  • Parameters:

    • $FilePath: Specifies the path of the configuration file to be read.

  • Functionality:

    • Inside the function, an empty hash table ($params) is created to store the configuration settings.

    • The Get-Content cmdlet is used to read the content of the configuration file specified by $FilePath.

    • The ForEach-Object cmdlet is then used to iterate through each line of the file.

    • Within the loop, each line is converted into key-value pairs using ConvertFrom-StringData. This cmdlet interprets each line as a string in the format key=value.

    • Since backslashes need to be escaped in PowerShell strings, Replace('\','\\') replaces single backslashes with double backslashes to handle any paths or escape characters properly.

    • The key-value pairs are added to the hash table $params.

    • Finally, the function returns the populated hash table containing the configuration settings.


# ExtractLogFunction.ps1

# Function to convert SID to username
function Get-UsernameFromSID {
    param(
        [string]$SID
    )

    # Create a SecurityIdentifier object from the SID
    $identifier = New-Object System.Security.Principal.SecurityIdentifier($SID)
    
    # Translate the SID to NTAccount format (username)
    $username = $identifier.Translate([System.Security.Principal.NTAccount]).Value
    
    # Return the username
    return $username
}

# Function to retrieve and process log events
function Process-LogEvents {
    param(
        [string]$LogFilePath
    )

    # Retrieve system log events from the last 7 days
    Get-EventLog System -After (Get-Date).AddDays(-7) | `
    # Filter events with specified InstanceId values
    Where-Object { $_.InstanceId -in (6001, 6002, 7001, 7002) } | `
    # Process each event
    ForEach-Object {
        # Create a custom object with event properties
        [PSCustomObject]@{
            InstanceId = $_.InstanceId
            Message = if ($_.InstanceId % 2 -eq 1) { "Logon" } else { "Logoff" } # Determine if it's a logon or logoff event based on InstanceId
            TimeGenerated = $_.TimeGenerated
            User = if ($_.ReplacementStrings[-1] -match "^S-1-5") { Get-UsernameFromSID $_.ReplacementStrings[-1] } else { $_.ReplacementStrings[-1] } # Convert SID to username if available
        }
    } | Export-Csv -Path $LogFilePath -NoTypeInformation # Export processed events to a CSV file
}
  • Function Name: Process-LogEvents

  • Functionality:

    • This function retrieves system log events from the last 7 days, processes them, and exports the processed events to a CSV file.

  • Parameters:

    • $LogFilePath: Specifies the path where the CSV file containing the processed events will be saved.

  • Functionality:

    • It retrieves system log events from the System log that occurred within the last 7 days using Get-EventLog.

    • Filters the events based on their InstanceId values to include only specific event types (6001, 6002, 7001, 7002) using Where-Object.

    • For each event, it creates a custom object containing properties such as InstanceId, Message (Logon/Logoff), TimeGenerated, and User.

    • It determines if it's a logon or logoff event based on the InstanceId.

    • If the User property contains a SID, it calls the Get-UsernameFromSID function to convert it to a username.

    • Finally, it exports the processed events to a CSV file specified by $LogFilePath using Export-Csv with the -NoTypeInformation parameter.


# LogProcessingFunctions.ps1

# Function to process only excess data from a log file
function Process-ExcessLogData {
    param(
        [string]$LogFilePath,
        [string]$ReportExcessFile
    )

    # Read the content of the log file
    $logLines = Get-Content -Path $LogFilePath

    # If there is a report file for excess data, delete it
    if (Test-Path $ReportExcessFile) {
        Remove-Item $ReportExcessFile -Force
    }

    # Initialize an array to store excess records
    $excessRecords = @()

    # Get the number of lines in the log file
    $lineCount = $logLines.Count

    # Loop through the array using indices
    for ($i = 1; $i -lt $lineCount - 1; $i++) {
        # Parse each line to extract necessary information
        $fields = $logLines[$i] -split '","'
        $instanceId = $fields[0].Trim('"')
        $message = $fields[1].Trim('"')
        $timeGenerated = $fields[2].Trim('"')
        $user = $fields[3].Trim('"')

        $Nextfields = $logLines[$i + 1] -split '","'
        $NextinstanceId = $Nextfields[0].Trim('"')
        $Nextmessage = $Nextfields[1].Trim('"')
        $NexttimeGenerated = $Nextfields[2].Trim('"')
        $Nextuser = $Nextfields[3].Trim('"')

        # Get time information
        $logTime = [DateTime]::ParseExact($timeGenerated, "M/d/yyyy h:mm:ss tt", $null)
        $NextlogTime = [DateTime]::ParseExact($NexttimeGenerated, "M/d/yyyy h:mm:ss tt", $null)

        # Extract records where logon or logoff events occur between 1 AM and 7 AM
        if ($logTime.Hour -ge 1 -and $logTime.Hour -lt 7) {
            # Create a line to add to the report
            $excessRecords += "$instanceId,$message,$timeGenerated,$user"
        }

        # Extract records where a logoff event occurs before 1 AM and is followed by a logon event after 7 AM
        if ($logTime.Hour -ge 7 -and $message -eq "Logoff" -and $NextlogTime.Hour -lt 1 -and $Nextmessage -eq "Logon") {
            # Create lines to add to the report
            $excessRecords += "$instanceId,$message,$timeGenerated,$user"
            $excessRecords += "$NextinstanceId,$Nextmessage,$NexttimeGenerated,$Nextuser"
        }
    }

    # If excess records are found, write them to a file
    if ($excessRecords.Count -gt 0) {
        # Get the header
        $header = '"InstanceId","Message","TimeGenerated","User"'
        $excessRecordsWithHeader = $header + "`r`n" + ($excessRecords -join "`r`n")

        # Create the report
        $excessRecordsWithHeader | Out-File -FilePath $ReportExcessFile -Encoding UTF8
        Write-Host "Excess time report has been created." -ForegroundColor Green
    } else {
        Write-Host "No excess records found." -ForegroundColor Yellow
    }
}
  • Function Name: Process-ExcessLogData

  • Functionality:

    • This function processes only the excess data from a log file.

  • Parameters:

    • $LogFilePath: Specifies the path to the log file to be processed.

    • $ReportExcessFile: Specifies the path where the report file for excess data will be saved.

  • Functionality:

    1. Reads the content of the log file specified by $LogFilePath using Get-Content cmdlet.

    2. Checks if the report file for excess data exists ($ReportExcessFile), and if it does, it removes it using Remove-Item.

    3. Initializes an array named $excessRecords to store the excess data.

    4. Calculates the number of lines in the log file.

    5. Iterates through each line of the log file using a for loop.

    6. Parses each line to extract necessary information such as InstanceId, Message, TimeGenerated, and User.

    7. Determines if the log entry represents an excess record based on specific conditions:

      • If the logon or logoff event occurs between 1 AM and 7 AM, it's considered excess data.

      • If a logoff event occurs before 1 AM and is followed by a logon event after 7 AM, both entries are considered excess data.

    8. Adds the excess records to the $excessRecords array.

    9. If excess records are found:

      • Prepends a header to the excess records.

      • Writes the excess records along with the header to a new file specified by $ReportExcessFile using Out-File.

      • Displays a message indicating that the excess time report has been created.

    10. If no excess records are found, it displays a message indicating that no excess records were found.


# EmailFunctions.ps1

# Function to send an email
function Send-Email {
    param(
        [string]$From,
        [string]$To,
        [string]$Subject,
        [string]$Body,
        [string]$ReportNormalFile,
        [string]$ReportExcessFile,
        [string]$SmtpServer,
        [int]$SmtpPort,
        [string]$Username,
        [string]$Pw,
        [string]$MailSendLog
    )

    # Configure SMTP client to use UTF-8 encoding
    $mailMessage = New-Object System.Net.Mail.MailMessage
    $mailMessage.From = $From
    $mailMessage.To.Add($To)
    $mailMessage.Subject = $Subject
    $mailMessage.Body = $Body
    $mailMessage.IsBodyHtml = $false
    $mailMessage.BodyEncoding = [System.Text.Encoding]::UTF8
    $mailMessage.SubjectEncoding = [System.Text.Encoding]::UTF8

    # Check if the regular report exists
    if (Test-Path $ReportNormalFile) {
        $attachmentRegular = New-Object System.Net.Mail.Attachment($ReportNormalFile)
        $mailMessage.Attachments.Add($attachmentRegular)
    }

    # Check if the excess report exists
    if (Test-Path $ReportExcessFile) {
        $attachmentExcess = New-Object System.Net.Mail.Attachment($ReportExcessFile)
        $mailMessage.Attachments.Add($attachmentExcess)
        $mailMessage.Subject += " !!! Warning: Excess Records Found !!! "
    }

    # Create SMTP client
    $smtpClient = New-Object System.Net.Mail.SmtpClient($SmtpServer, $SmtpPort)
    $smtpClient.EnableSsl = $true
    $smtpClient.Credentials = New-Object System.Net.NetworkCredential($Username, $Pw)

    # Send the email
    try {
        $smtpClient.Send($mailMessage)
        Write-Output $mailMessage
        Write-Host "Email sent successfully to $($To)." -ForegroundColor Green
        # Write to the email send log
        Add-Content -Path $MailSendLog -Value "$(Get-Date): Email sent successfully to $($To)."
    } catch {
        Write-Host "Failed to send email to $($To): $($_.Exception.Message)" -ForegroundColor Red
        # Write to the error log
        Add-Content -Path $MailSendLog -Value "$(Get-Date): Failed to send email to $($To): $($_.Exception.Message)"
    } finally {
        $mailMessage.Dispose()
    }
}
  • Function Name: Send-Email

  • Functionality:

    • This function sends an email with optional attachments and logs the outcome.

  • Parameters:

    • $From: Specifies the sender's email address.

    • $To: Specifies the recipient's email address.

    • $Subject: Specifies the subject of the email.

    • $Body: Specifies the body/content of the email.

    • $ReportNormalFile: Specifies the path of the regular report file to be attached (optional).

    • $ReportExcessFile: Specifies the path of the excess report file to be attached (optional).

    • $SmtpServer: Specifies the SMTP server address.

    • $SmtpPort: Specifies the SMTP server port.

    • $Username: Specifies the username for SMTP authentication.

    • $Pw: Specifies the password for SMTP authentication.

    • $MailSendLog: Specifies the path of the log file to record email sending activity.

  • Functionality:

    1. Creates a MailMessage object to configure email settings such as sender, recipient, subject, and body.

    2. Checks if a regular report file exists ($ReportNormalFile) and attaches it to the email if found.

    3. Checks if an excess report file exists ($ReportExcessFile) and attaches it to the email if found. It also modifies the email subject to include a warning about excess records.

    4. Creates an SMTP client (SmtpClient) to send the email.

    5. Configures the SMTP client to use SSL encryption and sets credentials for authentication.

    6. Attempts to send the email. If successful, it writes a success message to the console and logs the event to the specified log file ($MailSendLog).

    7. If sending the email fails, it writes an error message to the console and logs the error along with the timestamp to the specified log file.

    8. Finally, disposes of the MailMessage object to release system resources.


# Import Modules
# Import ExtractLogFunction.ps1
. "$PSScriptRoot\ExtractLogFunction.ps1"
# Import SettingsFunctions.ps1
. "$PSScriptRoot\LoadConf.ps1"
# Import LogProcessingFunctions.ps1
. "$PSScriptRoot\LogProcessingFunctions.ps1"
# Import EmailFunctions.ps1
. "$PSScriptRoot\EmailFunctions.ps1"

# Read Configuration File
$CONF_FILE_PATH = "D:\LogonLog\setting.conf" # Path to the configuration file
$PARAM = Read-SettingsFile -FilePath $CONF_FILE_PATH

# Variable Assignment
$LogFilePath = $PARAM.LogFilePath
$ReportNormalFile = $LogFilePath
$ReportExcessFile = $PARAM.ReportFilePath
$MailSendLog = $PARAM.MailSendLog
$SmtpServer = $PARAM.SmtpServer
$SmtpPort = $PARAM.SmtpPort
$Username = $PARAM.Username
$Pw = $PARAM.Pw
$To = $PARAM.To
$Subject = $PARAM.Subject
$Body = $PARAM.Body

# Retrieve Logs from Windows System Log
Process-LogEvents -LogFilePath $LogFilePath 

# Process Logs and Generate Reports
Process-ExcessLogData -LogFilePath $LogFilePath -ReportExcessFile $ReportExcessFile

# Send Email
Send-Email -From $Username -To $To -Subject $Subject -Body $Body -ReportNormalFile $ReportNormalFile -ReportExcessFile $ReportExcessFile -SmtpServer $SmtpServer -SmtpPort $SmtpPort -Username $Username -Pw $Pw -MailSendLog $MailSendLog

This script segment performs several key actions related to log processing and email functionality:

  1. Module Import: The script imports multiple PowerShell modules necessary for log processing and email functionalities. These modules include ExtractLogFunction.ps1, SettingsFunctions.ps1, LogProcessingFunctions.ps1, and EmailFunctions.ps1. These modules likely contain various functions required for the subsequent operations.

  2. Reading Configuration File: The script reads a configuration file located at the path specified by $CONF_FILE_PATH ("D:\LogonLog\setting.conf"). It utilizes the Read-SettingsFile function from the SettingsFunctions.ps1 module to parse the configuration settings and store them in the $PARAM variable.

  3. Variable Assignment: The script assigns values to various variables based on the configuration settings retrieved from the configuration file. These variables include $LogFilePath, $ReportNormalFile, $ReportExcessFile, $MailSendLog, $SmtpServer, $SmtpPort, $Username, $Pw, $To, $Subject, and $Body. These variables likely contain file paths, email addresses, SMTP server details, and other parameters required for log processing and email sending.

  4. Log Processing: The script calls the Process-LogEvents function from the LogProcessingFunctions.ps1 module to retrieve and process log events from the Windows System log file specified by $LogFilePath.

  5. Excess Log Data Processing: It then calls the Process-ExcessLogData function from the LogProcessingFunctions.ps1 module to process the log events further and generate reports, including an excess log data report, based on certain criteria.

  6. Email Sending: Finally, the script calls the Send-Email function from the EmailFunctions.ps1 module to send an email notification. It passes various parameters such as sender, recipient, subject, body, attachment files (if any), SMTP server details, and authentication credentials to the function for email configuration and sending.

Done! Have a restful sleep!


この記事が気に入ったらサポートをしてみませんか?