HankLloydright / Telsa API Safe Tools: Token Generator, API Query Tool, and Referral Info Retrieval

// ==UserScript==
// @name         Telsa API Safe Tools: Token Generator, API Query Tool, and Referral Info Retrieval 
// @namespace    https://openuserjs.org/users/HankLloydright
// @description  Tesla API Safe Tools: Token Generator, API Query Tool, and Referral Info Retrieval 
// @grant        GM_addStyle
// @author       HankLloydright
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js
// @require      https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js
// @include      https://owner-api.teslamotors.com/oauth/token
// @license      MIT
// @version      2020.7.11
// @date         2020-07-11
// ==/UserScript==

// **************************** Directions:   **************************** 
// 1. Load and enable this script into TamperMonkey in your browser
// 2. Go to this secure Tesla Motors API website:  https://owner-api.teslamotors.com/oauth/token
// 3. Enter and submit your TESLA ACCOUNT login credentials 
// All of the source code for this extension is below.  You are submitting your TESLA ACCOUNT credentials ONLY back to the teslamotors.com website.
// At no time are your credentials sent anywhere else so submitting your credentials is completely safe, since you are always on the secure
// teslamotors.com domain/website.
// Contact me if you have any questions, enhancements, or suggestions
// ********************************************************************** 

GM_addStyle('.key { text-align:right; font-weight: bold; width:20%;} .val {text-align:left; width:20%; } .header{ text-align:center; font-weight: bold; color: green;}  table {border-style:solid;} #results {font-size:12pt;}');

$(document).ready(function() {

$("title").html("Tesla API Token Generator and Query Tool");
$('head').append('<link href="https://www.tesla.com/sites/default/files/favicon.ico" rel="shortcut icon" type="image/x-icon" />');

var pagediv="#results";
var br="<br>"; 

var title="<h2>Secure Tesla API Token Generator</h2>"+
	      "<h3>All data is local to your browser and sent securely to teslamotors.com as you can see in the URL address bar above."+br+
	      "Source code for this script available here: <a target=_blank href='https://openuserjs.org/scripts/HankLloydright/Telsa_API_Safe_Tools_Token_Generator,_API_Query_Tool,_and_Referral_Info_Retrieval/source'>Telsa_API_Token_Tools</a><h3>";

var token=Cookies.get('token');
var rtoken=Cookies.get('refresh');
var userid=Cookies.get('userid');
var create=Cookies.get('create');
var dt=eval(create*1000);
var cDate = new Date(dt);
var rDate=cDate.toLocaleString();

// this is Tesla's google analytics code in an array
// which is just stored here to be removed from loot box code to prevent js errors
// nothing is being tracked by google using this code
var googlecode=[
"(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){",
"(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),",
"m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)",
"})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');",
"ga('create', 'UA-9152935-18', 'auto');",
"ga('send', 'pageview');"];

if (token===undefined) {
	$(".dialog").css("width","80%").html(title+
	  "Enter Email Address:<input type=text id=email name=email size=50>"+br+br+
	  "Enter Password:<input type=password id=password name=password size=50>"+br+
	  "<button id='gettoken' type='button'>Get Tesla API Token/Login</button>"+br);
}
else {
	$(".dialog").html(title+"Token Loaded: <span class=header>"+token+"</span>"+br+
      "Refresh Token: <span class=header>"+rtoken+"</span>"+br+
      "Refresh Token Created:<span class=header>"+create+" "+rDate+"</span>"+br+br+
	  "<button id=logout>Log Out/Delete Token</button>&nbsp;&nbsp;&nbsp;&nbsp; <button id=clear>Clear all data below</button>&nbsp;&nbsp;&nbsp;"+
	  //"<button id=refer>Get Referral Info</button>&nbsp;&nbsp;&nbsp;&nbsp;"+
      //"<button id=loot>Get Loot Box</button>&nbsp;&nbsp;&nbsp;&nbsp;"+
      "<button id=list>List All Vehicles</button>&nbsp;&nbsp;&nbsp;&nbsp;"+
      "<button id=refresh>Refresh Token Now</button>&nbsp;&nbsp;&nbsp;&nbsp;"+
	  //"<button id=awards>Get Referral Awards</button>"
	  "<div id='results'></div>").css("width","80%");
}

$("#logout").click(function(){
	Cookies.remove('token');
	Cookies.remove('userid');
    location.reload();
});

$("#clear").click(function(){
	$("table, iframe").remove();
});

var carid=0;

$("#list").click(function(){
	$("#vehicles,#cardata").remove();
		$.ajax({
			type: 'get',
			url: 'https://owner-api.teslamotors.com/api/1/vehicles',
			beforeSend: function(xhr) {xhr.setRequestHeader('Authorization','Bearer '+token)},
			complete:  function(response) {
				var json=JSON.parse(response["responseText"]);
				$(pagediv).prepend("<table id='vehicles'></table>");
				$.each(json["response"], function(i, car) {
                    carid=car["id_s"];

					$("#vehicles").append("<tr><td colspan=2 style='width:40%'>Vehicle Name: <button id='getdata"+i+"' data-carid='"+carid+"'>Get data for: "+car["display_name"]+"</button></td></tr>");
                    $("#getdata"+i).click(function(){LoadData(carid);});
					DataTable("#vehicles",car);
				});
          },
			success: function (response) {},
			error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
		});
});

function LoadData(carid) {
    var rand=Math.floor(Math.random() * 100000) + 1;
    var turl='https://owner-api.teslamotors.com/api/1/vehicles/'+carid+'/vehicle_data?rand='+rand; // cachebusting sometimes needed
		$.ajax({
			type: 'get',
			url: turl,
			beforeSend: function(xhr) {	xhr.setRequestHeader('Authorization','Bearer '+token);},
			complete:  function(response) {
				var json=JSON.parse(response["responseText"]);
				$(pagediv).append("<table id='cardata'></table>");
				$("#cardata").append("<tr><td class=header colspan=2><br>Loading /vehicle_data endpoint..</td></tr>");
				DataTable("#cardata",json["response"]);
          },
			success: function (response) {},
			error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
		});
};


$("#refer").click(function(){
	var counts=[];
	counts["Ordered"]=0;counts["Delivered"]=0;counts["Cancelled"]=0;
	$.ajax({
		type: 'get',
		url: 'https://owner-api.teslamotors.com/referral_page',
		beforeSend: function(xhr) {xhr.setRequestHeader('Authorization','Bearer '+token)},
		complete:  function(response) {
			$("#refertab").remove();
			$(pagediv).prepend("<table id='refertab'></table>");
			var header="<tr class=header>"+
				"<td>Name</td>"+
				"<td>Ordered Date</td>"+
				"<td>Delivery Date</td>"+
			    "<td>Email Address</td>"+
				"</tr>";
			userid=GetUserId(response.responseText);
			//$("#refertab").prepend("<tr><td colspan=4 class=header>User ID: "+userid+"</td></tr>");
			$.ajax({
				type: 'get',
				url: 'https://referral-app.tesla.com/data/get-past-referral?userId='+userid,
				complete:  function(response) {
					var data=JSON.parse(response.responseText);
					//data=sortJSON(data.data.currentPhaseReferralList, "Status", 'desc');
					data=data.data.currentPhaseReferralList
					data=data.sort(getSortMethod('-Status', '+OrderPlacedDate', '+name'));
					var stat = "";
					$.each(data, function(i, ref) {
						var ddate= convert(ref.DeliveryDate);
						if (ref.Status=="Cancelled" || ref.Status=="Ordered") ddate=ref.Status;
						if (ref.Status != stat) {
							$("#refertab").append("<tr><td colspan=1 class=header>"+br+ref.Status+"</td></tr>"+header);
							stat=ref.Status;
						}
						$("#refertab").append("<tr  class=key>"+
							"<td>"+ref.RefereeName+'</td>'+
							"<td>"+convert(ref.OrderPlacedDate)+'</td>'+
							"<td>"+ddate+'</td>'+
							"<td>"+ref.RefereeEmail+"</td>"+
							"</tr>");
						counts[ref.Status]++;
						});
						$("#refertab").prepend("<tr><td colspan=4 class=header>Referrals Total:"+(counts["Ordered"]+counts["Delivered"])+"&nbsp;&nbsp;&nbsp;&nbsp; Ordered: "+counts["Ordered"]+"&nbsp;&nbsp;&nbsp;&nbsp; Delivered: "+counts["Delivered"]+"&nbsp;&nbsp;&nbsp;&nbsp; Cancelled: "+counts["Cancelled"]+"</td></tr>");
					},
					success: function (response) {},
					error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
				});
          },
			success: function (response) {},
			error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
		});
});

$("#loot").click(function(){
	$.ajax({
		type: 'get',
		url: 'https://owner-api.teslamotors.com/referral_page',
		beforeSend: function(xhr) {xhr.setRequestHeader('Authorization','Bearer '+token)},
		complete:  function(response) {
			$("#referframe").remove();
			$(pagediv).prepend("<iframe style='width:600px;height:780px;' id='referframe'></iframe>");
			$("#referframe").attr("srcdoc",response.responseText );
			userid=GetUserId(response.responseText);
        },
		success: function (response) {},
		error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
	});
});

$("#awards").click(function(){
	if (userid=="" || typeof userid === "undefined") {alert("Please retrieve Referral Info or Loot Box first to load Userid"); return;}
	console.log("userid="+userid);
	$.ajax({
		type: 'options',
		url: 'https://referral-app.tesla.com/api/referral/v3/features/myAwards',
		beforeSend: function(xhr) {
			//xhr.setRequestHeader('Access-Control-Request-Headers','accept-language,authorization');
			//xhr.setRequestHeader('Access-Control-Request-Method','GET');
			//xhr.setRequestHeader('Origin','https://owner-api.teslamotors.com');
		},
		complete:  function(response) {
			if (response.responseText != "OK") alert("Error Award OPTIONS call returned: "+response.responseText);
        },
		success: function (response) {},
		error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
	});
	$.ajax({
		type: 'get',
		url: 'https://referral-app.tesla.com/api/referral/v3/features/myAwards/',
		beforeSend: function(xhr) {
			xhr.setRequestHeader('Authorization','Bearer '+userid);
			xhr.setRequestHeader('Accept','application/json, text/plain, */*');
			xhr.setRequestHeader('Accept-Language','en_US');
			xhr.setRequestHeader('Content-Language','US');
		},
		complete:  function(response) {
			var json=JSON.parse(response["responseText"]);
			$("#awardlist").remove();
			$(pagediv).prepend("<table id='awardlist'></table>");
			$("#awardlist").append("<tr><td class=header colspan=2><br>Loot Box Referral Awards</td></tr>");
			AwardTable("#awardlist",json["data"]["dbData"]);
        },
		success: function (response) {},
		error:function(XMLHttpRequest,status,error){ alert("error "+error+" "+status);}
	});
});

function AwardTable(div,obj) {
		$.each(obj, function(key,val) {
			if (jQuery.type(val)=="object" || jQuery.type(val)=="aarray"  ) {
				//$(div).append("<tr><td class=header colspan=2><br>"+key+"</td></tr>");
				 AwardTable(div,val);
			}
			else {
				if (key == "Name") $(div).append("<tr><td class=val colspan=2>"+val+"</td></tr>");
				//console.log(key+":"+val+" type: "+jQuery.type(val));
			}
		});
}

function convert(unixtimestamp){
 var months_arr = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
 var date = new Date(unixtimestamp*1000);
 var year = date.getFullYear();
 var month = months_arr[date.getMonth()];
 var day = date.getDate();
 return month+'-'+day+'-'+year;
}
function sortJSON(data, key, way) {
    return data.sort(function(a, b) {
        var x = a[key]; var y = b[key];
        if (way === 'asc' ) { return ((x < y) ? -1 : ((x > y) ? 1 : 0)); }
        if (way === 'desc') { return ((x > y) ? -1 : ((x < y) ? 1 : 0)); }
    });
}

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

function DataTable(div,obj) {
		$.each(obj, function(key,val) {
			if (jQuery.type(val)=="object") {
				$(div).append("<tr><td class=header colspan=2><br>"+key+"</td></tr>");
				DataTable(div,val);
			}
			else {
				if (key == "option_codes") val=val.split(',').join(', ');
				$(div).append("<tr><td class=key>"+key+"</td><td class=val>"+val+"</td></tr>");
			}
		});

}

function GetUserId(page) {
	var start=page.indexOf('"userId":');
	var end=page.indexOf('","userIdGA');
	var userid=page.substr(start+10,end-start-10);
	Cookies.set('userid', userid);
	return userid;
}

$("#gettoken").click(function(){
		$.ajax({
			type: 'post',
			url: 'https://owner-api.teslamotors.com/oauth/token',
			dataType: 'json',
			data: {
				grant_type:'password',
				client_id:'81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384',
				client_secret:'c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3',
				email: $("#email").val(),
				password: $("#password").val()
			},
			complete:  function(response) {
				var reply=JSON.parse(response["responseText"]);
				console.log(reply);
				Cookies.set('token', reply["access_token"]);
				Cookies.set('refresh', reply["refresh_token"]);
                location.reload();
			},
			success: function (response) {},
			error:function(XMLHttpRequest,status,error){ alert("error: ["+error+"] status:"+status);}
		});
});


$("#refresh").click(function(){
		$.ajax({
			type: 'post',
			url: 'https://owner-api.teslamotors.com/oauth/token',
			dataType: 'json',
			data: {
				grant_type:'refresh_token',
				client_id:'81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384',
				client_secret:'c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3',
                refresh_token: Cookies.get('refresh')
			},
			complete:  function(response) {
				var reply=JSON.parse(response["responseText"]);
				console.log(reply);
				Cookies.set('token', reply["access_token"]);
				Cookies.set('refresh', reply["refresh_token"]);
                Cookies.set('create',reply["created_at"]);
                location.reload();
			},
			success: function (response) {},
			error:function(XMLHttpRequest,status,error){ alert("error: ["+error+"] status:"+status);}
		});
});



});