Thursday, April 15, 2021

PowerShell Scripts

 Get a list of running applications on windows 10

From Powershell

Get-Process | Where-Object { $_.MainWindowTitle } | Format-Table Name,Mainwindowtitle -AutoSize

or from windows CMD prompt

powershell "Get-Process | Where-Object { $_.MainWindowTitle } | Format-Table Name,Mainwindowtitle -AutoSize"

Tuesday, April 6, 2021

Asterisk Post Call Recording Script configuration

This code shows how to alter a file with the Post Call Recording script.  But in general shows a bit of how the process of making this asterisk feature work

I did worked on trying to get that AGENT ID variable via dialplan, but I just couldn't figure it out in Issabel. The Callcenter system in that is very tied in and I just wasn't able to figure it out. If someone else can show me, that would be fantastic.

I know that is value is stored in the QUEUE. LOG file as each call is sent to an agent. So using the "Post Call Recording Script", at the end of each call, I use a shell script that is automatically activated. This script searches the queue.log file for the unqiue callID and the value "COMPLETEAGENT". This indicates the end of a queue call. The shell parses the entry and pulls out the agent id, Then it renames the sound recording by adding on the AGENTXXXX ID.

So a call that went to queue Agent ID "9989", the recording changes from this:

rg-600-1000-20210331-213521-1617237321.105.wav

and becomes this:

rg-600-1000-20210331-213521-1617237321.105-Agent9989.wav

Here's how i did it:

You don't need any modifications to the Issabel diaplan. The only thing you have to do in Issabel is activate "Post Call Recording Script"

PBX -> "Advanced Settings"

HiddenMenu

You will need to activate "Display Readonly Settings"

and "Override Readonly Settings" Set them both to "TRUE" then click on the Green checkbox on the right side.

You wlll get prompted that you will need to "REFRESH THE PAGE" which you will have to.
Once both boxes are set to "TRUE" and you have pressed the checkbox, click on "ADVANCED SETTINGS" again to refresh.

Now scroll down and you will see

"Post Call Recording Script"

In that box put in this. The quotes around the path and script are required. Asterisk $variables are substituted with a "hat" symbol

HiddenMenu

"/etc/asterisk/updateagent.sh" ^{UNIQUEID} ^{MIXMONITOR_FILENAME}

This is the code that will run after the caller hangsup and the call recording is completed processing.
After each call, the "updateagent.sh" shell script is activated and it passes the UniqueID of the call as well as the current file location

Select the green check mark to the right of this box. Then press APPLY SETTINGS

Now we need to create a shell script.

In /etc/asterisk create a file called "updateagent.sh" (you can of course put this file in your preferential own directory, just make sure the path in the config in Issabel reflects it. You can also change the name.

In side this file you will want to copy in the following into that file and then save it.

#!/bin/bash

#This line finds the agent number in the queue_log file.  It then parses out any extra data to get the number value
#It will find thise 1617240353|1617234166.70|NONE|Agent/9989|AGENTLOGOFF|SIP/1000-00000028|6182 and finish with
#this "9989"
agentnumber=$(grep $1.*COMPLETEAGENT /var/log/asterisk/queue_log | awk -F "|" '{ print $4 }' | awk -F "/" '{ print $2 }')

#this line finds the file type you are using.  They are usually .WAV, but in case its something else we want to account for that
filetype=$(echo $2 | awk -F "." '{ print $3 }')

#This creates a new file name based on the AGENTID and the FileType you are using
newfilename=$(echo $2 | awk -F "." '{ print $1"."$2}')-Agent$agentnumber.$filetype

mv $2 $newfilename
#This rg-600-1000-20210331-213521-1617237321.105.wav
#becomes
#rg-600-1000-20210331-213521-1617237321.105-Agent-9989.wav

Now you will need to make the file executable

chmod u+x updateagent.sh

And now you need to change ownership to allow asterisk PBX to run it

chown asterisk:asterisk updateagent.sh

And that should be it. Make a test call and see if it works. If you have problems, just take out the code in "Post Call Recording Script" and will let you troubleshoot.

MODIFICATION TO PASS OTHER VARIABLES

You can pass additional variables into the script with two modifications

In the Call Record Script you can add more entries by adding them after the MixMonitor entry. Every variable that would be in asterisk needs to have the $ changed to a ? (hat) symbol. So if you wanted to pass $'CONTEXT' and $'EPOCH' data, you can add it. Very important to have "quotes" around the shellscript path. Many examples on internet forget to include this requirement

"/etc/asterisk/updateagent.sh" ^{UNIQUEID} ^{MIXMONITOR_FILENAME} ^{CONTEXT} ^{EPOCH}

THEN in the updateagent.sh script, you just need to alter the "newfilename" entry by adding a $3 and then a $4 like the example below. These would represent the 2 additional values.

newfilename=$(echo $2 | awk -F "." '{ print $1"."$2}')-Agent$agentnumber-$3-$4.$filetype

Save and you should be able to run the file. If the variables are passed correctly, you'll see them appear in the filename, something like this: (DialPlanVar1 and Var2)

rg-600-1000-20210331-230350-1617242630.111-Agent9989-DialPlanVar1-DialPlanVar2.wav

If the data is NOT available, they will just look like this: (you'll see the - that are delimiters)

rg-600-1000-20210331-230350-1617242630.111-Agent--.wav

    Monday, April 5, 2021

    Check GMAIL with a bashscript

    The code below will check a GMAIL account and if it receives a new email, it will trigger the Issabel PBX to make a phone call. This might be useful to some people. This code will check the GMAIL inbox for email.

    I setup a GMAIL account, but I had to turn down the security

    in the Gmail account to let a shell script access it. This script doesn't have much intelligence, so it can't really tell the difference between an alarm email or a regular email, so use a dedicated GMAIL account.

    gmailsecurity

    Now create a shell script called " check_email.sh " on the PBX


    #!/bin/bash username="Gmail-Name" password="Gmail-Password"

    curl -u $username:$password --silent "https://mail.google.com/mail/feed/atom" > emailresult
    
    cat emailresult | grep -oPm1 "(?<=<title>)[^<]+" | sed '1d' > title
    cat emailresult | grep -oPm1 "(?<=<modified>)[^<]+" | sed '1d' >  date
    sed -e 's/^/|/' -i date
    paste title date > merge
    
    diff merge lastmerge
    if [ $? -ne 0 ]; then
        echo "Channel: IAX2/T28_Y02_Y07/5551234" > /etc/asterisk/alarm.call
        echo "MaxRetries: 2" >> /etc/asterisk/alarm.call
        echo "RetryTime: 60" >> /etc/asterisk/alarm.call
        echo "WaitTime: 30" >> /etc/asterisk/alarm.call
        echo "application: Playback" >> /etc/asterisk/alarm.call
        echo "data: /path/to/sound/file.wav" >> /etc/asterisk/alarm.call
        chmod 777 /etc/asterisk/alarm.call
        chown asterisk:asterisk /etc/asterisk/alarm.call
        mv /etc/asterisk/alarm.call /var/spool/asterisk/outgoing/
    
    else
        echo "No New Email"
    
    fi
    
    mv merge -f lastmerge

    change "IAX2/T28_Y02_Y07/5551234" to be the outbound trunk and the phone number you want the system to call.
    

    data: /path/to/sound/file.wav - this is the recording you want the system to play when the person answers. "THIS IS AN ALARM, PLEASE CHECK SYSTEM"

    Make the file executable

    chmod u+x check_email.sh

    Now run the file

     ./check_email.sh

    The system

    checks GMAIL It looks at the list of emails in the

    INBOX and compares it to the list that was there previous If the list is the same, it doesn't do anything. If its different, it will generate a call file and ring the number in the script.

    Now create a CRON job and run this over a period of time. I wouldn't do it more than eleven every 5 mins, I'm not sure if GMAIL will throttle you or not

    The script finds the emails and the dates of each email, merges them together. Perhaps someone more proficient at parsing HTML code can make it do more. I might work on it more myself later. But something to try.

    The "GREP" statement I got from https://linuxconfig.org/check-your-gmail-inbox-for-new-emails-with-bash-script, that site might give you more information how to make things work.

    Friday, March 19, 2021

    Autohotkey FTP code (reference)

    This is reference location.   https://www.autohotkey.com/boards/viewtopic.php?t=79142

    Written by user jNizM (github )

    Mirror: Release on GitHub

    Code: Select all - Collapse View - Toggle Line numbers

    ; ===============================================================================================================================
    ; AutoHotkey wrapper for FTP Sessions
    ;
    ; Author ....: jNizM
    ; Released ..: 2020-07-26
    ; Modified ..: 2020-07-31
    ; Github ....: https://github.com/jNizM/Class_FTP
    ; Forum .....: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=79142
    ; ===============================================================================================================================
    
    
    class FTP
    {
    
    	static hWININET := DllCall("LoadLibrary", "str", "wininet.dll", "ptr")
    
    
    	; ===== PUBLIC METHODS ======================================================================================================
    
    	Close(hInternet)
    	{
    		if (hInternet)
    			if (this.InternetCloseHandle(hInternet))
    				return true
    		return false
    	}
    
    
    	Connect(hInternet, ServerName, Port := 21, UserName := "", Password := "", FTP_PASV := 1)
    	{
    		if (hConnect := this.InternetConnect(hInternet, ServerName, Port, UserName, Password, FTP_PASV))
    			return hConnect
    		return false
    	}
    
    
    	CreateDirectory(hConnect, Directory)
    	{
    		if (DllCall("wininet\FtpCreateDirectory", "ptr", hConnect, "ptr", &Directory))
    			return true
    		return false
    	}
    
    
    	DeleteFile(hConnect, FileName)
    	{
    		if (DllCall("wininet\FtpDeleteFile", "ptr", hConnect, "ptr", &FileName))
    			return true
    		return false
    	}
    
    
    	Disconnect(hConnect)
    	{
    		if (hConnect)
    			if (this.InternetCloseHandle(hConnect))
    				return true
    		return false
    	}
    
    
    	FindFiles(hConnect, SearchFile := "*.*")
    	{
    		static FILE_ATTRIBUTE_DIRECTORY := 0x10
    
    		Files := []
    		find := this.FindFirstFile(hConnect, hEnum, SearchFile)
    		if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
    			Files.Push(find)
    
    		while (find := this.FindNextFile(hEnum))
    			if !(find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
    				Files.Push(find)
    		this.Close(hEnum)
    		return Files
    	}
    
    
    	FindFolders(hConnect, SubDirectories := "*.*")
    	{
    		static FILE_ATTRIBUTE_DIRECTORY := 0x10
    
    		Folders := []
    		find := this.FindFirstFile(hConnect, hEnum, SubDirectories)
    		if (find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
    			Folders.Push(find)
    		while (find := this.FindNextFile(hEnum))
    			if (find.FileAttr & FILE_ATTRIBUTE_DIRECTORY)
    				Folders.Push(find)
    		this.Close(hEnum)
    		return Folders
    	}
    
    
    	GetCurrentDirectory(hConnect)
    	{
    		static MAX_PATH := 260 + 8
    
    		BUFFER_SIZE := VarSetCapacity(CurrentDirectory, MAX_PATH, 0)
    		if (DllCall("wininet\FtpGetCurrentDirectory", "ptr", hConnect, "ptr", &CurrentDirectory, "uint*", BUFFER_SIZE))
    			return StrGet(&CurrentDirectory)
    		return false
    	}
    
    
    	GetFile(hConnect, RemoteFile, NewFile, OverWrite := 0, Flags := 0)
    	{
    		if (DllCall("wininet\FtpGetFile", "ptr", hConnect, "ptr", &RemoteFile, "ptr", &NewFile, "int", !OverWrite, "uint", 0, "uint", Flags, "uptr", 0))
    			return true
    		return false
    	}
    
    
    	GetFileSize(hConnect, FileName, SizeFormat := "auto", SizeSuffix := false)
    	{
    		static GENERIC_READ := 0x80000000
    
    		if (hFile := this.OpenFile(hConnect, FileName, GENERIC_READ))
    		{
    			VarSetCapacity(FileSizeHigh, 8)
    			if (FileSizeLow := DllCall("wininet\FtpGetFileSize", "ptr", hFile, "uint*", FileSizeHigh, "uint"))
    			{
    				this.InternetCloseHandle(hFile)
    				return this.FormatBytes(FileSizeLow + (FileSizeHigh << 32), SizeFormat, SizeSuffix)
    			}
    			this.InternetCloseHandle(hFile)
    		}
    		return false
    	}
    
    
    	Open(Agent, Proxy := "", ProxyBypass := "")
    	{
    		if (hInternet := this.InternetOpen(Agent, Proxy, ProxyBypass))
    			return hInternet
    		return false
    	}
    
    
    	PutFile(hConnect, LocaleFile, RemoteFile, Flags := 0)
    	{
    		if (DllCall("wininet\FtpPutFile", "ptr", hConnect, "ptr", &LocaleFile, "ptr", &RemoteFile, "uint", Flags, "uptr", 0))
    			return true
    		return false
    	}
    
    
    	RemoveDirectory(hConnect, Directory)
    	{
    		if (DllCall("wininet\FtpRemoveDirectory", "ptr", hConnect, "ptr", &Directory))
    			return true
    		return false
    	}
    
    
    	RenameFile(hConnect, ExistingFile, NewFile)
    	{
    		if (DllCall("wininet\FtpRenameFile", "ptr", hConnect, "ptr", &ExistingFile, "ptr", &NewFile))
    			return true
    		return false
    	}
    
    
    	SetCurrentDirectory(hconnect, Directory)
    	{
    		if (DllCall("wininet\FtpSetCurrentDirectory", "ptr", hConnect, "ptr", &Directory))
    			return true
    		return false
    	}
    
    
    	; ===== PRIVATE METHODS =====================================================================================================
    
    	FileAttributes(Attributes)
    	{
    		static FILE_ATTRIBUTE := { 0x1: "READONLY", 0x2: "HIDDEN", 0x4: "SYSTEM", 0x10: "DIRECTORY", 0x20: "ARCHIVE", 0x40: "DEVICE", 0x80: "NORMAL"
    						, 0x100: "TEMPORARY", 0x200: "SPARSE_FILE", 0x400: "REPARSE_POINT", 0x800: "COMPRESSED", 0x1000: "OFFLINE"
    						, 0x2000: "NOT_CONTENT_INDEXED", 0x4000: "ENCRYPTED", 0x8000: "INTEGRITY_STREAM", 0x10000: "VIRTUAL"
    						, 0x20000: "NO_SCRUB_DATA", 0x40000: "RECALL_ON_OPEN", 0x400000: "RECALL_ON_DATA_ACCESS" }
    		GetFileAttributes := []
    		for k, v in FILE_ATTRIBUTE
    			if (k & Attributes)
    				GetFileAttributes.Push(v)
    		return GetFileAttributes
    	}
    
    
    	FindData(ByRef WIN32_FIND_DATA, SizeFormat := "auto", SizeSuffix := false)
    	{
    		static MAX_PATH := 260
    		static MAXDWORD := 0xffffffff
    
    		addr := &WIN32_FIND_DATA
    		FIND_DATA := []
    		FIND_DATA["FileAttr"]          := NumGet(addr + 0, "uint")
    		FIND_DATA["FileAttributes"]    := this.FileAttributes(NumGet(addr + 0, "uint"))
    		FIND_DATA["CreationTime"]      := this.FileTime(NumGet(addr +  4, "uint64"))
    		FIND_DATA["LastAccessTime"]    := this.FileTime(NumGet(addr + 12, "uint64"))
    		FIND_DATA["LastWriteTime"]     := this.FileTime(NumGet(addr + 20, "uint64"))
    		FIND_DATA["FileSize"]          := this.FormatBytes((NumGet(addr + 28, "uint") * (MAXDWORD + 1)) + NumGet(addr + 32, "uint"), SizeFormat, SizeSuffix)
    		FIND_DATA["FileName"]          := StrGet(addr + 44, "utf-16")
    		FIND_DATA["AlternateFileName"] := StrGet(addr + 44 + MAX_PATH * (A_IsUnicode ? 2 : 1), "utf-16")
    		return FIND_DATA
    	}
    
    
    	FindFirstFile(hConnect, ByRef hFind, SearchFile := "*.*", SizeFormat := "auto", SizeSuffix := false)
    	{
    		VarSetCapacity(WIN32_FIND_DATA, (A_IsUnicode ? 592 : 320), 0)
    		if (hFind := DllCall("wininet\FtpFindFirstFile", "ptr", hConnect, "str", SearchFile, "ptr", &WIN32_FIND_DATA, "uint", 0, "uint*", 0))
    			return this.FindData(WIN32_FIND_DATA, SizeFormat, SizeSuffix)
    		VarSetCapacity(WIN32_FIND_DATA, 0)
    		return false
    	}
    
    
    	FindNextFile(hFind, SearchFile := "*.*", SizeFormat := "auto", SizeSuffix := false)
    	{
    		VarSetCapacity(WIN32_FIND_DATA, (A_IsUnicode ? 592 : 320), 0)
    		if (DllCall("wininet\InternetFindNextFile", "ptr", hFind, "ptr", &WIN32_FIND_DATA))
    			return this.FindData(WIN32_FIND_DATA, SizeFormat, SizeSuffix)
    		VarSetCapacity(WIN32_FIND_DATA, 0)
    		return false
    	}
    
    
    	FileTime(addr)
    	{
    		this.FileTimeToSystemTime(addr, SystemTime)
    		this.SystemTimeToTzSpecificLocalTime(&SystemTime, LocalTime)
    		return Format("{:04}{:02}{:02}{:02}{:02}{:02}"
    					, NumGet(LocalTime,  0, "ushort")
    					, NumGet(LocalTime,  2, "ushort")
    					, NumGet(LocalTime,  6, "ushort")
    					, NumGet(LocalTime,  8, "ushort")
    					, NumGet(LocalTime, 10, "ushort")
    					, NumGet(LocalTime, 12, "ushort"))
    	}
    
    
    	FileTimeToSystemTime(FileTime, ByRef SystemTime)
    	{
    		VarSetCapacity(SystemTime, 16, 0)
    		if (DllCall("FileTimeToSystemTime", "int64*", FileTime, "ptr", &SystemTime))
    			return true
    		return false
    	}
    
    
    	FormatBytes(bytes, SizeFormat := "auto", suffix := false)
    	{
    		static SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT    := 0x0001
    		static SFBS_FLAGS_TRUNCATE_UNDISPLAYED_DECIMAL_DIGITS := 0x0002
    		static S_OK := 0
    
    		if (SizeFormat = "auto")
    		{
    			size := VarSetCapacity(buf, 1024, 0)
    			if (DllCall("shlwapi\StrFormatByteSizeEx", "int64", bytes, "int", SFBS_FLAGS_ROUND_TO_NEAREST_DISPLAYED_DIGIT, "str", buf, "uint", size) = S_OK)
    				output := buf
    		}
    		else if (SizeFormat = "kilobytes" || SizeFormat = "kb")
    			output := Round(bytes / 1024, 2) . (suffix ? " KB" : "")
    		else if (SizeFormat = "megabytes" || SizeFormat = "mb")
    			output := Round(bytes / 1024**2, 2) . (suffix ? " MB" : "")
    		else if (SizeFormat = "gigabytes" || SizeFormat = "gb")
    			output := Round(bytes / 1024**3, 2) . (suffix ? " GB" : "")
    		else if (SizeFormat = "terabytes" || SizeFormat = "tb")
    			output := Round(bytes / 1024**4, 2) . (suffix ? " TB" : "")
    		else
    			output := Round(bytes, 2) . (suffix ? " Bytes" : "")
    		return output
    	}
    
    
    	InternetCloseHandle(hInternet)
    	{
    		if (DllCall("wininet\InternetCloseHandle", "ptr", hInternet))
    			return true
    		return false
    	}
    
    
    	InternetConnect(hInternet, ServerName, Port := 21, UserName := "", Password := "", FTP_PASV := 1)
    	{
    		static INTERNET_DEFAULT_FTP_PORT := 21
    		static INTERNET_SERVICE_FTP      := 1
    		static INTERNET_FLAG_PASSIVE     := 0x08000000
    
    		if (hConnect := DllCall("wininet\InternetConnect", "ptr",    hInternet
    														 , "ptr",    &ServerName
    														 , "ushort", (Port = 21 ? INTERNET_DEFAULT_FTP_PORT : Port)
    														 , "ptr",    (UserName ? &UserName : 0)
    														 , "ptr",    (Password ? &Password : 0)
    														 , "uint",   INTERNET_SERVICE_FTP
    														 , "uint",   (FTP_PASV ? INTERNET_FLAG_PASSIVE : 0)
    														 , "uptr",   0
    														 , "ptr"))
    			return hConnect
    		return false
    	}
    
    
    	InternetOpen(Agent, Proxy := "", ProxyBypass := "")
    	{
    		static INTERNET_OPEN_TYPE_DIRECT := 1
    		static INTERNET_OPEN_TYPE_PROXY  := 3
    
    		if (hInternet := DllCall("wininet\InternetOpen", "ptr",  &Agent
    													   , "uint", (Proxy ? INTERNET_OPEN_TYPE_PROXY : INTERNET_OPEN_TYPE_DIRECT)
    													   , "ptr",  (Proxy ? &Proxy : 0)
    													   , "ptr",  (ProxyBypass ? &ProxyBypass : 0)
    													   , "uint", 0
    													   , "ptr"))
    			return hInternet
    		return false
    	}
    
    
    	OpenFile(hConnect, FileName, Access)
    	{
    		static FTP_TRANSFER_TYPE_BINARY := 2
    
    		if (hFTPSESSION := DllCall("wininet\FtpOpenFile", "ptr", hConnect, "ptr", &FileName, "uint", Access, "uint", FTP_TRANSFER_TYPE_BINARY, "uptr", 0))
    			return hFTPSESSION
    		return false
    	}
    
    
    	SystemTimeToTzSpecificLocalTime(SystemTime, ByRef LocalTime)
    	{
    		VarSetCapacity(LocalTime, 16, 0)
    		if (DllCall("SystemTimeToTzSpecificLocalTime", "ptr", 0, "ptr", SystemTime, "ptr", &LocalTime))
    			return true
    		return false
    	}
    
    }
    
    ; ===============================================================================================================================

    Then us this code to run it

    Writes a file to the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.PutFile(hSession, "C:\Temp\testfile.txt", "testfile.txt")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Retrieves a file from the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.GetFile(hSession, "testfile.txt", "C:\Temp\testfile.txt")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Retrieves the file size of the requested FTP resource.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    MsgBox % FTP.GetFileSize(hSession, "testfile.txt")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Deletes a file from the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.DeleteFile(hSession, "testfile.txt")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Creates a new directory on the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.CreateDirectory(hSession, "Test_Folder")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Changes the client's current directory on the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.SetCurrentDirectory(hSession, "Test_Folder")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Returns the client's current directory on the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    MsgBox % FTP.GetCurrentDirectory(hSession)
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Deletes a directory on the server.

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    FTP.RemoveDirectory(hSession, "Test_Folder")
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Enumerate all Files in root directory. (!!! EXPERIMENTAL !!!)

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    for k, File in FTP.FindFiles(hSession)
    	MsgBox % File.FileName
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Enumerate all Files in a subdirectory. (!!! EXPERIMENTAL !!!)

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    for k, File in FTP.FindFiles(hSession, "/Folder 2")
    	MsgBox % File.FileName
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Enumerate all Folders in root directory. (!!! EXPERIMENTAL !!!)

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    for k, Folder in FTP.FindFolders(hSession)
    	MsgBox % Folder.FileName
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)

    Enumerate all Folders in a subdirectory. (!!! EXPERIMENTAL !!!)

    Code: Select all - Toggle Line numbers

    hFTP := FTP.Open("AHK-FTP")
    hSession := FTP.Connect(hFTP, "ftp.example.com", 21, "user", "passwd")
    for k, Folder in FTP.FindFolders(hSession, "/Folder 2")
    	MsgBox % Folder.FileName
    FTP.Disconnect(hSession)
    FTP.Close(hFTP)