beatbox-puredata/purest_json/manual/Using-Pd-as-Twitter-Client....

737 lines
30 KiB
HTML
Raw Normal View History

2024-09-18 21:11:11 -03:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>
Using Pd as Twitter Client
</title>
<style>
/* Page tweaks */
.preview-page {
margin-top: 64px;
margin-bottom: 21px;
}
/* User-content tweaks */
.timeline-comment-wrapper > .timeline-comment:after,
.timeline-comment-wrapper > .timeline-comment:before {
content: none;
}
/* User-content overrides */
.discussion-timeline.wide {
width: 920px;
}
</style>
<link href="style.css" rel="stylesheet"/>
</head>
<body>
<div class="page">
<div class="preview-page" data-autorefresh-url="" id="preview-page">
<main id="js-repo-pjax-container">
<div class="clearfix new-discussion-timeline container-xl px-3 px-md-4 px-lg-5">
<div class="repository-content">
<div class="clearfix">
<div class="Layout Layout--flowRow-until-md Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end">
<div class="Layout-main">
<div class="Box md Box--responsive" id="readme">
<div class="Box-header d-flex border-bottom-0 flex-items-center flex-justify-between color-bg-default rounded-top-2">
<div class="d-flex flex-items-center">
<h2 class="Box-title">
Using Pd as Twitter Client
</h2>
</div>
</div>
<div class="Box-body px-5 pb-5">
<article class="markdown-body entry-content container-lg" id="grip-content">
<p>
This tutorial will show you, how you can create your own Twitter client for Pd. You will learn how to use PuREST JSON for OAuth authentication, and you will be able to apply the methodology to create clients for other webservices.
</p>
<p>
In the examples folder, you can find the patch
<code>
twitter-client.pd
</code>
. You will only need to fill in your credentials, that you will get with the patch itself.
</p>
<p>
In this tutorial, I will use the following notation:
</p>
<ul>
<li>
<code>
[my object]
</code>
: an object with the name "my-object".
</li>
<li>
<code>
[my own message(
</code>
: a message with the content "my own message".
</li>
</ul>
<h2>
<a aria-hidden="true" class="anchor" href="#basic-explanation-of-oauth" id="user-content-basic-explanation-of-oauth">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Basic Explanation of OAuth
</h2>
<p>
With OAuth, a user can give an application (here: Puredata) access to a webservice (here: Twitter) without giving the application your login to the service.
</p>
<p>
To do so, the application needs its own token and secret (client credentials, consumer key and consumer secret), as well as the user's (token credentials, access token and access token secret). The token credentials are different to the login name and password for the service.
</p>
<p>
As the token credentials are different from login name and password, the webservice and / or user can restrict the application, so that it may only perform certain actions, but not all (e.g. only read access, not showing sensitive informations).
</p>
<p>
When the application has both tokens and secrets, it needs to sign each request to the service with these.
Most of these is done under the hood of
<code>
[oauth]
</code>
. What you will learn in this tutorial, is how to get your application token and secret, the user's token and secret, and how to read developer documentation. The latter skill is important for creating your own clients for other services.
</p>
<p>
<strong>
Caveat!
</strong>
This document makes some assumptions, which were true at the time of the last edit (May 2015): My client credentials are still valid, despite me giving it away in the example patch. Twitter have not changed their API, or the API is still supported.
</p>
<p>
This document goes through the file
<code>
twitter-client.pd
</code>
in the examples step by step.
</p>
<h2>
<a aria-hidden="true" class="anchor" href="#setting-up-your-application" id="user-content-setting-up-your-application">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Setting Up Your Application
</h2>
<p>
All information about the Twitter API can be found at
<a href="https://dev.twitter.com/" rel="nofollow">
https://dev.twitter.com/
</a>
.
</p>
<p>
For this tutorial, we will use PuREST JSON as a registered application. This application has both read and write access. If you want to reliably use Pd as a Twitter client, and / or want to restrict your client to read access only, you will need to register your client.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#registering-your-client" id="user-content-registering-your-client">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Registering Your Client
</h3>
<p>
Login at
<a href="https://dev.twitter.com/" rel="nofollow">
https://dev.twitter.com/
</a>
with your Twitter login. Click on the
<em>
create your application
</em>
and create your application. Leave the field
<em>
Callback URL
</em>
empty.
</p>
<p>
After registering your application, you get your consumer key and secret. These are needed for any communication with twitter.
</p>
<p>
On that page, you can also create your own access token and access token secret.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#getting-access-token-and-access-token-secret" id="user-content-getting-access-token-and-access-token-secret">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Getting Access Token and Access Token Secret
</h3>
<p>
This section discusses the operations in the subpatch
<code>
[pd authorize]
</code>
.
</p>
<p>
<a rel="noopener noreferrer nofollow" target="_blank">
<img alt="pd authorize" src="twitter-authorize.png" style="max-width: 100%;"/>
</a>
</p>
<p>
You may skip this step, if you created your own access token and access token secret at the Twitter website.
</p>
<p>
Initialize
<code>
[oauth]
</code>
with the base URL, consumer key, and consumer secret (the instance of
<code>
[oauth]
</code>
already uses the PuREST JSON credentials).
</p>
<p>
Use
<code>
[POST /oauth/request_token(
</code>
method at
<code>
[oauth]
</code>
.
<code>
[oauth]
</code>
will issue a request to
<a href="https://api.twitter.com/oauth/request_token" rel="nofollow">
https://api.twitter.com/oauth/request_token
</a>
and sign the request with the consumer credentials.
</p>
<p>
In your Pd console, you will hopefully get something similar to this:
</p>
<pre><code>data1: symbol oauth_token=(YOUR_TEMP_TOKEN)&amp;amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;amp;oauth_callback_confirmed=true
status1: list oauth bang
</code></pre>
<p>
Now we have an temporary access token / secret pair. With this pair, we need to tell our user (YOU!) to go to Twitter and get a PIN to generate the real access token / secret pair.
</p>
<p>
Open a web browser and go to
<a href="https://api.twitter.com/oauth/authorize?oauth_token=(YOUR_TEMP_TOKEN)&amp;amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;amp;oauth_callback_confirmed=true" rel="nofollow">
https://api.twitter.com/oauth/authorize?oauth_token=(YOUR_TEMP_TOKEN)&amp;amp;oauth_token_secret=(YOUR_TEMP_TOKEN_SECRET)&amp;amp;oauth_callback_confirmed=true
</a>
. This will get you to a website, where you can authorize PuREST JSON for using the Twitter API. After authorization, you will see YOUR_PIN in large friendly letters printed on a web page. We will need these for the next step.
</p>
<p>
Now you get a PIN. This PIN will enable you to create your access token and access token key.
</p>
<p>
First, we have to tell
<code>
[oauth]
</code>
to use your new oauth access token and credentials. To do so, we will need to use the temporary access token / secret pair for signing the next OAuth request. We can do this either by editing the creation values of
<code>
[oauth]
</code>
or by sending a
<code>
[init BASE_URL CONSUMER_KEY CONSUMER_SECRET YOUR_TEMP_TOKEN YOUR_TEMP_TOKEN_SECRET(
</code>
to it. Use your return values from before.
Now send the PIN to Twitter to finally generate the read access token / secret pair. Use
<code>
[POST access_token oauth_identifier=YOUR_PIN(
</code>
for
<code>
[oauth]
</code>
to do so.
</p>
<p>
Hopefully, you get a message similar to this in your Pd console:
</p>
<pre><code>data1: symbol oauth_token=(YOUR_ACCESS_TOKEN)&amp;amp;oauth_token_secret=(YOUR_ACCESS_TOKEN_SECRET)&amp;amp;user_id=(YOUR_USER_ID)&amp;amp;screen_name=(YOUR_TWITTER_SCREEN_NAME)
</code></pre>
<h3>
<a aria-hidden="true" class="anchor" href="#test-your-twitter-client" id="user-content-test-your-twitter-client">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Test Your Twitter Client
</h3>
<p>
This section describes the subpatch
<code>
[pd simple-operations]
</code>
.
</p>
<p>
<a rel="noopener noreferrer nofollow" target="_blank">
<img alt="pd simple-operations" src="twitter-simple.png" style="max-width: 100%;"/>
</a>
</p>
<p>
You will need to tell
<code>
[oauth]
</code>
to use a) consumer key / secret pair and b) access token / secret pair. To do so, either set these (with the base url for all requests) in the creation arguments, or send these to it with the
<code>
[init(
</code>
message. Do this with the right instance of
<code>
[oauth]
</code>
.
</p>
<p>
Now, get your home timeline by issuing
<code>
[GET /1.1/statuses/home_timeline.json(
</code>
to
<code>
[oauth]
</code>
. Does your timeline appear in the Pd console? Great!
</p>
<p>
Now you can even send tweets from inside Pd. To do so, POST your status update. Two things must be considered: The message must be correctly URL encoded using
<code>
[urlparams]
</code>
. This example only sets the text, for more parameters,
<a href="https://dev.twitter.com/rest/reference/post/statuses/update" rel="nofollow">
see the documentation
</a>
.
</p>
<h2>
<a aria-hidden="true" class="anchor" href="#lets-search-and-sonify-tweets" id="user-content-lets-search-and-sonify-tweets">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Let's Search and Sonify Tweets
</h2>
<p>
This section discusses the subpatch
<code>
[pd sonify-it]
</code>
and includes operations on JSON data.
</p>
<p>
<a rel="noopener noreferrer nofollow" target="_blank">
<img alt="pd sonify-it" src="twitter-sonify.png" style="max-width: 100%;"/>
</a>
</p>
<p>
As this example is quite complicated, this is seperated in diverse subpatches.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#search-for-data" id="user-content-search-for-data">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Search for Data
</h3>
<p>
The
<a href="https://dev.twitter.com/rest/public/search" rel="nofollow">
search API is extensively documented
</a>
. In our case we search for tweets containing the hashtag
<code>
#Puredata
</code>
. This leads to an output similar to the following on the end of
<code>
[oauth]
</code>
.
</p>
<pre><code>\{"statuses":[\{"metadata":\{"iso_language_code":"en"\\,"result_type":"recent"\}\\,"created_at":"Thu Apr 09 21:33:41 +0000 1970"\\,"id":111\\,"id_str":"111"\\,"text":"#PureData
</code></pre>
<p>
The next logical step is disecting the message and finding suitable data for sonification. This example settles on the tweet message, user name and display name.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#transform-the-json-string-to-pd-symbols" id="user-content-transform-the-json-string-to-pd-symbols">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Transform the JSON string to Pd symbols
</h3>
<p>
This section discusses the subpatch
<code>
[pd data-transform]
</code>
.
</p>
<p>
<a rel="noopener noreferrer nofollow" target="_blank">
<img alt="Transforming the JSON data" src="twitter-json.png" style="max-width: 100%;"/>
</a>
</p>
<p>
We first feed the output of
<code>
[oauth]
</code>
to the first instance of
<code>
[json-decode]
</code>
. This will generate a sequence of lists on the middle output. After removing the list keyword using
<code>
[list trim]
</code>
, we can select only the symbol following the
<code>
statuses
</code>
beginning. This contains the status data.
</p>
<p>
Using the second
<code>
[json-decode]
</code>
we decode the statuses. This is a JSON array, so we get a sequence of several objects that are decoded. We strip off the list keyword, and route our status members
<code>
text
</code>
and
<code>
user
</code>
.
<code>
user
</code>
is a JSON object itself, so we need another instance of
<code>
[json-decode]
</code>
to get
<code>
name
</code>
and
<code>
screen_name
</code>
from the object. As the output may contain spaces, instances of
<code>
[list2symbol]
</code>
or
<code>
[l2s]
</code>
from zexy are used to generate correct symbols.
</p>
<p>
We pack these objects while not using the first inlet of
<code>
[pack]
</code>
for data. This gets connected to the left outlet of the second
<code>
[json-decode]
</code>
, so that after each array of the
<code>
statuses
</code>
array something like the following list is sent to the right outlet of the subpatch:
</p>
<pre><code>0 #Puredata\ is\ Turing\ complete!\ Yay Joe\ Doe joedoe1
</code></pre>
<p>
Beware:
<code>
\
</code>
is the escape character in Pd, so that the space following it is not a separator between two atoms but part of the symbol. This translates to: The user
<code>
Joe Doe
</code>
with the Twitter handle
<code>
joedoe1
</code>
has tweeted
<code>
#Puredata is Turing complete! Yay
</code>
.
</p>
<p>
After everything is analysed, the first
<code>
[json-decode]
</code>
is done and outputs a bang on its left outlet, which is connected to the left outlet of the subpatch.
</p>
<p>
Directly after the subpatch,
<code>
[fifop]
</code>
from zexy is used to store all lists of atoms. Using the bang from the left outlet of the subpatch, we can retrieve the first list and process it, beginning with
<code>
[unpack]
</code>
ing the list. Notice, that the first outlet is discarded, as this contains always
<code>
0
</code>
.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#sonifying-the-data" id="user-content-sonifying-the-data">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
Sonifying the Data
</h3>
<p>
This is just a rough sketch of what else is going on in the patch.
</p>
<p>
The tweet text is subsequently split into a sequence of the ASCII codes of the characters using
<code>
[spell]
</code>
from cyclone. Another FIFO buffer is used to store this sequence and is triggered by a metro. As the first ASCII codes 0x00 - 0x32 are non-printable characters, we do not generate a sound for those. The rest of the ASCII codes are interpreted as MIDI notes.
</p>
<p>
For the name, we find the length of the symbol by using
<code>
[symbol2list]
</code>
or
<code>
[s2l]
</code>
from zexy with an empty separator. Counting the length of the resulting list with
<code>
[list length]
</code>
, we get the symbol length. The length of the name is used to determine the volume of the tweet sonification.
</p>
<p>
Lastly we take the length of the username and use it to modify the frequency of an LFO.
</p>
<h2>
<a aria-hidden="true" class="anchor" href="#whats-up-with-the-base-url-stuff-why-is-it-used-as-the-first-parameter-to-everything" id="user-content-whats-up-with-the-base-url-stuff-why-is-it-used-as-the-first-parameter-to-everything">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
What's up with the Base URL stuff? Why Is It Used as the First Parameter to Everything?
</h2>
<p>
Consumer key and secret as well as access token and secret are sensitive data. Anybody getting the consumer key / secret pair in their hands can pose as that application to the webservice. Likewise, anybody getting their hand on the access token / secret pair
<strong>
can use the webservice with the provided accessibilty
</strong>
.
That means, if you send your OAuth request to the wrong webservice provider, you will get a 404 error back - in the best of all cases.
</p>
<p>
In the worst case, the wrong webservice provider is malicious and guesses, that you wanted to use Twitter and use your credentials to
<strong>
pose as your application with your access credentials
</strong>
.
</p>
<p>
Now, if you set base URL to
<a href="http://example.com" rel="nofollow">
http://example.com
</a>
and issue a
<code>
[GET http://example.org/test(
</code>
,
<code>
[oauth]
</code>
will perform a GET request to
<a href="http://example.comhttp://example.org/test" rel="nofollow">
http://example.comhttp://example.org/test
</a>
, something not existant.
</p>
<h2>
<a aria-hidden="true" class="anchor" href="#what-else-can-i-do-now-or-how-to-read-the-api-documentation" id="user-content-what-else-can-i-do-now-or-how-to-read-the-api-documentation">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
What Else Can I Do Now? Or: How to Read the API Documentation
</h2>
<p>
This section assumes, that you are not a professional programmer or do not have much experience in reading API reference documents.
</p>
<p>
Now, that we have a functional Twitter client, what are the possibilities? And how can I get information on how to use OAuth with other webservices?
</p>
<p>
Well, first go to
<a href="https://dev.twitter.com/docs" rel="nofollow">
the documentation from Twitter
</a>
themselves. All you need to know is there.
</p>
<p>
OK, just joking. As this library is called PuREST JSON and
<code>
[oauth]
</code>
is used for OAuth signed RESTful requests, we need to read the
<a href="https://dev.twitter.com/docs/api/1.1" rel="nofollow">
REST API documention
</a>
"only".
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#how-do-i-know-the-right-steps-to-get-my-login-information" id="user-content-how-do-i-know-the-right-steps-to-get-my-login-information">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
How Do I Know the Right Steps to Get My Login Information?
</h3>
<p>
Take the documentation for any method, e.g.
<a href="https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline" rel="nofollow">
GET statuses/mentions_timeline
</a>
.
</p>
<p>
On the right side, you see a section "Resource Information", which contains "Authentication: Requires user context" as a link.
</p>
<p>
So, we click on that link, and go to a page that tells us about OAuth authentication. We will need to use OAuth, so there is the link "Using OAuth" that gets us to another overview page. We will need to get an access token, which leads us to
<a href="https://dev.twitter.com/docs/auth/obtaining-access-tokens" rel="nofollow">
this wonderful selection page
</a>
.
</p>
<p>
What do we want to do? We have a (kind of) desktop app, that can't access a browser, so we will need to use ...
<a href="https://dev.twitter.com/docs/auth/pin-based-authorization" rel="nofollow">
PIN-based authorization
</a>
. And now, all the steps are linked from there.
</p>
<h3>
<a aria-hidden="true" class="anchor" href="#how-do-i-read-a-documentation-entry" id="user-content-how-do-i-read-a-documentation-entry">
<span aria-hidden="true" class="octicon octicon-link">
</span>
</a>
How Do I Read A Documentation Entry?
</h3>
<p>
OK, now that we have found our method to call, we need to know how to do our call.
</p>
<p>
Let's say, you want to tweet from Pd and have already found the method
<a href="https://dev.twitter.com/docs/api/1.1/post/statuses/update" rel="nofollow">
POST statuses/update
</a>
.
</p>
<p>
<a rel="noopener noreferrer nofollow" target="_blank">
<img alt="Screenshot from Twitter website" src="twitter-documentation.png" style="max-width: 100%;"/>
</a>
</p>
<p>
The important parts are marked with yellow by me. On the bottom of the page you can see examples, that tell you how to use the method.
</p>
<p>
That's all there is, there's no inherent magic.
</p>
</article>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<script>
function showCanonicalImages() {
var images = document.getElementsByTagName('img');
if (!images) {
return;
}
for (var index = 0; index < images.length; index++) {
var image = images[index];
if (image.getAttribute('data-canonical-src') && image.src !== image.getAttribute('data-canonical-src')) {
image.src = image.getAttribute('data-canonical-src');
}
}
}
function scrollToHash() {
if (location.hash && !document.querySelector(':target')) {
var element = document.getElementById('user-content-' + location.hash.slice(1));
if (element) {
element.scrollIntoView();
}
}
}
function autorefreshContent(eventSourceUrl) {
var initialTitle = document.title;
var contentElement = document.getElementById('grip-content');
var source = new EventSource(eventSourceUrl);
var isRendering = false;
source.onmessage = function(ev) {
var msg = JSON.parse(ev.data);
if (msg.updating) {
isRendering = true;
document.title = '(Rendering) ' + document.title;
} else {
isRendering = false;
document.title = initialTitle;
contentElement.innerHTML = msg.content;
showCanonicalImages();
}
}
source.onerror = function(e) {
if (e.readyState === EventSource.CLOSED && isRendering) {
isRendering = false;
document.title = initialTitle;
}
}
}
window.onhashchange = function() {
scrollToHash();
}
window.onload = function() {
scrollToHash();
}
showCanonicalImages();
var autorefreshUrl = document.getElementById('preview-page').getAttribute('data-autorefresh-url');
if (autorefreshUrl) {
autorefreshContent(autorefreshUrl);
}
</script>
</body>
</html>