Running http: requests from Max

notes
  1. How to separate the status return code  from the actual response data?

For jit.uldl-  status reports get sent out the right outlet and errors are reported in the Max window. However their doesn’t appear to be a way to get the http: status codes or other header data.

For curl, you can write the response (JSON for example) to a file. Then you can read the file using the [js] object and parse the JSON. If you are using [aka.shell] to run the curl command, the stdout and stderr can be routed from the object – for instance, into the Max window. The -v flag (verbose mode) causes curl to output a bunch of header data.

 

 

 

 

Real time Irish train map in Max

Using node.js

Time compressed video of Irish railway data from November 30th 2012.

update notes 2/11/2021

After many years…. I was able to get this running

  • in the project folder (ie., /trains)
    • npm init
    • then do: npm install <package> for these packages:
      • request
      • xml2js
      • util
      • http
      • ws
  • then just ran: node max-train1.js and started polling in train-drawing4.maxpat and the data started rolling in
  • webSockets code was replace using ws library

Also using sample webSockets code from here to handle the map points: https://masteringjs.io/tutorials/node/websockets

Next steps:

I would like to update all of this in node for max – but its also nice to have an example of doing OSC communication with Max the old way.

Original documentation follows


This map is centered on Dublin. Each train is a different color and the train number gets printed next to the current position. So for example you can see P607 coming into Dublin from the South.

It gets updates every 10 seconds or so – but sometimes the trains seem to ‘jump’ so I don’t think the actual  data itself gets updated that fast.

download

https://github.com/tkzic/internet-sensors

folder: trains

files:

main Max patches
  • max-train3.maxpat
  • train-drawing4.maxpat
abstractions and other files
  • data_recorder_list-tz.maxpat
node.js
  • max-train1.js: (main server)
  • bigInt.js: (Osc support)
  • byteConverter.js: (Osc support)
  • libOsc.js: (Osc library)
  • max.html: (the web client file – runs in browser)

node.js installation

Install node.js on your computer (or a separate server). Instructions here: http://nodejs.org

In a terminal window go to the project folder ie., /internetsensors/trains

type: npm init

(just accept all the default values)

The following node packages are required. Install using npm. For example:

type: npm install request

Do the same with all these packages

  • request
  • xml2js
  • util
  • http
  • ws

instructions

These instructions are a bit confusing. A simpler UI is in the works. (that’s funny – I said it 8 years ago)

  • Open both Max patches: max-train3 and train-drawing4
  • To run the server, open a terminal window, go to trains folder, and type:
node ./max-train1.js
  • Back in Max, in train-drawing4, you can start and stop polling with the toggle button in the upper left corner. If you want to toggle playback from the data recorder, you’ll need to have some data recorded – or load a file (this is done from max-train3)
  •  In max-train3, select a preset in the preset object. Try the top left one, The presets go from low res to high res on Dublin (left to right).
  •  You can load train-data1.txt by clicking read message on the data recorder. Then press play, or use metro (set to low number) for high speed.
  • In train-drawing4 you’ll need to manually clear the lcd and lcdsprites in the drawing section.
  • In max-train3 you can clear the color database using labeled button (if the train names don’t print on the map)

Running the chat thing

When you connect to the node server in a web browser, for example using the following url:

http://localhost:8124

You will get a message welcoming you and asking you to enter your name. After you enter your name, Max will send you a response. Then you can enter map points using the following format:

Dublin 53.15 -6.90

The map point will be drawn on the map in the Max patch.

how does it work?

Data communication is done with OSC messages. Here’s how

  • Max sends a /getTrains message to a node.js server
  • The server runs an http: request to the Irish rail API
  • The server parses the response data and sends back a bunch of /train name x y messages. to Max
  • You can also connect to the node server from web browsers in kind of a ‘chat’ thing (using Web sockets) and enter geographical place names with lat/lon – which get pinned to the map – and Max sends back personalized thank-you notes.

Here’s what the raw data looks like:

Note: if there’s no data at this link, check the time of day. Trains stop running around midnight UTC.

Programming:

Essentially what this program does is collect current train position data from Ireland and display it as points on an XY grid.

components:

  • Max/MSP
  • node.js
  • Web browser
  • Irish train API

Server

The server is written in node.js. This was my first real project using node. So I tried to make it do a bunch of things, just to test them out.The necessary features are

  • web server to make http: requests to get the train data
  • UDP server to pass data and messages back and forth from Max to node.js using Osc
  • convert XML train data to JSON,
  • convert point data from lat/lon to XY format
Additional features of the server include:
  • chat server using socket.io which allows several web browser sessions to connect to the server, talk with each other, send point data to Max, and receive acknowledgement from Max
Localhost vs. separate server

This is probably the most confusing part about this program. You can run it on the Mac in localhost mode, or you can run the node.js program on a separate server, as long as there is a direct ip path back to the computer where Max is running.  Running a separate server makes more sense if you are using the chat or a remote web control aspect of this. I have run the node program on a Ubuntu server on my local network without making any changes other than to the IP addresses as explained next.

You need to make 2 changes to go from localhost mode, to separate server mode:

In the client program, max.html – near the top, change the ip address from localhost to the specific ip like this:

// note: if you want to run this program on something other than localhost, then put in the ip
// address of the server here, instead of local host...

//     var socket = io.connect('http://localhost:8124');
//
       var socket = io.connect(192.168.1.10:8124');

 

In the server program, max-train1.js – change the destination address for UDP to the address of the computer where max is running:


var dSendAddress = '127.0.0.1';	// send response messages to max at this address
// do this if max is running somewhere other than the same computer as the node server
// var dSendAddress = '192.168.1.104';	// send response messages to max at this address

 

Also in the Max patch max-train3.maxpat you will need to change the [udpsend] object to give it the host address of the node server.

screenshots:

max-train3.maxpat

train-drawing4.maxpat

Data recorder:

To be able to save and replay data I included a data recorder in the patch. Its a revision of the data recorder from CNMAT by Michael Z. This version

  • works with lists that start with a symbol (not just ones that start with numbers)
  • handles the time delta correctly on the first record
  • adds additional transport controls, for example playback using a [metro] at any speed – and ability to goto a particular position in the data file

The actual code for the patch is horrendous. Its filled with debugging objects and is a maze of patch cords lacking explanation. But it works…

what’s next

  • Make sure that the Max Patch loads a preset map setting by default
  • put a timestamp message in the node console and Max console so we know at the end of a train list what the current time is.
  • clean up  instructions 
  • make a screencast do demonstrate how to use this needlessly complicated patch.

sending Tweets with curl in Max,

Using xively.com and zapier.com

Note: To get this project to work you’ll need a Twitter account. And you’ll need to set up a device (feed) at xively.com and a ‘zap’ at zapier.com as directed in this post. It explains how to send tweets using triggers.

 https://reactivemusic.net/?p=6903

Also, you may notice delays due to the number of steps involved.

Looking for an easier way? Send Tweets using ruby: https://reactivemusic.net/?p=7013

download

https://github.com/tkzic/internet-sensors

folder: twitter-curl

files

Max
  • tweetCurl5a.maxpat
externals

[aka.shell] download from here: http://www.iamas.ac.jp/~aka/max/ – and add the path to the folder to Options | File Preferences in Max

authorization

  • xively.com feed id and api-key is embedded in max patch
  • you need a Twitter account
  • you need to set up a xively.com feed with twitter trigger, (as described here  https://reactivemusic.net/?p=6903) to get your own feed id, API-key, and authorize access to your Twitter account

instructions

  • Open the Max patch: tweetcurl5a.maxpat
  • enter your xively feed number and API-key into the fields (then press enter)
  • Type your Tweet text.
  • Press the big green button.

notes on curl

You can use curl for http: requests in Max by formatting the command line with [sprintf] and running it in [aka.shell]. There are a few idiosyncrasies – for example with escape sequences.

In tweetCurl5a.maxpat, the curl command is built in two sections:

  1. The request data is written to a data file /tmp/abc.json
  2.  the actual curl command is formatted and run from the command line. 
Here is the part of the patch which formats request data:

Using ‘quotes’ with [sprintf]

You’ll notice a lot of backslashes used in [sprintf]. This is done to preserve quotes. Normally a quote in [sprintf] indicates a string. Use 3 backslashes to escape a quote:

\\\"

Passing arguments into [sprintf]

The [sprintf] code is obtuse because we are formatting JSON data. The resulting data looks like this:

{ "id":95586, "datastreams":[{ "current_value":"this is a tweet", "id": "tweet"}]}

 

Note that you can pass arguments into [sprintf] using %s – but if you are using a [textedit] to collect data from the user, you’ll need to use [tosymbol] to consolidate the text into a single symbol before passing into [sprintf]

Here’s the code which writes the formatted JSON data to a file:

The next step is to format the curl command, which will read the JSON data file and send an http: request to cosm.com. Here you can see the [sprintf] for this command.

Redirecting aka.shell output to the Max window

At the very end of the [sprintf] you will see

>2&1

This is the linux method to redirect error messages and standard output from [aka.shell] to the same place, which in this case will be the Max window.

command line curl

By the way, here is what the curl command will look like on the command line

curl -v --request PUT --data-binary @/tmp/abc.json --header "X-ApiKey: abcdefg1234567" http://api.cosm.com/v2/feeds/95586.json 2>&1

 

Note: The actual ApiKey above has been replaced with: abcdefg1234567 – so that you don’t accidentally send embarrassing Tweets from my account.

Soundcloud API in Max

In this patch, Max uses the Soundcloud API to find available tracks for a user and then stream or download one of the tracks.

Features:
  • Resolve the user-id for a given Soundcloud user name – in this case “dannyzic”
  • Process the JSON response to get a track list
  • Request the first track as either a download or streaming file
  • Play the track in Max using [jit.qt.movie]

The Soundcloud API reference provides examples using curl.

http://developers.soundcloud.com/docs/api/reference

download

https://github.com/tkzic/internet-sensors

folder: soundcloud

files

main Max patch
  • soundcloud2.maxpat
abstractions and other files
  • sc-process-user-data.js
  • sc-process-track-data.js

authorization

  • The Soundcloud client-id is embedded in the Max patch. To get a client ID you will first need a Soundcloud account. Then register an app at: http://soundcloud.com/you/apps

instructions

  • Open the Max patch: soundcloud2
  • Unlock the patch and enter your client-id in the yellow [message] object in the upper right corner. Lock and save the patch.
  • Click the green button to resolve the Username: ‘dannyzic’
  • Click the blue button retrieve ‘tracks’ for this user
  • Click the yellow button to begin streaming the first available track
  • Optional: click the red button to download the track.

Google oauth 2.0 authorization for devices

What this means: You create an app on a device which doesn’t have a browser. For example, an Arduino, an appliance, or a game console. This procedure shows how to authorize that device to access a user’s account for Google, Twitter, Facebook, etc.,

See this URL for Google instructions: https://developers.google.com/accounts/docs/OAuth2ForDevices

Notes and Google examples (using curl from a command line):

Here is an oauth 2.0 google request for a user code – The client id is obtained using instructions found at the link above.

curl -d "client_id=104588205543369.apps.googleusercontent.com&scope=https://www.googleapis.com/auth/userinfo.email  https://www.googleapis.com/auth/userinfo.profile" https://accounts.google.com/o/oauth2/device/code

Which returned this JSON response:

{
  "device_code" : "4/Gujc7GxpGFSHNlphxVZCK_y10yS6Kq",
  "user_code" : "ibaz70ej9",
  "verification_url" : "http://www.google.com/device",
  "expires_in" : 1800,
  "interval" : 5
}

Then you go to the URL in the response, enter the user code, and follow instructions…

Then from the device you do this…

curl -d "client_id=1045882053369.apps.googleusercontent.com&client_secret=zDP5UVwbqcYzv7rnVieKxnOV&code=4/Gujc7GxpGHNlphxVZCK_y10yS6Kq&grant_type=http://oauth.net/grant_type/device/1.0" https://accounts.google.com/o/oauth2/token

Which returns this response:

{
  "access_token" : "ya29.AHES6ZE2QxqzZyWkGu20lJljEIHYTf08VtggyRF73428w0LQ7lzFP_uw",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "id_token" : "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJhZGQ4NWFhMmRlZmZkMWFkZWJkNzc2NTgxNWMzZmVjZTM0MmIzNGEifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiaWQiOiIxMTExNzg0MjgyNzI3MDgxMTI0NTMiLCJhdWQiOiIxMDQ1ODgyMDUzMzY5LmFwcHMuZ2df9vZ2xldXNlcmNvbnRlbnQuY29tIiwiY2lkIjoiMTA0NTg4MjA1MzM2OS5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInZlcmlmaWVkX2VtYWlsIjoidHJ1ZSIsInRva2VuX2hhc2gefiOiJvVG9OdS0tYU1DUGhYbUI1S3p4TTN3IiwiZW1haWwiOiJ6aWNhcmVsdEBnb3VsZGFjYWRlbXkub3JnIiwiaGQiOiJnb3VsZGFjYWRlbXkub3JnIiwiaWF0IjoxMzU2MjQ2Mjg2LCJleHAiOjEzNTYyNTAxODZ9.DqIqLtg9m6wlHh5YSFFgXIOgbMW0E2mKR2FdY7PWtNJrt91moqVBe7dQxQPNalQMKhYTapJdVk2MB1oRl7zXEnLIe_VjI3BUwzTKqaG_sS9oRyh14_yqDWeMFru5d7OFUm1Ulwb2lLdWWwtttEVyJiw94oBdR0tuWg0MNkEOkXU",
  "refresh_token" : "1/NuEmigydABgeRwZaRCZbZZckJ-EJFZd8C1YZLURut8s"
}

Now your device can use the access token query string method…

curl https://www.googleapis.com/oauth2/v1/userinfo?access_token=ya29.AHES6ZQxqzZyWkGu20lJljEIHYTf08VtggyRF73428w0LQ7lzFP_uw

Here is the response:

{
 "id": "1111784282727081812453",
 "email": "[email protected]",
 "verified_email": true,
 "name": "Tony Tiger",
 "given_name": "Tony",
 "family_name": "Tiger",
 "hd": "looney.org"
}

Or you can use the http header option…

curl -H "Authorization: Bearer ya29.AHKKES6ZQxqzZyWkGu20lJljEIHYTf08VtggyRF73428w0LQ7lzFP_uw" https://www.googleapis.com/oauth2/v1/user info

which should return the exact same response.

[Also see] tkzic/max teaching examples/google-oauth2.0-readme.txt

 

 

 

Merriam Webster API

Dictionary and Thesaurus.

Get a developer API key from here: http://www.dictionaryapi.com

example of a request to the Thesaurus API

curl http://www.dictionaryapi.com/api/v1/references/thesaurus/xml/cheese?key=ee2466d2-07a0-40af-b959-abcdeb125f0ca

Here’s the XML response – filled with synonyms:

<?xml version="1.0" encoding="utf-8" ?>
<entry_list version="1.0">
	<entry id="cheese"><term><hw>cheese</hw></term><fl>noun</fl><sens><mc>that which is of low quality or worth</mc><vi>you wouldn't believe the <it>cheese</it> that the movie studio puts out</vi><syn>cheese, crapola [<it>slang</it>], dreck (<it>also</it> drek), muck, rubbish, sleaze, slop, slush, trash, tripe</syn><rel>camp, kitsch; claptrap, humbug, nonsense; bomb, clinker, clunker, dud, lemon, stinker, turkey; mess, muddle, shambles</rel></sens></entry>
	<entry id="big cheese"><term><hw>big cheese</hw></term><fl>noun</fl><sens><mc>one of high position or importance within a group</mc><vi>thinks he's a <it>big cheese</it> just because he's got a business card</vi><syn>big, big boy, big cheese, bigfoot, biggie, big gun, big leaguer, big-timer, big wheel, bigwig, fat cat, heavy, heavy hitter, heavyweight, high-muck-a-muck (<it>or</it> high-muckety-muck), honcho, kahuna, kingfish, kingpin, major leaguer, muckety-muck (<it>also</it> muck-a-muck <it>or</it> mucky-muck), nabob, nawab, nibs, nob [<it>chiefly British</it>], pooh-bah (<it>also</it> poo-bah), wheel</syn><rel>baron, czar (<it>also</it> tsar <it>or</it> tzar), king, lion, magnate, mogul, prince, tycoon; VIP</rel><near>inferior, subordinate, underling; mediocrity, obscurity</near><ant>lightweight, nobody, nonentity, nothing, shrimp, twerp, whippersnapper, zero, zilch</ant></sens></entry>
</entry_list>

 

 

 

Conversation with a robot in Max

This project brings together several examples of API programming with Max. The pandorabots.api patch contains an example of using curl to generate an XML response file, then converts XML to JSON using a Python script. The resulting JSON file is read into Max and parsed using the [js] object.

Here is an audio recording of my conversation (using Max) with a text chatbot named ‘Chomsky’

‘Chomsky’ lives at http://pandorabots.com.

My voice gets recorded by Max then converted to text by the Google speech-api.

The text is passed to the Pandorabots API. The chatbot response gets spoken by the aka.speech external which uses the Mac OS built-in text-to-speech system.

Note: The above recording was processed with a ‘silence truncate’ effect because there were  3-5 second delays between responses. In realtime it has the feel of the Houston/Apollo dialogs.

pandorabots-api.maxpat (which handles chatbot responses) gets text input from speech-to-google-text-api2.maxpat – a patch that converts speech to text using the Google speech-API.

https://reactivemusic.net/?p=4690

The output (responses from chatbot) get sent to twitter-search-to-speech2.maxpat which “speaks” using the Mac OS  text-to-speech program using the aka.speech external.

files

Max

  • speech-to-google-text-api2.maxpat
  • JSON-google-speech.js
  • pandorabots-api.maxpat
  • JSON-pandorabot.js
  • text-to-speech2.maxpat

externals:

[authorization]

  • none required

external programs:

  • sox: sox audio conversion program must be in the computer’s executable file path, ie., /usr/bin – or you can rewrite the [sprintf] input to [aka.shell] with the actual path. Get sox from: http://sox.sourceforge.net
  • xml2json (python) in tkzic/internetsensors/: xml2json/xml2json.py and xml2json/setup.py (for translating XML to JSON) – [NOTE] you will need to change the path in the [sprintf] object in pandorabots.api to point to the folder containing this python script.

instructions

  • Open the three Max patches.
    • speech-to-google-text-api2.maxpat
    • pandorabots-api.maxpat
    • text-to-speech2.maxpat
  • Clear the custid in the pandorabots-api patch
  • Start audio in the Google speech patch. Then toggle the mic button and say something.
  • After the first response, go to the pandorabots-api patch and click the new custid – so that the chatbot retains the thread of the conversation.

download:

The files for this project can be downloaded from the intenet-sensors archive at github

https://github.com/tkzic/internet-sensors

Pandorabots API

Update: Now part of Internet Sensors project: https://reactivemusic.net/?p=9834  

original post

Looking into using an API to communicate with chatbots

Here is info from pandorabots FAQ: http://www.pandorabots.com/botmaster/en/~15580d493a63acc7fab1820f~/faq

Chomsky bot id: botid=b0dafd24ee35a477

H.2 Is there an API allowing other programs to talk to a Pandorabot?

Pandorabots has an API called XML-RPC that you can use to connect third-party software to our server. The XML-RPC has been used to connect Pandorabots to a wide variety of third-party applications, including Mified, mIRC, Second Life and Flash.

You may interact with Pandorabots as a webservice. Pandorabots offers consulting services supporting arbitrary web services for premium services customers. Please contact [email protected] for more information.

A client can interact with a Pandorabot by POST’ing to:

http://www.pandorabots.com/pandora/talk-xml

The form variables the client needs to POST are:

  • botid – see H.1 above.
  • input – what you want said to the bot.
  • custid – an ID to track the conversation with a particular customer. This variable is optional. If you don’t send a value Pandorabots will return a custid attribute value in the <result> element of the returned XML. Use this in subsequent POST’s to continue a conversation.

This will give a text/xml response. For example:

<result status="0" botid="c49b63239e34d1d5" custid="d2228e2eee12d255">
  <input>hello</input>
  <that>Hi there!</that>
</result>

The <input> and <that> elements are named after the corresponding AIML elements for bot input and last response. If there is an error,status will be non-zero and there will be a human readable <message> element included describing the error. For example:

<result status="1" custid="d2228e2eee12d255">
  <input>hello</input>
  <message>Missing botid</message>
</result>

Note that the values POST’d need to be form-urlencoded.

[update}

Here are two examples I just got to work using curl

curl -X POST  --data "botid=b0dafd24ee35a477&input=hello" http://www.pandorabots.com/pandora/talk-xml

curl -X POST  --data "botid=b0dafd24ee35a477&input=Where are you?" http://www.pandorabots.com/pandora/talk-xml
Here is the result for the second question
<result status="0" botid="b0dafd24ee35a477" custid="b3422b612633ac87"><input>Where are you?</input><that>I am in the computer at Pandorabots.com.</that></result>