Last active
February 2, 2020 02:29
-
-
Save Deozaan/87e90c6679240f81743f to your computer and use it in GitHub Desktop.
Tweeter for Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************* | |
* Tweeter for Unity | |
* by Deozaan | |
* Description and instructions: https://tweeter.deozaan.com/ | |
* URL: https://gist.github.com/Deozaan/87e90c6679240f81743f | |
* Questions or Feedback? http://twitter.com/Deozaan | |
*******************************************************************/ | |
using System.Collections; | |
using System.Security.Cryptography; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
public class Tweeter : MonoBehaviour { | |
[SerializeField] | |
private string accessTokenSecret = ""; // LEAVE THIS BLANK IN PRODUCTION! | |
private static string tweeterURL = "URL_PATH_HERE" + "tweeter.php?status="; | |
public bool tweetInEditor = false; | |
void Start() { | |
DontDestroyOnLoad(gameObject); | |
if (string.IsNullOrEmpty(accessTokenSecret)) { | |
Debug.LogError("No access token assigned. Things won't work."); | |
} | |
if (tweeterURL == "URL_PATH_HEREtweeter.php?status=") { | |
Debug.LogError("You forgot to change the tweeter URL. Things won't work."); | |
} | |
} | |
/// <summary>Sends a tweet.</summary> | |
/// <param name="tweet">The tweet/status message.</param> | |
public static void Send(string tweet) { | |
if (Application.isEditor && !Instance.tweetInEditor) { return; } | |
if (!string.IsNullOrEmpty(Instance.accessTokenSecret)) { | |
Instance.StartCoroutine(Instance.SendTweetCoroutine(tweet)); | |
} | |
} | |
private IEnumerator SendTweetCoroutine(string tweet) { | |
string signature = CreateSignature(tweet); | |
string fullURL = tweeterURL + UnityWebRequest.EscapeURL(tweet) + "&hash=" + UnityWebRequest.EscapeURL(signature); | |
//Debug.Log(fullURL); | |
var w = UnityWebRequest.Get(fullURL); | |
w.SendWebRequest(); | |
yield return w; | |
if (!string.IsNullOrEmpty(w.error)) { | |
Debug.LogWarning("Tweet failed: " + w.error); | |
} else { | |
if (w.downloadHandler.text.StartsWith("{\"errors\"")) { | |
Debug.LogWarning("Tweet failed: " + w.downloadHandler.text); | |
} else { | |
#if UNITY_EDITOR | |
Debug.Log("Tweet Succeeded? " + w.downloadHandler.text); | |
#endif | |
} | |
} | |
} | |
private byte[] String2Bytes(string myString) { | |
return System.Text.Encoding.UTF8.GetBytes(myString); | |
} | |
private string CreateSignature(string signMe) { | |
return CreateSignature(signMe, accessTokenSecret); | |
} | |
private string CreateSignature(string signMe, string token) { | |
//Debug.Log("Hashing the following text: \n" + signMe); | |
HMACSHA1 signature = new HMACSHA1(String2Bytes(token)); | |
byte[] hash = signature.ComputeHash(String2Bytes(signMe)); | |
return System.Convert.ToBase64String(hash); | |
} | |
#region Singleton Behavior | |
private static Tweeter _mInstance; | |
public static Tweeter Instance { | |
get { | |
if (!_mInstance) { | |
Tweeter[] managers = GameObject.FindObjectsOfType(typeof(Tweeter)) as Tweeter[]; | |
if (managers.Length != 0) { | |
if (managers.Length == 1) { | |
_mInstance = managers[0]; | |
_mInstance.gameObject.name = typeof(Tweeter).Name; | |
return _mInstance; | |
} else { | |
Debug.LogError("You have more than one Tweeter in the scene. You only need 1, it's a singleton!"); | |
// delete all instances, saving only one (if any) if it has the secret | |
bool secretFound = false; | |
for (int i = 0; i < managers.Length; i++) { | |
if (secretFound || string.IsNullOrEmpty(managers[i].accessTokenSecret)) { | |
// and instance with the secret already exists or the secret is null/empty | |
Destroy(managers[i].gameObject); | |
} else { | |
// this is the first one we've found that has a non-null/empty secret. Keep it! | |
secretFound = true; | |
} | |
} | |
} | |
} else { | |
Debug.LogWarning("There were no Tweeters in the scene. Creating one with default accessToken.\n" + | |
"If default token is blank (as it should be) then tweeting won't work."); | |
GameObject gO = new GameObject(typeof(Tweeter).Name, typeof(Tweeter)); | |
_mInstance = gO.GetComponent<Tweeter>(); | |
DontDestroyOnLoad(gO); | |
} | |
} | |
return _mInstance; | |
} | |
set { | |
_mInstance = value as Tweeter; | |
} | |
} | |
#endregion | |
} | |
public static class TweeterExtensions { | |
/// <summary>Truncates a string if it is longer than length. | |
/// This also removes the @ symbol at the beginning of the string since truncation will break twitter username link.</summary> | |
/// <param name="length">The max length of the string before truncation occurs.</param> | |
public static string Truncate(this string mString, int length) { | |
return (mString.Length > length) ? | |
mString.RemoveAtSymbol().Substring(0, length) : // remove the @ sign since truncating the name will break link to Twitter username | |
mString; // name is <= length so no changes necessary | |
} | |
/// <summary>Removes @ from the beginning of a string, if one is present</summary> | |
public static string RemoveAtSymbol(this string mString) { | |
return (mString.StartsWith("@")) ? mString.Substring(1) : mString; | |
} | |
/// <summary>Prefixes a string with a period if it would otherwise start with an @ symbol. | |
/// This is useful to make messages show up in all feeds instead of being a pseudo-direct message.</summary> | |
public static string PrefixPeriod(this string mString) { | |
return (mString.StartsWith("@")) ? "." + mString : mString; | |
} | |
/// <summary>Gives the first name from a string where words are separated by a space.</summary> | |
public static string GetFirstName(this string mString) { | |
return mString.Split(' ')[0]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** Set access tokens here - see: https://dev.twitter.com/apps/ **/ | |
$settings = array( | |
'oauth_access_token' => "ACCESS_TOKEN", | |
'oauth_access_token_secret' => "ACCESS_TOKEN_SECRET", | |
'consumer_key' => "CONSUMER_API_KEY", // API Key | |
'consumer_secret' => "CONSUMER_API_SECRET" // API Secret | |
); | |
/** Set redirect (on failure) URL here **/ | |
$redirecturl = 'URL_HERE'; | |
// https://github.com/Deozaan/twitter-api-php | |
require_once('TwitterAPIExchange.php'); // make sure this url is correct | |
$url = 'https://api.twitter.com/1.1/statuses/update.json'; | |
$requestMethod = 'POST'; | |
$myStatus = html_entity_decode(htmlentities($_GET["status"], ENT_COMPAT, "UTF-8", false)); | |
$myHash = htmlentities($_GET["hash"], ENT_COMPAT, "UTF-8", false); | |
$scaryStatus = $_GET["status"]; | |
$postfields = array( | |
'status' => $myStatus, | |
); | |
// make sure we have both a status and a hash | |
if (!empty($myStatus) && isset($myStatus) && !empty($myHash) && isset($myHash)) { | |
// verify the hashes match | |
$newHash = base64_encode(hash_hmac('sha1', $myStatus, $settings['oauth_access_token_secret'], true)); | |
if ($newHash === $myHash) { | |
// hashes match, so send the tweet! | |
$twitter = new TwitterAPIExchange($settings); | |
echo $twitter ->buildOAuth($url, $requestMethod)->setPostfields($postfields)->performRequest(); | |
} else { | |
?> | |
<link rel="icon" type="image/png" href="icon.png"> | |
Failed. | |
<script type="text/javascript"> | |
<!-- | |
window.location = "<?php echo $redirecturl ?>" | |
//--> | |
</script> | |
<?php | |
} | |
} | |
?> | |
<link rel="icon" type="image/png" href="icon.png"> | |
<script type="text/javascript"> | |
<!-- | |
window.location = "<?php echo $redirecturl ?>" | |
//--> | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Twitter-API-PHP : Simple PHP wrapper for the v1.1 API | |
* | |
* PHP version 5.3.10 | |
* | |
* @category Awesomeness | |
* @package Twitter-API-PHP | |
* @author James Mallison <me@j7mbo.co.uk> | |
* @license MIT License | |
* @link http://github.com/j7mbo/twitter-api-php | |
*/ | |
class TwitterAPIExchange | |
{ | |
private $oauth_access_token; | |
private $oauth_access_token_secret; | |
private $consumer_key; | |
private $consumer_secret; | |
private $postfields; | |
private $getfield; | |
protected $oauth; | |
public $url; | |
/** | |
* Create the API access object. Requires an array of settings:: | |
* oauth access token, oauth access token secret, consumer key, consumer secret | |
* These are all available by creating your own application on dev.twitter.com | |
* Requires the cURL library | |
* | |
* @param array $settings | |
*/ | |
public function __construct(array $settings) | |
{ | |
if (!in_array('curl', get_loaded_extensions())) | |
{ | |
throw new Exception('You need to install cURL, see: http://curl.haxx.se/docs/install.html'); | |
} | |
if (!isset($settings['oauth_access_token']) | |
|| !isset($settings['oauth_access_token_secret']) | |
|| !isset($settings['consumer_key']) | |
|| !isset($settings['consumer_secret'])) | |
{ | |
throw new Exception('Make sure you are passing in the correct parameters'); | |
} | |
$this->oauth_access_token = $settings['oauth_access_token']; | |
$this->oauth_access_token_secret = $settings['oauth_access_token_secret']; | |
$this->consumer_key = $settings['consumer_key']; | |
$this->consumer_secret = $settings['consumer_secret']; | |
} | |
/** | |
* Set postfields array, example: array('screen_name' => 'J7mbo') | |
* | |
* @param array $array Array of parameters to send to API | |
* | |
* @return TwitterAPIExchange Instance of self for method chaining | |
*/ | |
public function setPostfields(array $array) | |
{ | |
if (!is_null($this->getGetfield())) | |
{ | |
throw new Exception('You can only choose get OR post fields.'); | |
} | |
if (isset($array['status']) && substr($array['status'], 0, 1) === '@') | |
{ | |
$array['status'] = sprintf("\0%s", $array['status']); | |
} | |
$this->postfields = $array; | |
return $this; | |
} | |
/** | |
* Set getfield string, example: '?screen_name=J7mbo' | |
* | |
* @param string $string Get key and value pairs as string | |
* | |
* @return \TwitterAPIExchange Instance of self for method chaining | |
*/ | |
public function setGetfield($string) | |
{ | |
if (!is_null($this->getPostfields())) | |
{ | |
throw new Exception('You can only choose get OR post fields.'); | |
} | |
$search = array('#', ',', '+', ':'); | |
$replace = array('%23', '%2C', '%2B', '%3A'); | |
$string = str_replace($search, $replace, $string); | |
$this->getfield = $string; | |
return $this; | |
} | |
/** | |
* Get getfield string (simple getter) | |
* | |
* @return string $this->getfields | |
*/ | |
public function getGetfield() | |
{ | |
return $this->getfield; | |
} | |
/** | |
* Get postfields array (simple getter) | |
* | |
* @return array $this->postfields | |
*/ | |
public function getPostfields() | |
{ | |
return $this->postfields; | |
} | |
/** | |
* Build the Oauth object using params set in construct and additionals | |
* passed to this method. For v1.1, see: https://dev.twitter.com/docs/api/1.1 | |
* | |
* @param string $url The API url to use. Example: https://api.twitter.com/1.1/search/tweets.json | |
* @param string $requestMethod Either POST or GET | |
* @return \TwitterAPIExchange Instance of self for method chaining | |
*/ | |
public function buildOauth($url, $requestMethod) | |
{ | |
if (!in_array(strtolower($requestMethod), array('post', 'get'))) | |
{ | |
throw new Exception('Request method must be either POST or GET'); | |
} | |
$consumer_key = $this->consumer_key; | |
$consumer_secret = $this->consumer_secret; | |
$oauth_access_token = $this->oauth_access_token; | |
$oauth_access_token_secret = $this->oauth_access_token_secret; | |
$oauth = array( | |
'oauth_consumer_key' => $consumer_key, | |
'oauth_nonce' => time(), | |
'oauth_signature_method' => 'HMAC-SHA1', | |
'oauth_token' => $oauth_access_token, | |
'oauth_timestamp' => time(), | |
'oauth_version' => '1.0' | |
); | |
$getfield = $this->getGetfield(); | |
if (!is_null($getfield)) | |
{ | |
$getfields = str_replace('?', '', explode('&', $getfield)); | |
foreach ($getfields as $g) | |
{ | |
$split = explode('=', $g); | |
$oauth[$split[0]] = $split[1]; | |
} | |
} | |
$base_info = $this->buildBaseString($url, $requestMethod, $oauth); | |
$composite_key = rawurlencode($consumer_secret) . '&' . rawurlencode($oauth_access_token_secret); | |
$oauth_signature = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true)); | |
$oauth['oauth_signature'] = $oauth_signature; | |
$this->url = $url; | |
$this->oauth = $oauth; | |
return $this; | |
} | |
/** | |
* Perform the actual data retrieval from the API | |
* | |
* @param boolean $return If true, returns data. | |
* | |
* @return string json If $return param is true, returns json data. | |
*/ | |
public function performRequest($return = true) | |
{ | |
if (!is_bool($return)) | |
{ | |
throw new Exception('performRequest parameter must be true or false'); | |
} | |
$header = array($this->buildAuthorizationHeader($this->oauth), 'Expect:'); | |
$getfield = $this->getGetfield(); | |
$postfields = $this->getPostfields(); | |
$options = array( | |
CURLOPT_HTTPHEADER => $header, | |
CURLOPT_HEADER => false, | |
CURLOPT_URL => $this->url, | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_TIMEOUT => 10, | |
); | |
if (!is_null($postfields)) | |
{ | |
$options[CURLOPT_POSTFIELDS] = $postfields; | |
} | |
else | |
{ | |
if ($getfield !== '') | |
{ | |
$options[CURLOPT_URL] .= $getfield; | |
} | |
} | |
$feed = curl_init(); | |
curl_setopt_array($feed, $options); | |
$json = curl_exec($feed); | |
curl_close($feed); | |
if ($return) { return $json; } | |
} | |
/** | |
* Private method to generate the base string used by cURL | |
* | |
* @param string $baseURI | |
* @param string $method | |
* @param array $params | |
* | |
* @return string Built base string | |
*/ | |
private function buildBaseString($baseURI, $method, $params) | |
{ | |
$return = array(); | |
ksort($params); | |
foreach($params as $key=>$value) | |
{ | |
$return[] = "$key=" . $value; | |
} | |
return $method . "&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $return)); | |
} | |
/** | |
* Private method to generate authorization header used by cURL | |
* | |
* @param array $oauth Array of oauth data generated by buildOauth() | |
* | |
* @return string $return Header used by cURL for request | |
*/ | |
private function buildAuthorizationHeader($oauth) | |
{ | |
$return = 'Authorization: OAuth '; | |
$values = array(); | |
foreach($oauth as $key => $value) | |
{ | |
$values[] = "$key=\"" . rawurlencode($value) . "\""; | |
} | |
$return .= implode(', ', $values); | |
return $return; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment