an HTTP GET or POST call to URL

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

an HTTP GET or POST call to URL

05 Feb 2020, 16:53

I want to create a form in AHK that can send SMS.
has received the following information from the SMS provider:
SMS is sent by making an HTTP GET or POST call to URL https://se-1.cellsynt.net/sms.phpebrit
How do I make the call to URL above with AHK?
username .: Username to the provider
password .: Password to the provider
type .: Message type, e.g. text
destination .: Receiver, in format 0046700123123 (in Sweden)
originator type .: Transmitter type, e.g. numeric
originator .: transmitter, e.g. 0046700123123 for numeric transmitter (in Sweden)
charset. .: Character set of parameter values are specified in either UTF-8 or ISO-8859-1 (default)
text.: The SMS message ...

An example on a SMS-message from AHK .: (a space is in https to display the entire URL-string)
http s://se-1.cellsynt.net/sms.php?username=demo&password=test123&destination=0046700123123&originatortype=numeric&originator=46700456456&charset=UTF-8&text=Test+123
User avatar
Masonjar13
Posts: 1555
Joined: 20 Jul 2014, 10:16
Location: Не Россия
Contact:

Re: an HTTP GET or POST call to URL

05 Feb 2020, 17:17

Build the post data with CreateFormData, then use WinHttp.WinHttpRequest.5.1 to send the data.

Code: Select all

endpoint:="se-1.cellsynt.net/sms.php" ; url pointing to the API endpoint
data:={"username":"demo","password":"test123"} ; key-val data to be posted

try{ ; only way to properly protect from an error here
	createFormData(rData,rHeader,data) ; formats the data, stores in rData, header info in rHeader
	hObject:=comObjectCreate("WinHttp.WinHttpRequest.5.1") ; create WinHttp object
	hObject.setRequestHeader("Content-Type",rHeader) ; set content header
	hObject.open("POST",endpoint) ; open a post event to the specified endpoint
	hObject.send(rData) ; send request with data

	; you can get the response data either in raw or text format
	; raw: hObject.responseBody
	; text: hObject.responseText	
}catch e{
	return e.message
}
OS: Windows 10 Pro | Editor: Notepad++
My Personal Function Library | Old Build - New Build
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

06 Feb 2020, 05:28

Thanks! (trying to understand what's going on in the code)

When I tried to use the code above, an error occurred.
Error: Return's parameter should be blank except inside a function.

Line#
040: hObject := comObjectCreate("WinHttp.WinHttpRequest.5.1")
041: hObject.setRequestHeader("Content-Type", rHeader)
042: hObject.open("POST", endpoint)
043: hObject.send(rData)
048: }
048: Catch,e
048: {
---> 049: Return,e.message

The program will exit.
Why? / What to do?
Is the command Try better to use than ErrorLevel?

[edit - information added]
API Manual as PDF

A PHP-example .:

Code: Select all

<?php
/**
 * API integration with Cellsynt to send SMS
 */

// Build HTTP GET query string
$httpGetParameters = http_build_query([
    // Username
    'username'       => 'demo',
    // Password
    'password'       => 'test123',

    // Recipient's phone number on international format
    'destination'    => '0046700123123',

    // Message type
    'type'           => 'text',
    // Message text
    'text'           => 'Test 123 åäöÅÄÖ!',
    // Character set for request (UTF-8 or ISO-8859-1)
    'charset'        => 'UTF-8',

    // Message originator (alpha = alphanumeric, numeric or shortcode = operator shortcode)
    'originatortype' => 'alpha',
    // Message originator
    'originator'     => 'Demo',
]);

// Perform HTTP request and store response in $response
$response = file_get_contents('https://se-1.cellsynt.net/sms.php' . '?' . $httpGetParameters);

// Tolka svar
if ($response === false) {
    echo 'Unable to perform request.' . PHP_EOL;
} elseif (strpos($response, 'OK: ') === 0) {
    $trackingid = substr($response, 4);
    echo 'SMS sent with trackingid ' . $trackingid . PHP_EOL;
} elseif (strpos($response, 'Error: ') === 0) {
    echo 'An error occured, the server sent the following response: ' . $response . PHP_EOL;
}

guest3456
Posts: 3462
Joined: 09 Oct 2013, 10:31

Re: an HTTP GET or POST call to URL

06 Feb 2020, 09:19

Albireo wrote:
06 Feb 2020, 05:28
Thanks! (trying to understand what's going on in the code)

When I tried to use the code above, an error occurred.
Error: Return's parameter should be blank except inside a function.
Why? / What to do?
just change it to msgbox, % e.message

Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

06 Feb 2020, 10:51

Thanks!
I have the code from CreateFormData.ahk included in my SMS-test script.
Now I got another Error in the Class CreateFormData
Error: Call to nonexistent function.

Specifically: BinArr_FromString(str) )

Line#
098: For k,v in objParam
099: {
100: if IsObject(v)
100: {
101: For i,FileName in v
102: {
103: str := BoundaryLine . CRLF . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF
---> 106: fileArrs.Push( BinArr_FromString(str) )
107: fileArrs.Push( BinArr_FromFile(FileName) )
108: fileArrs.Push( BinArr_FromString(CRLF) )
109: }
110: }
110: Else
110: {
111: str := BoundaryLine . CRLF . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF . v . CRLF

The program will exit.
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

06 Feb 2020, 10:58

Excuse me!
I forgot BinArr.ahk
User avatar
Masonjar13
Posts: 1555
Joined: 20 Jul 2014, 10:16
Location: Не Россия
Contact:

Re: an HTTP GET or POST call to URL

06 Feb 2020, 11:45

Sorry about that error, I was pulling that out from a class method, forgot that one return ;)
Albireo wrote:
06 Feb 2020, 05:28
Is the command Try better to use than ErrorLevel?
ErrorLevel doesn't actually do anything, it just allows you to get the error if a command sets one. A try block will not throw any visual/breaking errors, rather, if an error is encountered, it jumps to the respective catch block with an error object, which you can then do whatever you want. Or, you could exclude the catch block and just suppress any errors, but that isn't the best idea.

If you want to try it, run the code when you're offline, with and without the try block.
OS: Windows 10 Pro | Editor: Notepad++
My Personal Function Library | Old Build - New Build
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

06 Feb 2020, 14:12

I see- Try is much better than ErrorLevel in this case.

But now I got this error message .: 0x800401E3 - Åtgärden är inte tillgänglig (perhaps from Windows)
Maybe translated to Operation unavailable or The action is not available on the catch e-instruction below.

Code: Select all

catch e {
	MsgBox 64, %A_ScriptName% - Row %A_LineNumber%, % e.message


Now I'm testing this code (with correct user / password / phone number and so on)
With both CreateFormData.ahk and BinArr.ahk included in the code .:

Code: Select all

; 1. CreateFormData (Build the PostData )
;
; 2. WinHttpRequest object (to send the data)
; https://docs.microsoft.com/en-us/windows/win32/winhttp/winhttprequest
;

endpoint := "se-1.cellsynt.net/sms.php"	 ; url pointing to the API endpoint
UserName := "user"
Pass 	:= "password"

FromType := "numeric"
From 	:= "46701234567"
CharSet := "UTF-8"

ToNumber := "0046701234567"
Message := "Test+123"


data := {"username":UserName,"password":Pass,"destination":ToNumber,"originatortype":FromType,"originator":From,"charset":CharSet,"text":Message} ; Data to be posted
; MsgBox ,, %A_ScriptName% - Row %A_LineNumber%, % "endpoint .: " endpoint "`ndata.username .: " data.username "`ndata.password .: " data.password "`ndata.destination .: " data.destination


try{ ; only way to properly protect from an error here
	createFormData(rData, rHeader, data) ; formats the data, stores in rData, header info in rHeader
	hObject := comObjectCreate("WinHttp.WinHttpRequest.5.1") ; create WinHttp object
	hObject.setRequestHeader("Content-Type", rHeader) ; set content header
	hObject.open("POST", endpoint) ; open a post event to the specified endpoint
	hObject.send(rData) ; send request with data

	; you can get the response data either in raw or text format
	; raw: hObject.responseBody
	; text: hObject.responseText	
} catch e {
	MsgBox 64, %A_ScriptName% - Row %A_LineNumber%, % e.message
	; return e.message
}


; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; - - - - - - - - - - - - CreateFormData.ahk - - - - - - - - - - - - - - - - - - - - -
/*
	Version 6 feb 2020
	From .: https://gist.github.com/tmplinshi/8428a280bba58d25ef0b
	By .: tmplinshi
	CreateFormData - Creates "multipart/form-data" for http post
	https://www.autohotkey.com/boards/viewtopic.php?t=7647
	Usage: CreateFormData(ByRef retData, ByRef retHeader, objParam)
		retData   - (out) Data used for HTTP POST.
		retHeader - (out) Content-Type header used for HTTP POST.
		objParam  - (in)  An object defines the form parameters.
		            To specify files, use array as the value. Example:
		                objParam := { "key1": "value1"
		                            , "upload[]": ["1.png", "2.png"] }
	Requirements: BinArr.ahk -- https://gist.github.com/tmplinshi/a97d9a99b9aa5a65fd20
	Version    : 1.30 / 2019-01-13 - The file parameters are now placed at the end of the retData
	             1.20 / 2016-06-17 - Added CreateFormData_WinInet(), which can be used for VxE's HTTPRequest().
	             1.10 / 2015-06-23 - Fixed a bug
	             1.00 / 2015-05-14
*/

; Used for WinHttp.WinHttpRequest.5.1, Msxml2.XMLHTTP ...
CreateFormData(ByRef retData, ByRef retHeader, objParam) {
	New CreateFormData(retData, retHeader, objParam)
}

; Used for WinInet
CreateFormData_WinInet(ByRef retData, ByRef retHeader, objParam) {
	New CreateFormData(safeArr, retHeader, objParam)

	size := safeArr.MaxIndex() + 1
	VarSetCapacity(retData, size, 1)
	DllCall("oleaut32\SafeArrayAccessData", "ptr", ComObjValue(safeArr), "ptr*", pdata)
	DllCall("RtlMoveMemory", "ptr", &retData, "ptr", pdata, "ptr", size)
	DllCall("oleaut32\SafeArrayUnaccessData", "ptr", ComObjValue(safeArr))
}

Class CreateFormData {
	__New(ByRef retData, ByRef retHeader, objParam) {

		CRLF := "`r`n"

		Boundary := this.RandomBoundary()
		BoundaryLine := "------------------------------" . Boundary

		; Loop input paramters
		binArrs := []
		fileArrs := []
		For k, v in objParam
		{	If IsObject(v) {
				For i, FileName in v
				{
					str := BoundaryLine . CRLF
					     . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF
					     . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF
					fileArrs.Push( BinArr_FromString(str) )
					fileArrs.Push( BinArr_FromFile(FileName) )
					fileArrs.Push( BinArr_FromString(CRLF) )
				}
			} Else {
				str := BoundaryLine . CRLF
				     . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF
				     . v . CRLF
				binArrs.Push( BinArr_FromString(str) )
			}
		}

		binArrs.push( fileArrs* )

		str := BoundaryLine . "--" . CRLF
		binArrs.Push( BinArr_FromString(str) )

		retData := BinArr_Join(binArrs*)
		retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
	}

	RandomBoundary() {
		str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
		Sort, str, D| Random
		str := StrReplace(str, "|")
		Return SubStr(str, 1, 12)
	}

	MimeType(FileName) {
		n := FileOpen(FileName, "r").ReadUInt()
		Return (n        = 0x474E5089) ? "image/png"
		     : (n        = 0x38464947) ? "image/gif"
		     : (n&0xFFFF = 0x4D42    ) ? "image/bmp"
		     : (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
		     : (n&0xFFFF = 0x4949    ) ? "image/tiff"
		     : (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
		     : "application/octet-stream"
	}

}

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; - - - - - - - - - - - - BinArr.ahk  - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Update: 2015-6-4 - Added BinArr_ToFile()
; https://gist.github.com/tmplinshi/a97d9a99b9aa5a65fd20
; By tmplinshi
BinArr_FromString(str) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 2 ; adTypeText
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	oADO.Charset := "UTF-8"
	oADO.WriteText(str)

	oADO.Position := 0
	oADO.Type := 1 ; adTypeBinary
	oADO.Position := 3 ; Skip UTF-8 BOM
	return oADO.Read, oADO.Close
}

BinArr_FromFile(FileName) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Open
	oADO.LoadFromFile(FileName)
	return oADO.Read, oADO.Close
}

BinArr_Join(Arrays*) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	For i, arr in Arrays
		oADO.Write(arr)
	oADO.Position := 0
	return oADO.Read, oADO.Close
}

BinArr_ToString(BinArr, Encoding := "UTF-8") {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Mode := 3 ; adModeReadWrite
	oADO.Open
	oADO.Write(BinArr)

	oADO.Position := 0
	oADO.Type := 2 ; adTypeText
	oADO.Charset  := Encoding 
	return oADO.ReadText, oADO.Close
}

BinArr_ToFile(BinArr, FileName) {
	oADO := ComObjCreate("ADODB.Stream")

	oADO.Type := 1 ; adTypeBinary
	oADO.Open
	oADO.Write(BinArr)
	oADO.SaveToFile(FileName, 2)
	oADO.Close
}
Is it easy / possible to see what will be sent to the URL endpoint?
(e.g. hObject / rHeader / rData)
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

06 Feb 2020, 19:30

I think there must be another solution for me?

If I copy the string below, with the correct username / password / phone number and so on, to a browser (in my case FireFox),
a SMS with the text Test+123 is sent to me. (no space in https://...)
http s://se-1.cellsynt.net/sms.php?username=demo&password=test123&destination=0046700123123&originatortype=numeric&originator=46700456456&charset=UTF-8&text=Test+123
On the browser I got the message .: OK: 6414a917729df3f1acbd7bdf4e340bbc (OK: + trackingid on the SMS)
With the trackingid I can get the status of the delivery of the SMS.

But first I need to be able to send a HTTPS- message (without first opening a browser and then copying the text into the browser)
Is it possible with AHK?

Previously, I showed a program, written in PHP, as on a few lines solve this.
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: an HTTP GET or POST call to URL

06 Feb 2020, 23:11

You can make an HTTP GET or HTTP POST request, according to its API.

GET request

Code: Select all

whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("GET", "https://se-1.cellsynt.net/sms.php?username=demo&password=test123&destination=0046700123123&originatortype=numeric&originator=46700456456&charset=UTF-8&text=Test+123", true)
whr.Send()
; Using 'true' above and the call below allows the script to remain responsive.
whr.WaitForResponse()
MsgBox % whr.ResponseText
POST request

Code: Select all

whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://se-1.cellsynt.net/sms.php", true)
whr.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
whr.Send("username=demo&password=test123&destination=0046700123123&originatortype=numeric&originator=46700456456&charset=UTF-8&text=Test+123")
whr.WaitForResponse()
MsgBox % whr.ResponseText
CreateFormData.ahk is used for the content-type "multipart/form-data", which is normally used when the form contains a file to upload.
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL (Solved!)

07 Feb 2020, 07:29

Great! thanks!
It was this solution I was looking for - both methods seem to work perfectly
Albireo
Posts: 1748
Joined: 16 Oct 2013, 13:53

Re: an HTTP GET or POST call to URL

07 Feb 2020, 07:44

Is it better to use Try / Catch in this case (as in this example below?)
What problems will be catched? (error from WinHttp.WinHttpRequest.5.1? or ...)

Code: Select all

try{ ; only way to properly protect from an error here
	whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	whr.Open("POST", URL, true)
	whr.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
	whr.Send("username=" User "&password=" Pass "&destination=" ToPhone "&originatortype=" SenderType "&originator=" Sender "&charset=" ChrSet "&allowconcat=" MaxSMS "&text=" Message)
	whr.WaitForResponse()
	
	aMess := StrSplit(whr.ResponseText, A_Space, """")
	MsgBox ,, %A_ScriptName% - Row %A_LineNumber%, % "POST request .: `n1) Result .: " aMess[1] "`n2) trackingid .: " aMess[2] 
} catch e {
	MsgBox 64, %A_ScriptName% - Row %A_LineNumber%, % e.message
	; return e.message
}
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: an HTTP GET or POST call to URL

07 Feb 2020, 07:57

Yes. There will be errors like timeout, etc..
User avatar
SirSocks
Posts: 360
Joined: 26 Oct 2018, 08:14

Re: an HTTP GET or POST call to URL

07 Jun 2020, 09:38

Thank you @tmplinshi for the POST request example.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: an HTTP GET or POST call to URL

07 Jun 2020, 15:53

tmplinshi wrote:
06 Feb 2020, 23:11
You can make an HTTP GET or HTTP POST request, according to its API.
@tmplinshi Once I saw your post I got the idea that it could be used to accomplish the final request ("Using the refresh token to get an access token") for Google-Drive as it is described here: https://martinfowler.com/articles/command-line-google.html#OutlineFlowForAuthorization
I've downloaded your http.ahk from GitHub but have no idea how to use it in this case? Probably you can shed some light on this. Thx :)
tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: an HTTP GET or POST call to URL

08 Jun 2020, 05:23

@BoBo The simple answer to the question How to generate access token using refresh token through google drive API?:
If you are using web api then you should make a http POST call to URL : https://www.googleapis.com/oauth2/v4/token with following request body

client_id: <YOUR_CLIENT_ID>
client_secret: <YOUR_CLIENT_SECRET>
refresh_token: <REFRESH_TOKEN_FOR_THE_USER>
grant_type: refresh_token
So:

Code: Select all

; Replace xxxxxxxxxxxxxxxxxxx to the actual values
requestBody := "client_id=xxxxxxxxxxxxxxxxxxx"
             . "&client_secret=xxxxxxxxxxxxxxxxxxx"
             . "&refresh_token=xxxxxxxxxxxxxxxxxxx"
             . "&grant_type=refresh_token"

whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://www.googleapis.com/oauth2/v4/token")
whr.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
whr.Send(requestBody)
MsgBox % whr.ResponseText

access_token := Jxon_Load(whr.ResponseText).access_token ; jxon.ahk -- https://www.autohotkey.com/boards/viewtopic.php?t=627

; api test
whr.Open("GET", "https://www.googleapis.com/drive/v3/about?fields=*")
whr.SetRequestHeader("Authorization", "Bearer " . access_token)
whr.Send()
MsgBox % whr.ResponseText
Spoiler
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: an HTTP GET or POST call to URL

08 Jun 2020, 06:36

Without further ado: Sir, you made my day!
[AHK-Certicicate of Debt : BoBo, owing @tmplinshi a pint of :arrow: his favorite beer] :clap: :clap: 8-) :clap: :clap: Thx !
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: an HTTP GET or POST call to URL

08 Jun 2021, 18:54

One year later...

Could someone, anyone, please help me with reaching youtube from AHK? All I need is get some details from the video page in order to format a link.
My script used to work fine until they decided to throw in a cookie consent page for us poor europeans. Ever since I can only get that consent page "Before accessing YouTube" which contains two "buttons", one of which being a form submittal of type POST. The form contains about a dozen hidden input fields with name and value.

I tried sniffing headers in the browser, making repeated GET and POST calls using various request headers, to no avail. Tried usingWinHttp and XmlHttp - the latter is the only one that actually sets one cookie - the very first one in a set of about four or five - and then nothing. Besides all the cookies there are also at least three redirects that complicate things: from original URL www youtube com it goes to consent google com, then to consent google ro and then back to www youtube com (unless I missed some other redirect). With WinHttp one can select whether to follow redirects or not and get/set the cookies manually, while XmlHttp errored out at req.Option(WinHttpRequestOption_EnableRedirects) := true/false . I'm at my wits' end and to top it all not feeling well at all today. :sick:

So please, if anyone from the EU is around and can perform some tests I'd very much appreciate it, and any tips/advices as well.
Part of my AHK work can be found here.
User avatar
Drugwash
Posts: 850
Joined: 29 May 2014, 21:07
Location: Ploieşti, Romania
Contact:

Re: an HTTP GET or POST call to URL

13 Jun 2021, 13:44

Simply adding request header Cookie with CONSENT=YES+EN didn't work with XMLHTTP but it did work with WinHttp. Strange. However, the former seems to be preferred lately for some reason so it'd be great to find a common solution that would work in both versions.
Nevertheless, thank you for providing a working solution for one version of the request. :thumbup:
Part of my AHK work can be found here.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bobak and 242 guests