Coder Social home page Coder Social logo

jublo / codebird-php Goto Github PK

View Code? Open in Web Editor NEW
775.0 66.0 236.0 1.34 MB

Easy access to the Twitter REST API, Direct Messages API, Account Activity API, TON (Object Nest) API and Twitter Ads API — all from one PHP library.

Home Page: https://www.jublo.net/projects/codebird/php

License: GNU General Public License v3.0

PHP 100.00%
php twitter-api streaming-api codebird twitter-stream

codebird-php's Introduction

codebird-php

Easy access to the Twitter REST API, Direct Messages API, Account Activity API, TON (Object Nest) API and Twitter Ads API — all from one PHP library.

Copyright (C) 2010-2018 Jublo Limited [email protected]

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Codacy Badge Coverage Status Travis Status

Requirements

  • PHP 7.1.0 or higher
  • OpenSSL extension

Summary

Use Codebird to connect to the Twitter REST API, Streaming API, Collections API, TON (Object Nest) API and Twitter Ads API from your PHP code — all using just one library. Codebird supports full 3-way OAuth as well as application-only auth.

Authentication

To authenticate your API requests on behalf of a certain Twitter user (following OAuth 1.0a), take a look at these steps:

require_once ('codebird.php');
\Codebird\Codebird::setConsumerKey('YOURKEY', 'YOURSECRET'); // static, see README

$cb = \Codebird\Codebird::getInstance();

You may either set the OAuth token and secret, if you already have them:

$cb->setToken('YOURTOKEN', 'YOURTOKENSECRET');

Or you authenticate, like this:

session_start();

if (! isset($_SESSION['oauth_token'])) {
  // get the request token
  $reply = $cb->oauth_requestToken([
    'oauth_callback' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']
  ]);

  // store the token
  $cb->setToken($reply->oauth_token, $reply->oauth_token_secret);
  $_SESSION['oauth_token'] = $reply->oauth_token;
  $_SESSION['oauth_token_secret'] = $reply->oauth_token_secret;
  $_SESSION['oauth_verify'] = true;

  // redirect to auth website
  $auth_url = $cb->oauth_authorize();
  header('Location: ' . $auth_url);
  die();

} elseif (isset($_GET['oauth_verifier']) && isset($_SESSION['oauth_verify'])) {
  // verify the token
  $cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
  unset($_SESSION['oauth_verify']);

  // get the access token
  $reply = $cb->oauth_accessToken([
    'oauth_verifier' => $_GET['oauth_verifier']
  ]);

  // store the token (which is different from the request token!)
  $_SESSION['oauth_token'] = $reply->oauth_token;
  $_SESSION['oauth_token_secret'] = $reply->oauth_token_secret;

  // send to same URL, without oauth GET parameters
  header('Location: ' . basename(__FILE__));
  die();
}

// assign access token on each page load
$cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);

Logging out

In case you want to log out the current user (to log in a different user without creating a new Codebird object), just call the logout() method.

$cb->logout();

Codebird also supports calling the oauth/invalidate_token method directly:

$reply = $cb->oauth_invalidateToken([
    'access_token'        => '1234',
    'access_token_secret' => '5678'
]);

Application-only auth

Some API methods also support authenticating on a per-application level. This is useful for getting data that are not directly related to a specific Twitter user, but generic to the Twitter ecosystem (such as search/tweets).

To obtain an app-only bearer token, call the appropriate API:

$reply = $cb->oauth2_token();
$bearer_token = $reply->access_token;

I strongly recommend that you store the obtained bearer token in your database. There is no need to re-obtain the token with each page load, as it becomes invalid only when you call the oauth2/invalidate_token method.

If you already have your token, tell Codebird to use it:

\Codebird\Codebird::setBearerToken('YOURBEARERTOKEN');

In this case, you don't need to set the consumer key and secret. For sending an API request with app-only auth, see the ‘Usage examples’ section.

A word on your callback URL

Twitter is very restrictive about which URLs may be used for your callback URL. For example, even the presence of the ‘www’ subdomain must match with the domain that you specified in the settings of your app at https://developer.twitter.com/en/apps.

Mapping API methods to Codebird function calls

As you can see from the last example, there is a general way how Twitter’s API methods map to Codebird function calls. The general rules are:

  1. For each slash in a Twitter API method, use an underscore in the Codebird function.

Example: statuses/update maps to Codebird::statuses_update().

  1. For each underscore in a Twitter API method, use camelCase in the Codebird function.

Example: statuses/home_timeline maps to Codebird::statuses_homeTimeline().

  1. For each parameter template in method, use UPPERCASE in the Codebird function. Also don’t forget to include the parameter in your parameter list.

Examples:

  • statuses/show/:id maps to Codebird::statuses_show_ID('id=12345').
  • users/profile_image/:screen_name maps to Codebird::users_profileImage_SCREEN_NAME('screen_name=jublonet').

Usage examples

When you have an access token, calling the API is simple:

$cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']); // see above

$reply = (array) $cb->statuses_homeTimeline();
print_r($reply);

Tweeting is as easy as this:

$reply = $cb->statuses_update('status=Whohoo, I just Tweeted!');

⚠️ Make sure to urlencode any parameter values that contain query-reserved characters, like Tweeting the & sign:

$reply = $cb->statuses_update('status=' . urlencode('Fish & chips'));
// will result in this:
$reply = $cb->statuses_update('status=Fish+%26+chips');

In most cases, giving all parameters in an array is easier, because no encoding is needed:

$params = [
  'status' => 'Fish & chips'
];
$reply = $cb->statuses_update($params);
$params = [
  'status' => 'I love London',
  'lat'    => 51.5033,
  'long'   => 0.1197
];
$reply = $cb->statuses_update($params);
$params = [
  'screen_name' => 'jublonet'
];
$reply = $cb->users_show($params);

This is the resulting Tweet sent with the code above.

Requests with app-only auth

To send API requests without an access token for a user (app-only auth), add a second parameter to your method call, like this:

$reply = $cb->search_tweets('q=Twitter', true);

Bear in mind that not all API methods support application-only auth.

HTTP methods (GET, POST, DELETE etc.)

Never care about which HTTP method (verb) to use when calling a Twitter API. Codebird is intelligent enough to find out on its own.

Response codes

The HTTP response code that the API gave is included in any return values. You can find it within the return object’s httpstatus property.

Dealing with rate-limits

Basically, Codebird leaves it up to you to handle Twitter’s rate limit. The library returns the response HTTP status code, so you can detect rate limits.

I suggest you to check if the $reply->httpstatus property is 400 and check with the Twitter API to find out if you are currently being rate-limited. See the Rate Limiting FAQ for more information.

Unless your return format is JSON, you will receive rate-limiting details in the returned data’s $reply->rate property, if the Twitter API responds with rate-limiting HTTP headers.

Return formats

The default return format for API calls is a PHP object. For API methods returning multiple data (like statuses/home_timeline), you should cast the reply to array, like this:

$reply = $cb->statuses_homeTimeline();
$data = (array) $reply;

Upon your choice, you may also get PHP arrays directly:

$cb->setReturnFormat(CODEBIRD_RETURNFORMAT_ARRAY);

The Twitter API natively responds to API calls in JSON (JS Object Notation). To get a JSON string, set the corresponding return format:

$cb->setReturnFormat(CODEBIRD_RETURNFORMAT_JSON);

Uploading images and videos

Twitter will accept the following media types, all of which are supported by Codebird:

  • PNG
  • JPEG
  • BMP
  • WebP
  • GIF
  • Animated GIF
  • Video

Tweet media can be uploaded in a 2-step process:

First you send each media to Twitter. For images, it works like this:

// these files to upload. You can also just upload 1 image!
$media_files = [
  'bird1.jpg', 'bird2.jpg', 'bird3.jpg'
];
// will hold the uploaded IDs
$media_ids = [];

foreach ($media_files as $file) {
  // upload all media files
  $reply = $cb->media_upload([
    'media' => $file
  ]);
  // and collect their IDs
  $media_ids[] = $reply->media_id_string;
}

Uploading videos requires you to send the data in chunks. See the next section on this.

Second, you attach the collected media ids for all images to your call to statuses/update, like this:

// convert media ids to string list
$media_ids = implode(',', $media_ids);

// send Tweet with these medias
$reply = $cb->statuses_update([
  'status' => 'These are some of my relatives.',
  'media_ids' => $media_ids
]);
print_r($reply);

Here is a sample Tweet sent with the code above.

More documentation for uploading media is available on the Twitter Developer site.

Remote files

Remote files received from http and https servers are supported, too:

$reply = $cb->media_upload(array(
  'media' => 'http://www.bing.com/az/hprichbg/rb/BilbaoGuggenheim_EN-US11232447099_1366x768.jpg'
));

⚠️ URLs containing Unicode characters should be normalised. A sample normalisation function can be found at http://stackoverflow.com/a/6059053/1816603

To circumvent download issues when remote servers are slow to respond, you may customise the remote download timeout, like this:

$cb->setRemoteDownloadTimeout(10000); // milliseconds

Video files

Uploading videos to Twitter (≤ 15MB, MP4) requires you to send them in chunks. You need to perform at least 3 calls to obtain your media_id for the video:

  1. Send an INIT event to get a media_id draft.
  2. Upload your chunks with APPEND events, each one up to 5MB in size.
  3. Send a FINALIZE event to convert the draft to a ready-to-Tweet media_id.
  4. Post your Tweet with video attached.

Here’s a sample for video uploads:

$file       = 'demo-video.mp4';
$size_bytes = filesize($file);
$fp         = fopen($file, 'r');

// INIT the upload

$reply = $cb->media_upload([
  'command'     => 'INIT',
  'media_type'  => 'video/mp4',
  'total_bytes' => $size_bytes
]);

$media_id = $reply->media_id_string;

// APPEND data to the upload

$segment_id = 0;

while (! feof($fp)) {
  $chunk = fread($fp, 1048576); // 1MB per chunk for this sample

  $reply = $cb->media_upload([
    'command'       => 'APPEND',
    'media_id'      => $media_id,
    'segment_index' => $segment_id,
    'media'         => $chunk
  ]);

  $segment_id++;
}

fclose($fp);

// FINALIZE the upload

$reply = $cb->media_upload([
  'command'       => 'FINALIZE',
  'media_id'      => $media_id
]);

var_dump($reply);

if ($reply->httpstatus < 200 || $reply->httpstatus > 299) {
  die();
}

// if you have a field `processing_info` in the reply,
// use the STATUS command to check if the video has finished processing.

// Now use the media_id in a Tweet
$reply = $cb->statuses_update([
  'status'    => 'Twitter now accepts video uploads.',
  'media_ids' => $media_id
]);

Find more information about accepted media formats in the Twitter Developer docs.

⚠️ When uploading a video in multiple chunks, you may run into an error The validation of media ids failed. even though the media_id is correct. This is known. Please check back in the Twitter community forums.

Twitter Streaming API

The Streaming APIs give developers low latency access to Twitter’s global stream of Tweet data. A proper implementation of a streaming client will be pushed messages indicating Tweets and other events have occurred, without any of the overhead associated with polling a REST endpoint.

To consume one of the available Twitter streams, follow these two steps:

  1. Set up a callback function that gets called for every new streaming message that arrives.

    Codebird also calls this function once per second, to allow you to work on any due tasks, and to give you the chance to cancel the stream even if no new messages appear.

  2. After creating the callback, tell Codebird about it using a callable. Then start consuming the stream.

// First, create a callback function:

function some_callback($message)
{
  // gets called for every new streamed message
  // gets called with $message = NULL once per second

  if ($message !== null) {
    print_r($message);
    flush();
  }

  // return false to continue streaming
  // return true to close the stream

  // close streaming after 1 minute for this simple sample
  // don't rely on globals in your code!
  if (time() - $GLOBALS['time_start'] >= 60) {
    return true;
  }

  return false;
}

// set the streaming callback in Codebird
$cb->setStreamingCallback('some_callback');

// any callable is accepted:
// $cb->setStreamingCallback(['MyClass', 'some_callback']);

// for canceling, see callback function body
// not considered good practice in real world!
$GLOBALS['time_start'] = time();

// Second, start consuming the stream:
$reply = $cb->statuses_filter();

// See the *Mapping API methods to Codebird function calls* section for method names.
// $reply = $cb->statuses_filter('track=Windows');

You should be able to set a timeout for the streaming API using setTimeout. In addition, your callback will receive empty messages if no events occur, and you should make your function return true; in order to cancel the stream.

Find more information on the Streaming API in the developer documentation website.

Twitter Collections, Direct Messages and Account Activity APIs

Collections are a type of timeline that you control and can be hand curated and/or programmed using an API.

Pay close attention to the differences in how collections are presented — often they will be decomposed, efficient objects with information about users, Tweets, and timelines grouped, simplified, and stripped of unnecessary repetition.

Never care about the OAuth signing specialities and the JSON POST body for POST and PUT calls to these special APIs. Codebird takes off the work for you and will always send the correct Content-Type automatically.

Find out more about the Collections API in the Twitter API docs. More information on the Direct Messages API and the Account Activity API is available there as well.

Here’s a sample for adding a Tweet using the Collections API:

$reply = $cb->collections_entries_curate([
  'id' => 'custom-672852634622144512',
  'changes' => [
  ['op' => 'add', 'tweet_id' => '672727928262828032']
  ]
]);

var_dump($reply);

TON (Twitter Object Nest) API

The TON (Twitter Object Nest) API allows implementers to upload media and various assets to Twitter. The TON API supports non-resumable and resumable upload methods based on the size of the file. For files less than 64MB, non-resumable may be used. For files greater than or equal to 64MB, resumable must be used. Resumable uploads require chunk sizes of less than 64MB.

For accessing the TON API, please adapt the following code samples for uploading:

Single-chunk upload

// single-chunk upload

$reply = $cb->ton_bucket_BUCKET([
  'bucket' => 'ta_partner',
  'Content-Type' => 'image/jpeg',
  'media' => $file
]);

var_dump($reply);

// use the Location header now...
echo $reply->Location;

As you see from that sample, Codebird rewrites the special TON API headers into the reply, so you can easily access them. This also applies to the X-TON-Min-Chunk-Size and X-Ton-Max-Chunk-Size for chunked uploads:

Multi-chunk upload

// multi-chunk upload
$file       = 'demo-video.mp4';
$size_bytes = filesize($file);
$fp         = fopen($file, 'r');

// INIT the upload

$reply = $cb->__call(
  'ton/bucket/BUCKET?resumable=true',
  [[ // note the double square braces when using __call
    'bucket' => 'ta_partner',
    'Content-Type' => 'video/mp4',
    'X-Ton-Content-Type' => 'video/mp4',
    'X-Ton-Content-Length' => $size_bytes
  ]]
);

$target = $reply->Location;
// something like: '/1.1/ton/bucket/ta_partner/SzFxGfAg_Zj.mp4?resumable=true&resumeId=28401873'
$match = [];

// match the location parts
preg_match('/ton\/bucket\/.+\/(.+)\?resumable=true&resumeId=(\d+)/', $target, $match);
list ($target, $file, $resumeId) = $match;

// APPEND data to the upload

$segment_id = 0;

while (! feof($fp)) {
  $chunk = fread($fp, 1048576); // 1MB per chunk for this sample

  // special way to call Codebird for the upload chunks
  $reply = $cb->__call(
    'ton/bucket/BUCKET/FILE?resumable=true&resumeId=RESUMEID',
    [[ // note the double square braces when using __call
      'bucket' => 'ta_partner',
      'file' => $file, // you get real filename from INIT, see above
      'Content-Type' => 'image/jpeg',
      'Content-Range' => 'bytes '
        . ($segment_id * 1048576) . '-' . strlen($chunk) . '/' . $size_bytes,
      'resumeId' => $resumeId,
      'media' => $chunk
    ]]
  );

  $segment_id++;
}

fclose($fp);

Twitter Ads API

The Twitter Ads API allows partners to integrate with the Twitter advertising platform in their own advertising solutions. Selected partners have the ability to create custom tools to manage and execute Twitter Ad campaigns.

When accessing the Ads API or Ads Sandbox API, access it by prefixing your call with ads_. Watch out for the usual replacements for in-url parameters, particularly :account_id.

Tip: For accessing the Ads Sandbox API, use the ads_sandbox_ prefix, like shown further down.

Here is an example for calling the Twitter Ads API:

$reply = $cb->ads_accounts_ACCOUNT_ID_cards_appDownload([
  'account_id' => '123456789',
  'name' => 'Test',
  'app_country_code' => 'DE'
]);

Multiple-method API calls

In the Twitter Ads API, there are multiple methods that can be reached by HTTP GET, POST, PUT and/or DELETE. While Codebird does its best to guess which HTTP verb you’ll want to use, it’s the safest bet to give a hint yourself, like this:

$reply = $cb->ads_sandbox_accounts_ACCOUNT_ID_cards_imageConversation_CARD_ID([
  'httpmethod' => 'DELETE',
  'account_id' => '123456789',
  'card_id' => '2468013579'
]);

Codebird will remove the httpmethod parameter from the parameters list automatically, and set the corresponding HTTP verb.

How Do I…?

…use multiple Codebird instances?

By default, Codebird works with just one instance. This programming paradigma is called a singleton.

Getting the main Codebird object is done like this:

$cb = \Codebird\Codebird::getInstance();

If you need to run requests to the Twitter API for multiple users at once, Codebird supports this as well. Instead of getting the instance like shown above, create a new object:

$cb1 = new \Codebird\Codebird;
$cb2 = new \Codebird\Codebird;

Please note that your OAuth consumer key and secret is shared within multiple Codebird instances, while the OAuth request and access tokens with their secrets are not shared.

…access a user’s profile image?

First retrieve the user object using

$reply = $cb->users_show("screen_name=$username");

with $username being the username of the account you wish to retrieve the profile image from.

Then get the value from the index profile_image_url or profile_image_url_https of the user object previously retrieved.

For example:

$reply['profile_image_url'] will then return the profile image url without https.

…get user ID, screen name and more details about the current user?

When the user returns from the authentication screen, you need to trade the obtained request token for an access token, using the OAuth verifier. As discussed in the section ‘Usage example,’ you use a call to oauth/access_token to do that.

The API reply to this method call tells you details about the user that just logged in. These details contain the user ID and the screen name.

Take a look at the returned data as follows:

stdClass Object
(
  [oauth_token] => 14648265-rPn8EJwfB**********************
  [oauth_token_secret] => agvf3L3**************************
  [user_id] => 14648265
  [screen_name] => jublonet
  [httpstatus] => 200
)

If you need to get more details, such as the user’s latest Tweet, you should fetch the complete User Entity. The simplest way to get the user entity of the currently authenticated user is to use the account/verify_credentials API method. In Codebird, it works like this:

$reply = $cb->account_verifyCredentials();
print_r($reply);

I suggest to cache the User Entity after obtaining it, as the account/verify_credentials method is rate-limited by 15 calls per 15 minutes.

…walk through cursored results?

The Twitter REST API utilizes a technique called ‘cursoring’ to paginate large result sets. Cursoring separates results into pages of no more than 5000 results at a time, and provides a means to move backwards and forwards through these pages.

Here is how you can walk through cursored results with Codebird.

  1. Get the first result set of a cursored method:
$result1 = $cb->followers_list();
  1. To navigate forth, take the next_cursor_str:
$nextCursor = $result1->next_cursor_str;
  1. If $nextCursor is not 0, use this cursor to request the next result page:
  if ($nextCursor > 0) {
    $result2 = $cb->followers_list('cursor=' . $nextCursor);
  }

To navigate back instead of forth, use the field $resultX->previous_cursor_str instead of next_cursor_str.

It might make sense to use the cursors in a loop. Watch out, though, not to send more than the allowed number of requests to followers/list per rate-limit timeframe, or else you will hit your rate-limit.

…use xAuth with Codebird?

Codebird supports xAuth just like every other authentication used at Twitter. Remember that your application needs to be whitelisted to be able to use xAuth.

Here’s an example:

$reply = $cb->oauth_accessToken([
  'x_auth_username' => 'username',
  'x_auth_password' => '4h3_p4$$w0rd',
  'x_auth_mode' => 'client_auth'
]);

Are you getting a strange error message? If the user is enrolled in login verification, the server will return a HTTP 401 error with a custom body. If you are using the send_error_codes parameter, you will receive the following error message in the response body:

<?xml version="1.0" encoding="UTF-8"?>
<errors>
<error code="231">User must verify login</error>
</errors>

Otherwise, the response body will contain a plaintext response:

User must verify login

When this error occurs, advise the user to generate a temporary password on twitter.com and use that to complete signing in to the application.

…know what cacert.pem is for?

Connections to the Twitter API are done over a secured SSL connection. Codebird-php checks if the Twitter API server has a valid SSL certificate. Valid certificates have a correct signature-chain. The cacert.pem file contains a list of all public certificates for root certificate authorities. You can find more information about this file at http://curl.haxx.se/docs/caextract.html.

…set the timeout for requests to the Twitter API?

For connecting to Twitter, Codebird uses the cURL library, if available. You can specify both the connection timeout and the request timeout, in milliseconds:

$cb->setConnectionTimeout(2000);
$cb->setTimeout(5000);

If you don't specify the timeout, codebird uses these values:

  • connection time = 3000 ms = 3 s
  • timeout = 10000 ms = 10 s

…disable cURL?

Codebird automatically detects whether you have the PHP cURL extension enabled. If not, the library will try to connect to Twitter via socket. For this to work, the PHP setting allow_url_fopen must be enabled.

You may also manually disable cURL. Use the following call:

$cb->setUseCurl(false);

…use a proxy?

Codebird allows proxy support for both cURL handles and sockets.

To activate proxy mode, use the following call:

$cb->setProxy('<host>', '<port>');

You may also use an authenticated proxy. Use the following call:

$cb->setProxy('<host>', '<port>');
$cb->setProxyAuthentication('<username>:<password>');

By default, a HTTP proxy is assumed. To use a different proxy type, use the corresponding CURLPROXY_* constants, like this:

$cb->setProxy('<host>', '<port>', CURLPROXY_SOCKS5);

…quote a Tweet?

Quoting a Tweet is different from a Retweet because you may add your own text. The original Tweet will appear below your quote. To quote a Tweet, add a link to the original Tweet to your quote, like in this sample:

$original_tweet = [
  'id_str' => '684483801687392256',
  'user' => [
    'screen_name' => 'LarryMcTweet'
  ]
];
$original_tweet = (object) $original_tweet; // sample, get real Tweet from API

$id = $original_tweet->id_str; // use the `id_str` field because of long numbers
$screen_name = $original_tweet->user->screen_name;

// looks like this: https://twitter.com/LarryMcTweet/status/684483801687392256
$url = "https://twitter.com/$screen_name/status/$id";
$text = 'I’d like to quote a Tweet.'; // maximum length = 140 minus 24 (link length) minus 1 space

$reply = $cb->statuses_update([
  'status' => "$text $url"
]);

codebird-php's People

Contributors

adamwiw avatar codacy-badger avatar edent avatar fabfab avatar fabiancz avatar felds avatar freen avatar jeka-kiselyov avatar jmgomezpoveda avatar joshuaatkins avatar lifeboatpres avatar masev avatar matgargano avatar maxime-pasquier avatar mihroot avatar milesokeefe avatar miteshashar avatar mtricht avatar mynetx avatar oldjp avatar prodigyview avatar robholmes avatar scrutinizer-auto-fixer avatar sl4mmer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

codebird-php's Issues

Support internal API methods

In addition to the API methods publicly documented, the Twitter API also supports undocumented API methods, often for use by internal or official apps. Codebird may support them (or part of them).

Searching more than 100 tweets

Hi there,

I think this is not considered as an issue, but I would like to know if it possible to get more than 100 results searching in tweets.

// Search parameters
$q = '%23Goya2014'; 
$count = 1000; 
$params = array('screen_name' => $q,'q' => $q,'count' => $count);
$data = (array) $cb->search_tweets($params);

It always gets 100 results only. I tried to use a "do while" and cursors, but it is not working. That is the code:

$q = '%23Goya2014'; 
$cursor = -1;
 do 
 { //navigate cursors loop
    $count = 100; 
    $params = array('screen_name' => $q,'q' => $q,'count' => $count, 'cursor' => $cursor);
    $data = (array) $cb->search_tweets($params);
   // Extract data from array $data on $tuitero array 
    foreach ($data['statuses'] as $estatuses) 
     {
    foreach ($estatuses as $usuario)
         {
        if (!empty($usuario->screen_name)) 
         { 
          $tuitero[$usuario->id] = $usuario->screen_name; 
                 }
         }
    }
   $cursor = $cb->next_cursor_str; // Skip to next cursor
 } while ($cursor > 0);

I really appreciate your help.
Luis

Add unit testing suite

Codebird should contain a test suite for finding regressions and hidden bugs. The test suite should use a test framework, for example phpunit.

issues with header('Location: ' . basename(__FILE__)); in authorization flow

Hi,

i'm using the authorization flow as you have it in your readme file and I thought I should share a couple of issues i had

both are related to this line

// send to same URL, without oauth GET parameters
header('Location: ' . basename(FILE));

the first is (obviously) that the parameters are removed using this line. I'm using a dynamic url as the callback (file.php?id=123) and this really messed this up

the second issue is that i was using this code in an include file

eg the code was included in the header.php include file and this code was forcing a redirect to header.php directly

other than that, great libray, it was a huge help :)

Make `private` members and methods `protected`

Having all members and some methods private makes it impractical or impossible to extend the class.

For example, If one wishes to use a different transport than cURL, this could be accomplished by overriding _callApi(), but since _oauth_consumer_key and _oauth_consumer_secret are both private, it is not possible to provide a custom implementation of this method while maintaining all functionality.

For ultimate flexibility, Codebird should make private methods and members protected (where it makes sense) or provide alternative getters to access the members.

search/tweets

Hi, I was trying to retrieve list of tweets based on the search API search/tweets. This is my code:

//Twitter OAuth Settings
....

//Get authenticated
Codebird::setConsumerKey($CONSUMER_KEY, $CONSUMER_SECRET);
$cb = Codebird::getInstance();
$cb->setToken($ACCESS_TOKEN, $ACCESS_TOKEN_SECRET);

$params = array(
    'q' => 'jquery'
)

$data = (array) $cb->search_tweets($params);
print_r($data);

It works with user_timeline, but i can't get it working with search.

oauth_callback value 'oob'

Hi

When I run

    $reply = $cb->oauth_requestToken(array(
    'oauth_callback' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']
        ));

like in the documentation, the $reply is:

    [request] => /oauth/request_token [error] => Desktop applications only support the oauth_callback value 'oob' [httpstatus] => 401 

How to fix this?

Getting Error 89 even though Tokens have not expired

Hi - I may be doing this totally wrong - but I'm getting an error 89 when I try to use the example from the readme. I've checked my dev.twitter account and the keys still seem to be valid, so I'm not sure what would be causing the problem. Any ideas?

Here's my code:

/* Copied from example and modified slightly */
function get_tweets(){

require_once ('library/lib/codebird.php');

\Codebird\Codebird::setConsumerKey('$CONSUMER_KEY', '$CONSUMER_SECRET'); // static, see 'Using multiple Codebird instances'
$cb = \Codebird\Codebird::getInstance();
$cb->setToken('$ACCESS_TOKEN', '$ACCESS_TOKEN_SECRET');
$cb->setReturnFormat(CODEBIRD_RETURNFORMAT_ARRAY);

$params=array(
    'screen_name'=>'hurdlers',
    'count'=>5
);

try {
    $data = (array) $cb->statuses_userTimeline($params);
}
catch(Exception $e) { return $e; }

if(isset($data['errors'])) return $data['errors'];
//Output result in JSON, getting it ready for jQuery to process
return json_encode($data);

}

Cursor issue

Sorry to bother with this, might be a stupid question, but how do I work with the 'cursor' setting?

For example: if you retrieve a followers list, you can go to the next page by giving a cursor param in the url.

See: https://dev.twitter.com/docs/api/1.1/get/followers/list and https://dev.twitter.com/docs/misc/cursoring

Is this option missing, or am I doing something wrong? Besides that: very usefull script! Took me a while for I found this one, tried 100 others, but this is the best thus far.

Uncaught exception 'Exception' with message 'To get the authorize URL, the OAuth token must be set.' in /home/teenisit/public_html/twitterapp/codebird.php:316

Hi,
I've seen this issue already but it seems to be back, has anyone found a solution?

I'm using version 2.4.0 and the code from the readme. weird issue, the code runs perfectly on my localhost, but as soon as I upload all the files on my cpanel at teenisity.com/twitterapp, I get this strange error.

i have changed the URL and callback in twitter, recreated the keys, everything...
Please guide me, any idea would be welcome!

Data retrieval only at the first time

Here is the code :

require_once ('codebird.php');
\Codebird\Codebird::setConsumerKey('XXXX', 'XXXXX');

$cb = \Codebird\Codebird::getInstance();

session_start();
if (! isset($_GET['oauth_verifier'])) {
// gets a request token
$reply = $cb->oauth_requestToken(array(
'oauth_callback' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']
));

// stores it
$cb->setToken($reply->oauth_token, $reply->oauth_token_secret);
$_SESSION['oauth_token'] = $reply->oauth_token;
$_SESSION['oauth_token_secret'] = $reply->oauth_token_secret;
$_SESSION['oauth_verified']=FALSE;
// gets the authorize screen URL
$auth_url = $cb->oauth_authorize();
header('Location: ' . $auth_url);
die();

} elseif (! isset($_SESSION['oauth_verified'])) {
// gets the access token
$cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$reply = $cb->oauth_accessToken(array(
'oauth_verifier' => $_GET['oauth_verifier']
));
// store the authenticated token, which may be different from the request token (!)
$_SESSION['oauth_token'] = $reply->oauth_token;
$_SESSION['oauth_token_secret'] = $reply->oauth_token_secret;
$cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$_SESSION['oauth_verified'] = true;
}
$reply = (array) $cb->statuses_homeTimeline();
print_r($reply);

It get me array of home timeline for user on the very first hit when new session starts but gives error on refresh. Error :
Array ( [0] => stdClass Object ( [message] => Bad Authentication data [code] => 215 ) ) [httpstatus] => 400 )

Issue with PHP String

I'm using codebird 2.4.1 and got problem on
here's my code:

<h2>Verifikasi ID Twitter...</h2>

<?php

require_once('codebird.php');
$cb = new Codebird\Codebird;

$id = $_POST['id'];
$pass = $_POST['pass'];

 //if($_POST['loginTwitter'] == "Submit"){
 //if(isset($_POST['loginTwitter'])){
 if(isset($id) && isset($pass)){
    //cek bener apa engga nya

    $CONSUMER_KEY = 'consumerkey';
    $CONSUMER_SECRET = 'consumersecret';
    $ACCESS_TOKEN = 'accesstoken';
    $ACCESS_TOKEN_SECRET = 'accesstokensecret';

    //if($_POST['id'] == '' || $_POST['pass'] == ''){ header('login.php'); }

    $cb::setConsumerKey($CONSUMER_KEY, $CONSUMER_SECRET);
    $cb::getInstance();
    $cb->setToken($ACCESS_TOKEN, $ACCESS_TOKEN_SECRET);

    $code = $cb->account_verifyCredentials();
    if($code == 200){ 
        //$reply = $cb->oauth2_token();
        //$bearer_token = $reply->access_token;
        header('upload.php');
    }
    else{ header('login.php'); }

  //echo "Redirect to Upload Image...."; for($i = 0; $i <= 20000000; $i++); header('upload.php');
 }

 else{
    echo "<br />";
    echo "<h3>Something went wrong... Please Check Again Your Credentials</h3>";
  }

?>

my error report:
Parse error: syntax error, unexpected '$CONSUMER_SECRET' (T_VARIABLE) in C:\wamp\www\twcodebird\verifikasi.php on line 17

i don't know where's the error part. FYI i'm using PHP 5.4.12, curl and openssl module already active

Ampersand in statuses_update string

I’m seeing an issue with passing a string that contains & to statuses_update().

If I run no encoding, the API responds Could not authenticate you. If I use urlencode(), the ampersand is encoded twice, &amp;#038; in the response.

Is there a preferable way to encode the string before passing to statuses_update()?

Could be related to #7.

Problem with users_show()

I'm having a problem getting the users_show() call to work, but only when I try to call it as an authorized user.

The non-working code:

\Codebird\Codebird::setConsumerKey($consumerKey, $consumerSecret);
$cb = \Codebird\Codebird::getInstance();
$cb->setToken($userAccessKey, $userSecret);
$params = array(
    'include_entities' => FALSE,
    'screen_name' => $screenName
);
$reply = $cb->users_show($params);

This returns error 32 - "Could not authenticate you"

If I call users_show() with app-only authorization, however, it works.

I know that $userAccessKey and $userSecret contain valid oauth keys - the same code above works fine if I call statuses_update() to post a test message, with user authorization.

JSON return type

I would like to cache results with a wrapper class.
To store a response in the DB, I would like to have the original JSON string returned from the server.

For example:

<?php
$cb->setReturnFormat(CODEBIRD_RETURNFORMAT_JSON);
$json_string = $cb->statuses_userTimeline(array('screen_name'=>'example'));

Undefined property: stdClass::$oauth_token

Hi
My website is working fine locally (PHP version 5.3.15) but when I test on the server I'm getting this error:

Notice: Undefined property: stdClass::$oauth_token

The PHP version of the server is 5.3.27 and I have a feeling it may be to do with namespaces. Is there a way to initialise Codebird without namespace?

I'm initialising the same as the example, the CONSUMER_KEY and CONSUMER_SECRET are defined in my config file.

require_once ('../inc/twitter/codebird.php');
\Codebird\Codebird::setConsumerKey(CONSUMER_KEY, CONSUMER_SECRET);
$cb = \Codebird\Codebird::getInstance();

Thanks!

documentation error

Hello, i was trying to configure an example with your library an follow the README documentation but i get an error:

$reply = $cb->oauth_requestToken(array(
'oauth_callback' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']
));

this line of code dosent work, the oauth_requestToken function not existe.

Thanks

Support muting API methods

POST mutes/users/create Mutes the user specified in the ID parameter for the authenticating user. Returns the muted user in the requested format when successful. Returns a string describing the failure condition when unsuccessful. Actions taken in this method are asynchronous and changes will be eventually consistent.
POST mutes/users/destroy Un-mutes the user specified in the ID parameter for the authenticating user. Returns the unmuted user in the requested format when successful. Returns a string describing the failure condition when unsuccessful. Actions taken in this method are asynchronous and changes will be eventually...
GET mutes/users/ids Returns an array of numeric user ids the authenticating user has muted.
GET mutes/users/list Returns an array of user objects the authenticating user has muted.

https://dev.twitter.com/discussions/28358

List Statuses only returning tweets from one user

I am using

$cb->lists_statuses('slug=teams&owner_screen_name=MLS&count=4');

And although I am getting 4 tweets, they are all from one user. The user happens to be someone with the most recent tweet. The next tweet in that list is not from this user.

Any ideas why that may be?

Error 77 while validating the Twitter API certificate.

hi, I'm trying to use the code on your readme. I just copied and pasted it, and changed the keys to match my app credentials. When I try to load my page, I get : Exception: Error 77 while validating the Twitter API certificate. [C:\wamp\www\Twitterapp\codebird.php:891] and nothing happens. Any idea where it could come from?

Class 'Codebird' not found (I might just be stupid)

I doubt this a problem with Codebird, it's much more likely a problem with me, and I think I'm just overly tired and missing something.

I have this:

require 'codebird.php';

 Codebird::setConsumerKey($consumer_key, $consumer_secret);

And I just keep getting a fatal error saying it can't find the Codebird class. codebird.php is in the same directory as the file calling it. I just downloaded the files from github today. I'm on php > 5.3. I know the file is getting called because I can write an error_log statement after the namespace declaration in codebird.php and it appears in my log.

I plead temporary insanity. Please tell me it's something easy I'm overlooking...

OAuth broken

In Codebird 2.3.0, when trying to authenticate with OAuth and callback, you will always receive the following error:

Failed to validate token

This is due to changes in the way how Codebird sends the authentication data.

Please upgrade to Codebird 2.3.1.

Posting status update adding slashes (escaping double & single quotes)

Here is my code:

$twitter_text = "This is 'sample' text & it causes problems."; $reply = $cb->statuses_update('status='.urlencode($twitter_text));

If I don't use urlencode() the tweet gets cut off at the ampersand. With or without urlencode() single quotes show up as ' and the double quotes show up as "

I've tried using stripslashes() to no effect.

"Could Not Validate You" Message when trying to get list status

Hi, This library is great. Thanks for putting it out there. I'm having a bit of trouble getting a list's tweets. I issue the following call:

$reply = (array) $this->codebird->lists_statuses($list_id);

That yields an error:

array(2) {
  ["errors"]=>
  array(1) {
    [0]=>
    object(stdClass)#275 (2) {
      ["message"]=>
      string(26) "Could not authenticate you"
      ["code"]=>
      int(32)
    }
  }
  ["httpstatus"]=>
  int(401)
}

When I simply call for the user's timeline like this:

reply = (array) $this->codebird->statuses_homeTimeline();

everything goes alright.

I noticed in the Twitter API documentation that the call to status_list requires authentication, whereas the call to the home timeline only requires the user context. When I instantiated the codebird object, I provided the consumer key and secret and set the token and secret in the code directly.

Does anyone have any ideas why I might be getting this error?

Exception: missing OAuth token for authorize URL

Fatal error: Uncaught exception 'Exception' with message 'To get the authorize URL, the OAuth token must be set.

0 C:\xampp\htdocs\test1\index.php(69): Codebird->oauth_authorize()
#1 {main}

Any hint?

How to use it with CodeIgniter

Any one tried using it with CI? I couldn't get it to work. If you know a way to do that or a CI port of this, please let us know.

-Thanks!

Error 77 while validating the Twitter API certificate.

The script can`t find the "cacert.pem" file then returns "Error 77 while validating the Twitter API certificate.", but the certificate file is in the right place.

I resolved replacing line 906 in codebird.php:

Before:

curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');

After:

curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');

cacert.pem

Installed 2.4.1 with the certificate file, but can you please explain the purpose of that file? Is it some kind of protection against exploits?

oauth_requestToken internal 500 error

private $CI;
private $connection;
private $api_key;
private $api_secret;
private $callback_url;

public function __construct() 
{
    $this->CI =& get_instance();

    $params = $this->CI->config->item('twitter', 'mythos');
    $this->api_key = $params['api_key'];
    $this->api_secret = $params['api_secret'];
    $this->callback_url = $params['callback_url'];

    \Codebird\Codebird::setConsumerKey($this->api_key, $this->api_secret);
    $this->connection = \Codebird\Codebird::getInstance();

    $this->CI->load->library('session');
}

public function getConnection() 
{
    $callback = array();
    $callback['oauth_callback'] = $this->callback_url;
    return $this->connection->oauth_requestToken($callback);
}

hi! I just wanted to know if there's something wrong with the oauth_requestToken function? i'm not sure why but I get a internal 500 server error whenever I use it.

Thanks for the advance feedback!

Expired token

Getting an expired token notice from the return, does your code regenerate the signature each time, any ideas?:

Array ( [errors] => Array ( [0] => stdClass Object ( [message] => Invalid or expired token [code] => 89 ) ) [httpstatus] => 401 )

App-only authorization

Been running some tests and it seems I can use app-only authorization without ever calling setBearerToken(). The following code works:

\Codebird\Codebird::setConsumerKey($twitterConsumerKey, $twitterConsumerSecret);
$cb = \Codebird\Codebird::getInstance();
$reply = $cb->users_show(array('screen_name' => 'USERNAME'), TRUE);

Is this how it's supposed to work? Can I use code like this or is it necessary to call setBearerToken() each time for some reason that I'm missing?

Invalid or expired token

I see this has been raised before but as far as I can see it's till an issue...

my code is basically cut and paste from git example:

oauth_requestToken(array( 'oauth_callback' => 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] )); ``` // stores it $cb->setToken($reply->oauth_token, $reply->oauth_token_secret); $_SESSION['oauth_token'] = $reply->oauth_token; $_SESSION['oauth_token_secret'] = $reply->oauth_token_secret; // gets the authorize screen URL $auth_url = $cb->oauth_authorize(); header('Location: ' . $auth_url); die(); ``` } else { // gets the access token $cb->setToken($_SESSION['oauth_token'], $_SESSION['oauth_token_secret']); $reply = $cb->oauth_accessToken(array( 'oauth_verifier' => $_GET['oauth_verifier'] )); // store the authenticated token, which may be different from the request token (!) $_SESSION['oauth_token'] = $reply->oauth_token; $_SESSION['oauth_token_secret'] = $reply->oauth_token_secret; } $reply = (array) $cb->statuses_homeTimeline(); ``` print_r($reply); echo '
'; ``` //Tweeting is as easy as this: $reply = $cb->statuses_update('status=Whohoo, I just tweeted!'); ``` print_r($reply); echo '
'; ``` //When uploading files to Twitter, the array syntax is obligatory: $params = array( 'status' => 'test @dm_dev_test1 #test', 'media[]' => 'test.jpg' ); $reply = $cb->statuses_updateWithMedia($params); ``` print_r($reply); echo '
'; ``` ?>

if I open page in browser i get the twitter authorise window and then:

Array ( [errors] => Array ( [0] => stdClass Object ( [message] => Invalid or expired token [code] => 89 ) ) [httpstatus] => 401 )
stdClass Object ( [errors] => Array ( [0] => stdClass Object ( [message] => Invalid or expired token [code] => 89 ) ) [httpstatus] => 401 )
stdClass Object ( [errors] => Array ( [0] => stdClass Object ( [message] => Invalid or expired token [code] => 89 ) ) [httpstatus] => 401 )

if I immediately reload page the tweets are correctly submitted to twitter and i can see them in my twitter feed.

if I reload the page one more time i get:

Array ( [errors] => Array ( [0] => stdClass Object ( [message] => Bad Authentication data [code] => 215 ) ) [httpstatus] => 400 )
stdClass Object ( [errors] => Array ( [0] => stdClass Object ( [message] => Bad Authentication data [code] => 215 ) ) [httpstatus] => 400 )
stdClass Object ( [errors] => Array ( [0] => stdClass Object ( [message] => Bad Authentication data [code] => 215 ) ) [httpstatus] => 400 )

any ideas what's going wrong here?
many thanks.

following tweet returns a 401 cannot authenticate error

Here is the tweet and the code. Its 139 characters. Can't figure out why its failing. Thoughts?

$msg = 'Hello writers & followers. Trying new svc from @statusfactory1 to get more posts to you. Watch 4 event & mtg updates & hopefully much more.';

$response = $cb->statuses_update('status=' . $msg);

Issues when pulling statuses/user_timeline

Hi there,

I'm using codebird.php for search/tweets, and it works wonderfully - However, I am having issues getting the user timeline to display.

I understand that there are certain permissions needed from a User in order to allow it to read your tweets - but I have tested with the owner of my Twitter App, and still getting Code 32: Could not authenticate you.

I am using the setToken method with my access token of the App. I am then using this code.

\Codebird\Codebird::setConsumerKey($CONSUMER_KEY, $CONSUMER_SECRET);
$cb = \Codebird\Codebird::getInstance();
$cb->setToken($ACCESS_TOKEN, $ACCESS_TOKEN_SECRET)

$api = 'statuses/userTimeline';
$params['screen_name'] = $username; // App owner - the app has read access 
$params['include_rts'] =  true;
$params['count'] = 5;

$data = (array) $cb->$api($params);

What am I doing wrong? Do I need to use the access token generation method rather than the hard-coded Application access token?

Can't authenticate with valid keys.

i get error
Failed to validate oauth signature and token
When i am trying authenticate using codebird.

I check for authentication headers - it's ok. There is authentication string from _sign method
Authorization: OAuth oauth_consumer_key="wpuw73TTHj0Myt6r1lWaA", oauth_nonce="ee0a9740", oauth_signature="1OJOHtNNcMYQbTkTxdh3yZ8tBZE%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1366032354", oauth_token="7", oauth_version="1.0"

I even update codebird, but error does not disappear (this code worked in past).
What is wrong?

Don't require consumer key if bearer token is already known (was: App Only Authentication Issue on Search)

I'm having an issue with app only authentication after setting a bearer token:

    $cb = Codebird::getInstance();
    Codebird::setBearerToken($twitter_bearer_token);

    $params = array (
        'q'             => urlencode('#hashtag'),
        'result_type'   => 'recent',
        'count'         => '10'
    );

    $response = $cb->search_tweets($params, true);

The Error I'm getting is:

Fatal error: Uncaught exception 'Exception' with message 'To generate a signature, the consumer key must be set.' in project/vendor/jublonet/codebird-php/src/codebird.php:502
Stack trace:
#0 project/vendor/jublonet/codebird-php/src/codebird.php(863): Codebird\Codebird->_sign('GET', 'https://api.twi...', Array)
#1 project/vendor/jublonet/codebird-php/src/codebird.php(294): Codebird\Codebird->_callApi('GET', 'search/tweets', 'search/tweets', Array, false, true)
#2 project/index.php(19): Codebird\Codebird->__call('search_tweets', Array)
#3 project/index.php(19): Codebird\Codebird->search_tweets(Array, true)
#4 {main}
  thrown in project/vendor/jublonet/codebird-php/src/codebird.php on line 502

It looks like it's not respecting the "true" for app only auth and still checking for consumer key/secrets for account authentication.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.