Using Twitter API on shared server - Rate limit exceeded even though I am caching the response - api

I have written a php script which gets the latest status update for 12 different twitter accounts by pulling an xml for each and caching it on my server. This currently runs every 30 minutes.
Unfortunately I keep getting the "Rate limit exceeded. Clients may not make more than 150 requests per hour." error event though i'm only making 24 requests from the 150 I should have.
I assume this is because my domain is on a shared server and twitter is counting other requests against me.
How can I authorise my requests so i'm not restriced by the standard IP limit?
I have no experience of OAuth so need step by step instructions if possible.
Thanks in advance!

OK so I managed to get the most of this working with no previous experience of API's etc.
Here is my step by step guide:
Step 1.
Create a Twitter list.
Go to: https://twitter.com/username/lists
Click "Create list"
Enter details and save.
Go to a twitter user you wish to add to the list and click the gear dropdown and select "Add or remove from lists". Tick the checkbox next to your list.
Step 2.
Create a Twitter App via: https://dev.twitter.com/apps/new
Log in using your Twitter credentials.
Give your app a name, description etc.
Go to the Settings tab and change the Access type to Read and Write then click "Update this Twitter application's settings".
Click "Create my access token" at the bottom of the page.
You will now have a Consumer Key, Consumer secret, Access token and Access token secret. Make a note of these.
Step 3. Create API tokens.
Download and install onto your server the Abraham Twitter oAuth library from: https://github.com/abraham/twitteroauth (I'll use a folder called "twitter").
Create a new file, name it authorise.php in the oAuth folder and put the following code inside (with your generated keys in place of the named text). (Put the code between < ? PHP and ?> brackets).
// Create our twitter API object
require_once("twitteroauth/twitteroauth.php");
$oauth = new TwitterOAuth('Put-Consumer-Key-here', 'Put-Consumer-secret-here',
'Put-Access-Token-here', 'Put-Access-token-secret-here');
// Send an API request to verify credentials
$credentials = $oauth->get("account/verify_credentials");
echo "Connected as #" . $credentials->screen_name;
// Post our new "hello world" status
$oauth->post('statuses/update', array('status' => "hello world"));
This has now authorised your twitter App for the API and posted a "hello world" status on your twitter account.
Note: The Read / Write access change we did earlier alowed the code to post the status update, it's not actually needed to pull the list from the API but I did it to make sure it was working OK. (You can turn this off again by going back to the Settings).
Step 4.
Create PHP file to pull your list and cache the file.
Create an XML file (YOUR-FILE-NAME.xml) and save it in the oAuth folder.
Create a PHP file (YOUR-PHP-FILE.php) and save it in the oAuth folder
Edit the below code with your twitter API keys, file name and twitter list details and save it in your PHP file. (Put the code within < ? PHP and ?> brackets).
/* Twitter keys & secrets here */
$consumer_key = 'INSERT HERE';
$consumer_secret = 'INSERT HERE';
$access_token = 'INSERT HERE';
$access_token_secret = 'INSERT HERE';
// Create Twitter API object
require_once('twitteroauth/twitteroauth.php');
// get access token and secret from Twitter
$oauth = new TwitterOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret);
// fake a user agent to have higher rate limit
$oauth->useragent = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.9) Gecko/20071025 Firefox/2.0.0.9';
// Send an API request to verify credentials
$credentials = $oauth->get('account/verify_credentials');
echo 'Connected as #' . $credentials->screen_name . '\n';
// Show API hits remaining
$remaining = $oauth->get('account/rate_limit_status');
echo "Current API hits remaining: {$remaining->remaining_hits}.\n";
$ch = curl_init();
$file = fopen("YOUR-FILE-NAME.xml", "w+");
curl_setopt($ch, CURLOPT_URL,'https://api.twitter.com/1/lists/statuses.xml?slug=INSERT-LIST-NAME&owner_screen_name=INSERT-YOUR-TWITTER-USERNAME-HERE&include_entities=true');
curl_setopt($ch, CURLOPT_FILE, $file);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($file);?>
Copy the file path into your browser and test it. (e.g. http://www.yourwebsite.com/twitter/YOUR-PHP-FILE.php)
This should contact twitter, pull the list as an XMl file and save it into YOUR-FILE-NAME.xml. Test it by opening the XML file, it should have the latest statuses from the users in your twitter list.
Step 5.
Automate the PHP script to run as often as you like (up to 350 times per hour) via a Cron job.
Open your Cpanel and click "Cron jobs" (usually under Advanced).
You can choose the regularity of your script using the common settings.
In the command field add the following code:
php /home/CPANEL-USERNAME/public_html/WEBSITE/twitter/YOUR-PHP-FILE.php >/dev/null 2>&1
Your script will now run as often as you have chosen, pull the list from twitter and save it into YOUR-FILE-NAME.xml.
Step 6.
You can now pull statuses from the cached XML file meaning your visitors will not be making unnecessary calls to the API.
I've not worked out how to target a specific screen_name yet if anyone can help there?

a) don't check 12 different accounts, create a [public] list https://twitter.com/lists and check only the it => 12 times less requests
b) use this awesome oAuth lib: https://github.com/abraham/twitteroauth and use oAuth requests instead of unsigned => you will get 350 requests and they will not be affected by IP limit

Related

How do I get sorted results from the Google Photos search API?

I'm using the search API for Google Photos documented here. I'd like the results in the response to be sorted from newest to oldest, but by default, the results are sorted from oldest to newest. Is there a way to reverse the sorting?
I believe your goal as follows.
You want to sort the result values from the method of "Method: mediaItems.search".
You want to sort the values from oldest to newest.
Issue and workaround:
Unfortunately, in the current stage, it seems that there is no parameter for sorting the returned values for the the method of "Method: mediaItems.search" in Google Photos API. Also, it seems that such parameter is not existing in the method of "mediaItems.list".
By the way, it was found that when albumId is used in the request body for the method of "Method: mediaItems.search", the returned values are sorted as the ascending order. If you use the albumn ID, I think that your goal can be achieve by this.
On the other hand, when albumId is NOT used in the request body, the returned values are sorted as the descending order. And also, it seems that when filteres is used in the request body, the returned values are sorted as the descending order.
From your question, I thought that in your situation, albumId might be not used. So in this case, as the current workaround, how about sorting the values using a script after the values are retrieved? In this answer, I would like to propose to use the Web Apps created by Google Apps Script as a wrapper API.
Usage:
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Linking Cloud Platform Project to Google Apps Script Project.
About this, you can see the detail flow at here.
And also, please enable Google Photos API at API console.
3. Add scope.
In this case, please addt the scope of https://www.googleapis.com/auth/photoslibrary to the manifest file (appsscript.json).
4. Script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps. This Web Apps is used as an API.
function doGet(e) {
const key = "sampleKey"; // This is used for using this Web Apps.
try {
if (e.parameter.key != key) throw new Error("Invalid key.");
const albumId = e.parameter.albumId;
const filters = e.parameter.filters;
const sort = e.parameter.sort;
const headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
const url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
let mediaItems = [];
let pageToken = "";
const metadata = {pageSize: 100, pageToken: pageToken};
if (albumId) metadata.albumId = albumId;
if (filters) metadata.filters = JSON.parse(filters);
do {
const params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify(metadata),
}
const res = UrlFetchApp.fetch(url, params);
const obj = JSON.parse(res.getContentText());
mediaItems = mediaItems.concat(obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
if (mediaItems.length > 0) {
if (sort && sort == "ascending") {
mediaItems.sort((a, b) => new Date(a.mediaMetadata.creationTime) < new Date(b.mediaMetadata.creationTime) ? -1 : 1);
}
return ContentService.createTextOutput(JSON.stringify({values: mediaItems}));
}
return ContentService.createTextOutput(JSON.stringify({error: "No values."}));
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({error: err.message}));
}
}
5. Deploy Web Apps.
The detail information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the important of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
When you want to use the access token, please set it to Anyone with Google account or Only myself. By this, the user can access to the Web Apps using the access token. When you use the access token, please include the scope of https://www.googleapis.com/auth/drive.readonly or https://www.googleapis.com/auth/drive.
Please click "Deploy" button.
When "The Web App requires you to authorize access to your data" is shown, please click "Authorize access".
Automatically open a dialog box of "Authorization required".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Copy the URL of Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
6. Testing.
As the test of this Web Apps, I would like to propose to use the following curl command. Please replace https://script.google.com/macros/s/###/exec with your Web Apps URL.
Simple use:
In this curl command, the result value is returned as the ascending order of oldest to newest.
$ curl -GL -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
Use albumId:
When you want to use the album ID, please use the following curl command.
$ curl -GL -d "albumId=###" -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this case, even when -d "sort=ascending" is not used, the result value is returned as the ascending order of oldest to newest.
Use filters:
When you want to use the filters, please use the following curl command.
$ curl -GL -d 'filters={"dateFilter":{"ranges":[{"startDate":{"year":2020},"endDate":{"year":2021}}]}}' -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this command, the values of 2020 - 2021 are returned as the ascending order of oldest to newest.
Note:
Although when I searched this at the Google issue tracker, I couldn't find about it. So how about reporting this as the future request? Ref
References:
Method: mediaItems.search
Related thread.
How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet
Google photos api adding photos not working, upload seems to work
Google Apps Scripts: import (upload) media from Google Drive to Google Photos?

datatables / jquery / session variable / login form

Scenario:
1) login.php verifies the username and password and sets a session with the user id
$_SESSION['id'] = $id;
2) datatables / jquery calls an action.php file to fill the table with data
"ajax":{
url:"action.php",
type:"POST",
data:{action:'listTable'},
dataType:"json"
},
3) the action.php file calls the function listTable()
include('table.php');
$table = new table();
$table->listTable();
4) the listTable function (table.php) returns the whole data which is landing in the datatable
$output = array(
"draw" => intval($_POST["draw"]),
"recordsTotal" => $numRows,
"recordsFiltered" => $numRows,
"data" => $tableData
);
echo json_encode($output);
5) the data was selected with a sql command and was put into the tableData variable
Everything works fine in that case.
Ideas:
I wanted to acccess the session variable within the sql select command. This is not possible, because of the jquery / action.php api. Those are complete different files and have no access to that session. It is possible to set the user id within a hidden formular field, but this is not secure and easy to manipulate. A cookie file is also user editable. Furthermore Javascript cannot read server side session variables.
Question:
How can i use / access that php session variable in that scenario?
Update:
Thats not working too :/
overview.php
$userID = $_SESSION['id'];
data.js
"ajax":{
url:"action.php",
type:"POST",
data:{userID:userID, action:'listTable'},
dataType:"json"
},
table.php
$userID = $_POST["userID"];
I really don't understand here the problem, because of $_SESSION variable is available across the php files even if an ajax calls the php file. Until you call the AJAX call from the same browser and the ajax call includes the cookies (one of the cookie will be the session ID) then you will be able to reach your user ID in the SQL query on the PHP side, without passing to javascript/jquery your user's ID.
As you wrote in login.php:
$_SESSION['id'] = $id;
In your table.php where the SQL command is living:
echo isset($_SESSION['id']) ? "I'm existing!" : "I'm NOT existing!";
It will print "I'm existing!" if the user logged in and "I'm NOT existing!" if the user didn't log in. All the $_SESSION variables are available across the php files until the server gets the SESSION ID from the browser (which is a cookie). Cookies are automatically sent if you calling the same domain.

Twitter Ads API error: INSUFFICIENT_USER_AUTHORIZED_PERMISSION. How to solve it?

I am trying to perform a request to the twitter Ads API in my dev environment. I am already registered to get access to this service.
I have received a confirmation e-mail like this:
Your application (ID:12345678) has been approved for the Twitter Ads API program and your organization has been granted a Developer license for Read/Write access. ...
This is why I suppose to have my APP ready to query the Ads API.
Besides that I have information about the APP (tokens and secrets) in the page https://developer.twitter.com/en/apps but I can't find any reference to the account_id, mentioned in the official documentation.
Advertising accounts are registered on ads.twitter.com and identified
in the API by account_id. Advertising accounts link directly to funding
sources and leverage content from one or more Twitter user accounts as
‘promotable users’. Each advertising account can grant permission to
one or more Twitter user accounts. The advertising account, or “current
account,” is represented in nearly every URL executed as an in-line
:account_id parameter.
Following this post I have create the follow code in oder to get access to the Twitter Ads API:
$settings = array(
'oauth_access_token' => env('TWITTER_ACCESS_TOKEN'),
'oauth_access_token_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
'consumer_key' => env('TWITTER_CONSUMER_KEY'),
'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
);
$url = 'https://api.twitter.com/1.1/followers/ids.json';
$getfield = '?screen_name=J7mbo';
$requestMethod = 'GET';
$twitter = new TwitterAPIExchangeService($settings);
$data = $twitter->setGetfield($getfield)
->buildOauth($url, $requestMethod)
->performRequest();
dd($data);
The previous code is working (I am not querying Ads API. But the next one ( querying the Ads Api) is not working:
$url = 'https://ads-api.twitter.com/5/accounts';
$requestMethod = 'GET';
$twitter = new TwitterAPIExchangeService($settings);
$data = $twitter->buildOauth($url, $requestMethod)->performRequest();
dd($data);
{"errors":[{"code":"INSUFFICIENT_USER_AUTHORIZED_PERMISSION","message":"User 2222222222 is not authorized to make this request. Please have them reauthorize with your client application APPNAme."}],"request":{"params":{}}}
What am I missing?
I have found a solution. I don't know if this is the only one but it works.
We must instal Twurl. Twurl is a curl-like application, tailored specifically for the Twitter API.
Install twurl in your system. $ sudo gem install twurl
Set authorization to twurl acceess your twitter APP. $ twurl authorize --consumer-key xxxxx --consumer-secret xxxxx
That is the output for the prevoius command: Go to https://api.twitter.com/oauth/authorize?oauth_consumer_key=xxxx&oauth_nonce=ffff&oauth_signature=bbb&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1556889574&oauth_token=ddd&oauth_version=1.0 and paste in the supplied PIN
Open the browser copy and paste the provided URL https://api. .... version=1.0
You will be redirected to a page asking to confirm the authorization. Confirm it.
You will receive a message: 'You've granted access to APP_Name! Next, return to APP_Name and enter this PIN to complete the authorization process. PIN = 09010101'.
Just copy the PIN number and paste back in the terminal and hit enter.
You will get a message in the terminal Authorization successful.
Go to yor APP_Name page https://developer.twitter.com/en/apps/123456 and go to Keys and tokens section. You need to regenerate the Access token & access token secret. Hit the button 'regenerate'
Once it is regenerate you can get access to the api trough twurl in your terminal: $ twurl -H "ads-api.twitter.com" "/5/accounts". Please note that today (May-2019) I am using number 5 in "/5/accounts". You must to check your version at your date.
Now you can get access to the Twitter Ads API trough curl in php also.
Create a class TwitterAPIExchangeService (I am in Laravel 5.8). You can get the class in this post.
Use the follow code with your keys:
$settings = array(
'oauth_access_token' => env('TWITTER_ACCESS_TOKEN'),
'oauth_access_token_secret' => env('TWITTER_ACCESS_TOKEN_SECRET'),
'consumer_key' => env('TWITTER_CONSUMER_KEY'),
'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
);
//Regular Twitter API
$url = 'https://api.twitter.com/1.1/followers/ids.json';
$getfield = '?screen_name=J7mbo';
//Ads Twitter API
//$url = 'https://ads-api.twitter.com/5/accounts';
//$getfield = '';
$requestMethod = 'GET';
$twitter = new TwitterAPIExchangeService($settings);
$data = $twitter->setGetfield($getfield)
->buildOauth($url, $requestMethod)
->performRequest();
dd($data);
Need to regenerate keys and tokens. after that its work for me

replace credentials with API Key when using Rally scripts

So, I'm using the following Ruby scripts to make bulk changes to user permissions: https://github.com/RallyTools/Rally-User-Management
My issue is that I need to put this code on a server where other people might be able to access it, and I want to change the my_vars.rb file to hold my credentials in the form of an API key instead of my username and password.
Is this possible? Because it doesn't seem as simple as removing the username and password lines, and replacing those lines with one for an API key
The rally_api gem's documentation shows example of creating a connection with ApiKey in the Usage section. The user management utility is based on this gem.
#Using api keys? -- currently rally1 only
Go to https://rally1.rallydev.com/login
You can generate an api key on this site and use it *instead* of username and password
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:api_key] = "_your_api_key_from_https://rally1.rallydev.com/login"
config[:workspace] = "Workspace Name"
config[:project] = "Project Name"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
The commit history shows that rally_api started to support ApiKey authentication in version 1.0

How do you obtain a WeChat access_token certificate from api.wechat.com?

This site tries to explain the process: http://admin.wechat.com/wiki/index.php?title=Access_token
The problem is nowhere do they tell you where to get the AppID or what exactly the secret is.Has anyone else succeeded in communicating with WeChat?
Essentially we # WeChat have 2 types of accounts, subscription and service. Subscription account only gives you access to the Message API which allows for receiving messages and autoresponses and allows you to broadcast to your users once a day. Subscription accounts are also grouped in a category in your contacts under subscription.
A service account gives you an APP ID and APP SECRET which allows you to generate an access token which is needed for pretty much all the other API's apart from the Message API. A service account displays in the user's contact list under the main chats in between all your other normal contacts. You can only broadcast to each of your users once a month on a service account.
If you have a service account you will get the APP ID and APP SECRET from admin.wechat.com -> login -> function -> advanced -> developer mode -> Just under your token you will see the APP ID and APP SECRET
To see what type of account you have go to admin.wechat.com -> login and then look at the top right of the screen next to your account name you will see your account name and just above that it will either say subscription account or service account.
If you want to test all the API's I recommend going to the developer sandbox environment where you get full access to all the API's: How does link with href for Line and Wechat?
Please note your number needs to be in the international format so 072 111 2233 you have to enter as +27721112233
Login http://admin.wechat.com
[advanced] --> [Developer Mode], you will got your Appid & AppSecret.
You don't have a wechat OA account?
Join [WeChat Space] https://plus.google.com/communities/102783597675617808511
You may go to http://dev.wechat.com/ to sign up for a developer account.
After you sign up, you will get your App ID and AppKey via your signup email.
Then, you can go to http://admin.wechat.com/wiki/index.php?title=Main_Page to obtain more information.
I wrote a code snippet on github that explains the entire process. The code is for django but can be used with any python framework
here is a snippet
import xml.etree.ElementTree as ET
from wechat.views import WeChatView
MyCustomView(WeChatView):
token = "ad4sf65weG7Db6ddWE"
on_message(self, message):
root = ET.fromstring(message)
from = root[1].text
message_type = root[3].text
content = root[4].text
print('from: {}'.format(from))
print('message type: {}'.format(message_type))
print('content: {}'.format(content))
The full code is here https://github.com/tawanda/django-wechat
Here's my code,maybe you can try it.
//Getting access_token from customize menus
static function get_access_token($appid,$secret){
$url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$appid."&secret=".$secret;
$json=http_request_json($url);//here cannot use file_get_contents
$data=json_decode($json,true);
if($data['access_token']){
return $data['access_token'];
}else{
return "Error occurred while geting the access_token";
}
}
//Though URL request is https',cannot use file_get_contents.Using CURL while asking the JSON data
function http_request_json($url){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}