diff options
| -rw-r--r-- | AD-powershell-tools/README.md | 1 | ||||
| -rw-r--r-- | AD-powershell-tools/ad-user-report.ps1 | 18 | ||||
| -rw-r--r-- | AD-powershell-tools/bulk-disable.ps1 | 10 | ||||
| -rw-r--r-- | AD-powershell-tools/bulk-reactivate.ps1 | 9 | ||||
| -rw-r--r-- | AD-powershell-tools/bulk-reset.ps1 | 24 | ||||
| -rw-r--r-- | AD-powershell-tools/inactive-ad-device-report.ps1 | 57 | ||||
| -rw-r--r-- | AD-powershell-tools/test-ad-credentials.ps1 | 47 | ||||
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | bitwarden-tools/README.md | 2 | ||||
| -rw-r--r-- | bitwarden-tools/bit2pass.py | 44 | ||||
| -rw-r--r-- | discord/README.md | 10 | ||||
| -rwxr-xr-x | discord/discord_alert.py | 48 | ||||
| -rw-r--r-- | subtitle-podcast/README.md | 143 | ||||
| -rw-r--r-- | subtitle-podcast/ffmpeg-path.png | bin | 0 -> 23897 bytes | |||
| -rw-r--r-- | subtitle-podcast/indicted-me.jpg | bin | 0 -> 120913 bytes | |||
| -rw-r--r-- | subtitle-podcast/indicted-me.mp3 | bin | 0 -> 59656 bytes | |||
| -rwxr-xr-x | subtitle-podcast/indicted-me.mp4 | bin | 0 -> 134775 bytes | |||
| -rw-r--r-- | subtitle-podcast/python-path.png | bin | 0 -> 181783 bytes | |||
| -rw-r--r-- | subtitle-podcast/subtitle-podcast.ps1 | 67 |
19 files changed, 484 insertions, 1 deletions
diff --git a/AD-powershell-tools/README.md b/AD-powershell-tools/README.md new file mode 100644 index 0000000..97f35cd --- /dev/null +++ b/AD-powershell-tools/README.md @@ -0,0 +1 @@ +Some AD convenience scripts diff --git a/AD-powershell-tools/ad-user-report.ps1 b/AD-powershell-tools/ad-user-report.ps1 new file mode 100644 index 0000000..954a34a --- /dev/null +++ b/AD-powershell-tools/ad-user-report.ps1 @@ -0,0 +1,18 @@ +# Simple user report script
+param (
+ [switch]$report
+ )
+
+Import-Module ActiveDirectory
+
+$today=(get-date -Format "yyyy-MM-dd")
+$users = Get-ADUser -filter * | Sort-Object name
+
+if($report) {
+ $fn = "users-$today.csv"
+ $users | export-csv .\$fn
+ [Console]::Error.WriteLine("Saved result list to $fn")
+} else {
+ [Console]::Error.WriteLine("Writing device list to stdout")
+ write-output $users
+}
diff --git a/AD-powershell-tools/bulk-disable.ps1 b/AD-powershell-tools/bulk-disable.ps1 new file mode 100644 index 0000000..e1fd180 --- /dev/null +++ b/AD-powershell-tools/bulk-disable.ps1 @@ -0,0 +1,10 @@ +# Import users from CSV and disable them
+
+Import-Module ActiveDirectory
+
+$csv = Get-Content $args[0]
+
+ForEach ($user in $csv) {
+ Disable-ADAccount -Identity $user
+ Write-Host $user"'s account has been fully disabled"
+}
diff --git a/AD-powershell-tools/bulk-reactivate.ps1 b/AD-powershell-tools/bulk-reactivate.ps1 new file mode 100644 index 0000000..e287aea --- /dev/null +++ b/AD-powershell-tools/bulk-reactivate.ps1 @@ -0,0 +1,9 @@ +Import-Module ActiveDirectory
+
+$csv = Get-Content $args[0]
+
+ForEach ($user in $csv) {
+ Enable-ADAccount -Identity $user
+
+ Write-Host $user"'s account has been re-enabled"
+}
diff --git a/AD-powershell-tools/bulk-reset.ps1 b/AD-powershell-tools/bulk-reset.ps1 new file mode 100644 index 0000000..e66aad6 --- /dev/null +++ b/AD-powershell-tools/bulk-reset.ps1 @@ -0,0 +1,24 @@ +Import-Module ActiveDirectory
+
+function Gen-Random-Password {
+ $str = ""
+ for ($i = 0; $i -lt 24 ; $i++) {
+ $rand = Get-Random -Minimum 32 -Maximum 127
+ $str += [char]$rand
+ }
+ $newpwd = ConvertTo-SecureString -String [String]$str -AsPlainText -Force
+ return $newpwd
+}
+
+# Import users from CSV
+$csv = Get-Content $args[0]
+
+ForEach ($user in $csv) {
+ $newPassword = Gen-Random-Password
+
+ # Reset user password.
+ Set-ADAccountPassword -Identity $user -NewPassword $newPassword -Reset
+
+ Write-Host $user"'s password has been reset"
+ Write-Host $newPassword
+}
diff --git a/AD-powershell-tools/inactive-ad-device-report.ps1 b/AD-powershell-tools/inactive-ad-device-report.ps1 new file mode 100644 index 0000000..560a534 --- /dev/null +++ b/AD-powershell-tools/inactive-ad-device-report.ps1 @@ -0,0 +1,57 @@ +# PLEASE READ SCRIPT BEFORE RUNNING
+
+# Based largely on https://activedirectorypro.com/find-remove-old-computer-accounts-active-directory/
+# but changed his brack/object syntax to a string query
+
+# Usage
+# \inactive-ad-device-report.ps1 "OU=Workstations,DC=example,DC=com" "dd/MM/yyyy" [-report] [-disable]
+
+# Report and disable are optional switches to print the results to a CSV
+# and disable the computer accounts, respectively
+
+# A cutoff date and a search base, must be provided.
+
+# All computers with Login times before
+# the cutoff date are included in the results of the report
+
+# The search base is an LDAP filter that must (at a minimum) specify
+# your domain controller. And probably an OU you want to search, like:
+#
+# "OU=Workstations,DC=example,DC=com"
+
+# See here for an example: https://docs.microsoft.com/en-us/powershell/module/activedirectory/get-adcomputer?view=windowsserver2022-ps#example-4--get-computer-accounts-in-a-specific-location-using-an-ldapfilter
+param (
+ [Parameter(Mandatory)][string]$searchbase,
+ [Parameter(Mandatory)][string]$cutoff,
+ [switch]$report,
+ [switch]$disable
+)
+Import-Module ActiveDirectory
+$today=(get-date -Format "yyyy-MM-dd")
+try {
+ $filter = "(LastLogonDate -lt `"$cutoff`") -and (Enabled -eq `"$true`")"
+ $devices = Get-ADcomputer -filter $filter -properties LastLogonDate,Enabled,DistinguishedName `
+ -SearchBase $searchbase `
+ | select name, LastLogonDate, DistinguishedName
+ | sort LastLogonDate
+}
+catch {
+ write-error "Bad input. Usage: '.\inactive-ad-device-report.ps1 `"ldap-filter`" `"dd/MM/yyyy`" [-report] [-disable]'"
+}
+
+if ($disable) {
+ ForEach ($device in $devices) {
+ Set-ADComputer -Identity $device.Name -Enabled $false -Verbose -WhatIf
+ }
+
+ [Console]::Error.WriteLine("All devices disabled")
+}
+
+if($report) {
+ $fn = "old-computers-$today.csv"
+ $devices | export-csv .\$fn
+ [Console]::Error.WriteLine("Saved result list to $fn")
+} else {
+ [Console]::Error.WriteLine("Writing device list to stdout")
+ write-output $devices
+}
diff --git a/AD-powershell-tools/test-ad-credentials.ps1 b/AD-powershell-tools/test-ad-credentials.ps1 new file mode 100644 index 0000000..bd0ba84 --- /dev/null +++ b/AD-powershell-tools/test-ad-credentials.ps1 @@ -0,0 +1,47 @@ +# Adapted from: https://itpro-tips.com/test-ad-authentication-with-powershell/
+# The interesting bit about this one is that it doesn't seem to get logged by AD,
+# so you won't end up with false positives from testing creds
+
+function Test-ADAuthentication {
+ Param(
+ [Parameter(Mandatory)]
+ [string]$User,
+ [Parameter(Mandatory)]
+ $Password,
+ [Parameter(Mandatory = $false)]
+ $Server,
+ [Parameter(Mandatory = $false)]
+ [string]$Domain = $env:USERDOMAIN
+ )
+
+ Add-Type -AssemblyName System.DirectoryServices.AccountManagement
+
+ $contextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
+
+ $argumentList = New-Object -TypeName "System.Collections.ArrayList"
+ $null = $argumentList.Add($contextType)
+ $null = $argumentList.Add($Domain)
+
+ if($null -ne $Server){
+ $argumentList.Add($Server)
+ }
+
+ $principalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $argumentList -ErrorAction SilentlyContinue
+
+ if ($null -eq $principalContext) {
+ Write-Warning "$Domain\$User - AD Authentication failed"
+ }
+
+ if ($principalContext.ValidateCredentials($User, $Password)) {
+ Write-Output "$Domain\$User - AD Authentication OK"
+ }
+ else {
+ Write-Warning "$Domain\$User - AD Authentication failed"
+ }
+}
+
+$csv = Import-Csv $args[0]
+ForEach ($userpass in $csv) {
+ Test-ADAuthentication -User $userpass.user -Password $userpass.password
+}
+
@@ -5,5 +5,8 @@ ### Programs so far
* fhash - intuitive CLI for hashing files
* xls2csv - converts old excel files into a csv format without having to use Excel
-* julia-c - C-based CLI for efficient julia set image rendering because python was too slow.
+* julia-c - C-based CLI for efficient julia set image rendering because python was too slow.
+* AD-powershell-tools - Convenience scripts for managing AD users mostly
+* bitwarden-tools - script to dump bitwarden exports to UNIX pass before I realized Bitwarden had a CLI
+* discord - Discord alerting scripts
diff --git a/bitwarden-tools/README.md b/bitwarden-tools/README.md new file mode 100644 index 0000000..9cb9ded --- /dev/null +++ b/bitwarden-tools/README.md @@ -0,0 +1,2 @@ +(for now) just one script to handle importing Bitwarden data to UNIX +pass diff --git a/bitwarden-tools/bit2pass.py b/bitwarden-tools/bit2pass.py new file mode 100644 index 0000000..bd9fe99 --- /dev/null +++ b/bitwarden-tools/bit2pass.py @@ -0,0 +1,44 @@ +#!/usr/bin/python3 +""" +bit2pass.py - grabs the bare minimum info from a bitwarden JSON export +(unencrypted) to populate a UNIX pass datastore. This assumes you named +your entry and gave it a password, otherwise, this script will yell at +you. + +This does NOT grab notes or usernames. I use pass purely for easy (and +secure) copying of passwords. If I really need the notes, it's probably +not something I'm going to be copying much. I also exclude anything +that's not a login because, well that's what bitwarden's good for... +Don't limit yourself to one tool + + +Usage: +0) (before running) Initialize a pass database: + pass init +1) python bit2pass.py <your-file> +""" +import sys +import subprocess +import json +with open(sys.argv[1]) as f: + data = json.load(f) + +folders = { x['id'] : x['name'] for x in data['folders'] } +passwords = { + folders[x['folderId']] + '/' + x['name'] : + x['login']['password'] + for x in data['items'] + if x['type'] == 1 + } +print(passwords) + +for p in passwords: + echo = subprocess.run(["echo", passwords[p]], + check=True, + capture_output=True + ) + pass2pass = subprocess.run(["pass", "insert", "-e", p], + input=echo.stdout, + capture_output=True + ) + print(pass2pass.stdout) diff --git a/discord/README.md b/discord/README.md new file mode 100644 index 0000000..189bf7d --- /dev/null +++ b/discord/README.md @@ -0,0 +1,10 @@ +Adapted from past discord alerts + +Example usage +```bash +$ echo "Test Alert!" | python discord_alert.py https://url +``` + +https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks + +Be sure to keep your discord webhook private! diff --git a/discord/discord_alert.py b/discord/discord_alert.py new file mode 100755 index 0000000..fc9542c --- /dev/null +++ b/discord/discord_alert.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +# Example usage +# echo "Test Alert!" | python discord_alert.py https://url +# https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks + +import sys +import requests +import argparse + + +def send_update(name, msg, discord_webhook): + """ Send a push to discord webhook url""" + formatted = f"⚠️ ALERT {name}\n\n{msg}" + message = { 'content' : formatted } + sys.stderr.write("Sending request.... ") + r = requests.post(url=discord_webhook, data=message) + sys.stderr.write(f"{r.status_code}\n") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-t", "--title", default="") + parser.add_argument("url") + args = parser.parse_args() + + if not args.url: + sys.stderr.write("A webhook url is required\n") + sys.stderr.write("Usage:\n\n") + sys.stderr.write("python discord_alert.py [-t] <title> <url>\n") + sys.exit(1) + + msg = "" + line = input() + while line: + msg += line + try: + line = input() + except EOFError: + break + send_update(args.title, msg, args.url) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + sys.stderr.write("User stopped program\n") + sys.exit(0) + diff --git a/subtitle-podcast/README.md b/subtitle-podcast/README.md new file mode 100644 index 0000000..16efb17 --- /dev/null +++ b/subtitle-podcast/README.md @@ -0,0 +1,143 @@ +# subtitle-podcast.ps1 + +This script takes an audio file as input and generates a subtitle track, +a static image, and combines them to create a subtitled video of the +audio. I originally made this to transcribe recorded tabletop games for +a friend to allow for easy re-reading. + +Transcription relies entirely on the WhisperX project which uses an +Automatic Speech Regonition (ASR) model original developed by OpenAI. If +you like this script, please consider supporting the WhisperX project +with a donation! + +https://github.com/m-bain/whisperX + +## Requirements +- Powershell +- Python 3.9 to 3.13 +- ffmpeg +- About 5-10 GB of free space for AI models + +## Setup + +### Install Powershell + +Highly suggest downloading the latest Powershell version from github: + +https://github.com/PowerShell/PowerShell/releases + +The latest version as of now: + +https://github.com/PowerShell/PowerShell/releases/download/v7.5.4/PowerShell-7.5.4-win-x64.msi + +### Install Python and pip + +At the time of writing (Jan 2026) WhisperX seems to only work with +python 3.13 and lower. The latest version of Python may or may not work + +https://www.python.org/ftp/python/3.13.11/python-3.13.11-amd64.exe + +Be sure to check "Add to Path" during installation + + + +### Install Whisperx + + +If you used the link above, you should be able to run this on Powershell + +```pwsh +pip.exe install whisperx +``` + +### Install ffmpeg + +1. Download the latest build [here](https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip) + +2. Unzip the folder and rename it to "ffmpeg" to keep it simple. + +3. Move the unzipped ffmpeg folder to a place you'll remember. `C:\Program Files\` + is usually a good bet + +4. Add the path to the ffmpeg "bin" folder to your Path. If you are not familiar with environment variables, you may want to read [this guide](https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/) first + * Search "Path" in the search bar and find the "Environment + variables..." button + * In the new window, double click the item in the list that's + titled "PATH" + * Click "New" and copy the location of the "bin" folder + +If you have it right, your path should look something like this with a different username of course: + + + +Once confirmed, click "OK" on all the windows. + +### Allow execution + +If your Powershell disables execution by default + +```pwsh +Unblock-File subtitle-podcast.ps1 +``` + +## Usage + +Move the script into the directory you have your audio files. Open up +Powershell in that location (right click > Open in Powershell) + +```pwsh +.\subtitle-podcast.ps1 episode01.mp3 +``` + +Place cover files in the same directory as your mp3, with a name like "episode01.jpg" + + +For example, if your files are in a folder called "pods" + +``` +pods\ +---- episode01.mp3 +---- episode01.jpg +---- subtitle-podcast.ps1 +``` + +Only ".png" and ".jpg" extentions are allowed since I'm lazy. + +### Sample output + +https://github.com/user-attachments/assets/c9a8b408-b48b-464a-b386-1eed8a5184c2 + +## Hacking + +### Video format + +This script produces and mp4 file by default since it is decent for web embedding. If you want a different video output, just change line 45 + +```pwsh +$OutputVideo = "$Title.mp4" +``` + +### Accuracy + +WhisperX is still fairly new and actively developed, so do not expect +perfect transcription results. For example, you may find a number of +proper names don't get transcribed well. Grunts, laughter, and other +non-distinct may also produce unexpected results. + +To make this as simple as possible without using any custom models, this +script uses the large English model provided by WhisperX. + +But there are many other models the tool supports, most notably, +other languages. For the most details on this, see the WhipserX github +and make edits accordingly + +https://github.com/m-bain/whisperX?tab=readme-ov-file#other-languages + +A potential hack for missing names would be to bias the prompt of +whisperx with known names that appear in the audio. If you want to +experiment with this you can add the following line after line 38 (be +sure to include the backtick ` at the end) + +```pwsh +--initial_prompt "Names in this broadcast include: Mike Fernèz, Mr. McGuire" ` +``` diff --git a/subtitle-podcast/ffmpeg-path.png b/subtitle-podcast/ffmpeg-path.png Binary files differnew file mode 100644 index 0000000..e533723 --- /dev/null +++ b/subtitle-podcast/ffmpeg-path.png diff --git a/subtitle-podcast/indicted-me.jpg b/subtitle-podcast/indicted-me.jpg Binary files differnew file mode 100644 index 0000000..1937898 --- /dev/null +++ b/subtitle-podcast/indicted-me.jpg diff --git a/subtitle-podcast/indicted-me.mp3 b/subtitle-podcast/indicted-me.mp3 Binary files differnew file mode 100644 index 0000000..10416ae --- /dev/null +++ b/subtitle-podcast/indicted-me.mp3 diff --git a/subtitle-podcast/indicted-me.mp4 b/subtitle-podcast/indicted-me.mp4 Binary files differnew file mode 100755 index 0000000..df660a2 --- /dev/null +++ b/subtitle-podcast/indicted-me.mp4 diff --git a/subtitle-podcast/python-path.png b/subtitle-podcast/python-path.png Binary files differnew file mode 100644 index 0000000..79804ba --- /dev/null +++ b/subtitle-podcast/python-path.png diff --git a/subtitle-podcast/subtitle-podcast.ps1 b/subtitle-podcast/subtitle-podcast.ps1 new file mode 100644 index 0000000..68bb408 --- /dev/null +++ b/subtitle-podcast/subtitle-podcast.ps1 @@ -0,0 +1,67 @@ +<# +Usage: + .\subtitle-podcast.ps1 episode01.mp3 + +Place cover files in the same directory as your mp3, with a name like "episode01.jpg" (PNG is also OK) + +If a cover is not supplied, a black background will be made instead + +Requirements: + - Python in PATH + - ffmpeg in PATH + whispeX installed via pip +#> + +param ( + [Parameter(Mandatory = $true)] + [string]$InputAudio +) + +# https://github.com/m-bain/whisperX/issues/1304#issuecomment-3599061751 +$env:TORCH_FORCE_NO_WEIGHTS_ONLY_LOAD = "true" + +$Title = [System.IO.Path]::GetFileNameWithoutExtension($InputAudio) + +Write-Host "Processing: $InputAudio" +Write-Host "Base title: $Title" + +# ----------------------------- +# Run WhisperX +# ---------------------------- +# https://github.com/m-bain/whisperX/issues/878 +whisperx ` + --compute_type float32 ` + --model large-v2 ` + --align_model WAV2VEC2_ASR_LARGE_LV60K_960H ` + --output_format srt ` + --batch_size 4 ` + --highlight_words True ` + $InputAudio + + +# ----------------------------- +# Create subtitled video +# ----------------------------- +$OutputVideo = "$Title.mp4" + +$Image = @() +if (Test-Path "$Title.jpg") { + $Image += @("-loop", "1", "-i", "$Title.jpg") +} elseif (Test-Path "$Title.png") { + $Image += @("-loop", "1", "-i", "$Title.png") +} else{ + Write-Warning "No image with '$Title' found, making blank background" + $Image += @("-f", "lavfi", "-i", "color=c=black:s=1280x720") +} + + +ffmpeg ` + @Image ` + -i $InputAudio ` + -vf "subtitles=${Title}.srt:force_style='FontSize=28,Alignment=2'" ` + -c:a copy ` + -shortest ` + $OutputVideo + +Write-Host "Done!" +Write-Host "Output: $OutputVideo" |
