Building a Chrome Extension for Gmail, trying to use Chrome Auth to gain access to gmail api per this StackOverflow post and the Gmail API docs, among others. I successfully receive the token with chrome.identity.getAuthToken({ 'interactive': true }, function(token) {} however when I plug the token into the request url I get the following 401 error response (code follows)
error response
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Login Required",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Login Required"
}
}
My code:
background.js
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
thisToken = token
chrome.runtime.onMessage.addListener(
function(request,sender,sendResponse){
var gapiRequestUrlAndToken = "https://www.googleapis.com/gmail/v1/users/mail%40gmail.com/threads?key={" + thisToken + "}"
var makeGetRequest = function (gapiRequestURL)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", gapiRequestURL, false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
makeGetRequest(gapiRequestUrlAndToken);
}
);
});
}
})
manifest.json
{
"manifest_version": 2,
"key": "<key>",
"name": "exampleName",
"description": "Description",
"version": "0.0.1.7",
"default locale": "en",
"icons": { "128": "imgs/pledge_pin.png"},
"content_scripts" : [
{
"matches": ["*://mail.google.com/mail/*"],
"js": ["js/jquery.js", "js/compose.js", "bower_components/jqnotifybar/jquery.notifyBar.js"],
"css": ["css/stylesheet.css", "bower_components/jqnotifybar/css/jquery.notifyBar.css"]
}
],
"background": {
"scripts": ["scripts/background.js"]
},
"permissions": [
"identity"
],
"oauth2": {
"client_id": "<client id>",
"scopes": ["https://www.googleapis.com/auth/gmail.modify"]
}
}
I suspect it has something to do with the fact that I'm trying to use Chrome Auth for Gmail api, but other posts I've read have lead me to believe this is a viable option.
In case my code didn't give it away, I'm a newbie, so any help is most welcome, and I greatly appreciate your time.
key is for generic application secrets. For user specific tokens you need to use access_token. And token should not be wrapped in {}. If mail%40gmail.com is the actual value you are using in the URL, it won't work. It either needs to be the email address of the authenticated user or me.
So change:
"https://www.googleapis.com/gmail/v1/users/mail%40gmail.com/threads?key={" + thisToken + "}"
To this:
"https://www.googleapis.com/gmail/v1/users/me/threads?access_token=" + thisToken
Related
I'm unable to enter into any of the listener functions for notifications API with manifest V2. I'm able to execute the callback function after Notification is displayed. Also I can see that the chrome.notifications.onClicked.addListener is present while using in debugging mode.
Here's my manifest.json file
{
"name": "Learning",
"manifest_version": 2,
"version": "1.3",
"description": "Notification Popup",
"background": {
"scripts": ["script.js"],
"persistent" : false
},
"permissions": [
"notifications"
],
"content_security_policy": "script-src 'self' 'unsafe-eval' https://ajax.googleapis.com/; object-src 'self'"
}
script.js
var options = {
type: "basic",
title: "Someone just replied to your mail",
message: "You have been hired in our company",
iconUrl: "stackIcon.PNG"
}
chrome.notifications.create(options,function() {
console.log('Notification Callback');
});
chrome.notifications.onClicked.addListener(function() {
console.log('notification Clicked');
});
chrome.notifications.onClosed.addListener(function() {
console.log('notification Closed');
});
chrome.notifications.onButtonClicked.addListener(function() {
console.log('notification Button Clicked');
});
chrome.runtime.onInstalled.addListener(function() {
console.log('runtime onInstalled');
})
console.log results
Notification Callback
This is an unfortunate bug in Chromium Extensions, https://crbug.com/1168477, it has been fixed in version 91 canary.
I'm building a webhook that will send the completed recordings info from Zoom to a deployed Web App. Webhook will fire when the recording is completed.
Below is the complet schema of the event notification:
{
"event": "string",
"payload": {
"account_id": "string",
"object": {
"id": "integer",
"uuid": "string",
"host_id": "string",
"topic": "string",
"type": "integer",
"start_time": "string",
"timezone": "string",
"host_email": "string",
"duration": "integer",
"share_url": "string",
"total_size": "integer",
"recording_count": "integer",
"recording_files": [
{
"id": "string",
"meeting_id": "string",
"recording_start": "string",
"recording_end": "string",
"file_type": "string",
"file_size": "number",
"play_url": "string",
"download_url": "string",
"status": "string",
"recording_type": "string"
}
]
}
}
}
This is my Web App code:
function doGet(e) {
return ContentService.createTextOutput('doGet request');
}
function doPost(e) {
try {
var input = JSON.stringify(e.postData.contents);
input = JSON.parse(input);
//var myData = JSON.parse(e.postData.contents);
//return HtmlService.createHtmlOutput(myData);
}
catch (e) {
throw new Error(e);
}
return ContentService.createTextOutput("doPost received");
}
The Web App is deployed with Execute the app as the owner and everyone has access to the app.
I'm getting Status 302 on Zoom side.
Please give me some advice on this. Thank you in advance!
How about this modification?
Modification points:
At Web Apps, when ContentService.createTextOutput is returned, it seems that the redirect might be used. From the status code of 302 in your question, I thought that this might be the reason of your issue. So for example, in order to avoid this issue, how about the following modification?
Modified script:
function doPost(e) {
try {
var input = JSON.stringify(e.postData.contents);
input = JSON.parse(input);
//var myData = JSON.parse(e.postData.contents);
//return HtmlService.createHtmlOutput(myData);
}
catch (e) {
throw new Error(e);
}
// return ContentService.createTextOutput("doPost received"); // Removed
}
When you modified the script of Web Apps, please redeploy Web Apps as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
Note:
If the status code was changed to 200 when no values are returned to the zoom side, in this case, I think that HtmlService.createHtmlOutput might be able to be also used.
I have a little proble with Google Sheets API
I have a Google Sheet Document on my GDrive. And I can work with it using Google Sheets API and Google Drive API. So I can move it, update it or make copies.
My task is:
1. make a copy of this document (I can do this)
2. publish this copy as web app. So each copy has a doGet() functon inside so it can be published as a Web app. Manually it can be done like: Publish -> Deploy as web app.. But I can't find any API to do this.
Get an URL of published service
UPDATE
I read documentation projects.deployments.create And I maanged to create a new deployment (before that I should create a version) But my new deployemnt has no web access, no url etc. IF I check via projects.deployments.list it shows:
{
"deploymentId": "AKfycbxVfuoeIQmumgy_Efhw12NCcqE7vqosYoxbDiKj5CT4mL_GbtybXsh1ppMIX22wQX20",
"deploymentConfig": {
"scriptId": "1zfjbALVe0jGbZCtqjFR0RP2-O___hR7MtAlx3biuJGXKsrKh3y1W0hMT",
"versionNumber": 1,
"manifestFileName": "appsscript",
"description": "v1"
},
"updateTime": "2019-05-13T22:33:23.760Z"
}
And if I will do this manually via web interface it will looks like
{
"deploymentId": "AKfycbyn3smPKxJcZwsm9SzSTtzNCAcWJzf1OVs4WTslvHo",
"deploymentConfig": {
"scriptId": "1zfjbALVe0jGbZCtqjFR0RP2-O___hR7MtAlx3biuJGXKsrKh3y1W0hMT",
"manifestFileName": "appsscript"
},
"updateTime": "1970-01-01T00:00:00Z",
"entryPoints": [
{
"entryPointType": "WEB_APP",
"webApp": {
"url": "https://script.google.com/macros/s/AKfycbyn3smPKxJcZwsm9SzSTtzNCAcWJzf1OVs4WTslvHo/exec",
"entryPointConfig": {
"access": "ANYONE_ANONYMOUS",
"executeAs": "USER_DEPLOYING"
}
}
}
]
}
The Apps Script API needs to be used. You can use the REST API and make a UrlFetchApp.fetch(url) request. This is a two step process. First you must create a new Apps Script file, then you must update that new Apps Script file with new contents in the manifest file. The manifest file must have a property for webapp which is what designates the type of deployment.
The code will probably look something like the following:
function deployA_project() {
var id, OAuthToken,options,payload,rtrn,url;
id = ScriptApp.getScriptId();//Get the project ID of this script project
Logger.log('id: ' + id)
url = 'https://script.googleapis.com/v1/projects/' + id + '/deployments';//For REST
OAuthToken = ScriptApp.getOAuthToken();
payload = {
"versionNumber": number,
"manifestFileName": string,
"description": string
}
options = {
"method" : "POST",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + OAuthToken
},
"contentType": "application/json",
"payload": JSON.stringify(payload)
};
rtrn = UrlFetchApp.fetch(url,options);
Logger.log('rtrn: ' + rtrn)
}
See the documentation:
projects.deployments.create
How the apps script project is deployed is designated in the manifest file:
{
"timeZone": "America/New_York",
"dependencies": {
},
"webapp": {
"access": "MYSELF",
"executeAs": "USER_DEPLOYING"
},
"exceptionLogging": "STACKDRIVER"
}
The API doesn't provide a configuration setting for creating the type of deployment. So, turning the deployment into a Web App is done by updating the manifest file. This must be a two step process. First, you create the new project file, then you update it with a JSON object of file content. You can designate the file content of the manifest file named "appsscript.json"
The content must be JSON in the following format:
{
"scriptId": "scriptId",
"files": [{
"name": "appsscript",
"type": "JSON",
"source": "{\"timeZone\":\"America/New_York\", \"webapp\": {\"access\": \"MYSELF\",\"executeAs\": \"USER_DEPLOYING\"},\"exceptionLogging\":\"STACKDRIVER\"}",
"lastModifyUser": {
"name": "MyName",
"email": "example#gmail.com",
}
}]
}
You need to make another request to a different url, and the request must be a PUT request in order to make an update:
url = 'https://script.googleapis.com/v1/projects/' + id + '/deployments/' + {deploymentId}
var newContent = {
"scriptId": "scriptId",
"files": [{
"name": "appsscript",
"type": "JSON",
"source": "{\"timeZone\":\"America/New_York\", \"webapp\": {\"access\": \"MYSELF\",\"executeAs\": \"USER_DEPLOYING\"},\"exceptionLogging\":\"STACKDRIVER\"}",
"lastModifyUser": {
"name": "MyName",
"email": "example#gmail.com",
}
}]
}
var requestBody = {};
requestBody.files = newContent;
requestBody.deploymentConfig = {
"scriptId": string,
"versionNumber": number,
"manifestFileName": string,
"description": string
}
options = {
"method" : "PUT",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + OAuthToken
},
"contentType": "application/json",
"payload": JSON.stringify(requestBody)
};
rtrn = UrlFetchApp.fetch(url,options);
Logger.log('rtrn: ' + rtrn)
This is my first time working with javascript and chrome extensions. I wanted to append ?share=1 at the end of quora urls so i dont have to sign up. I made an attempt my looking at some answers but i am unable to find why this string is appended two times ?share=1?shared=1 at the end of my urls.
here is my code
Manifest.json
{
"manifest_version": 2,
"name": "Quora view",
"description": "View quora without signing up ",
"version": "1.0.0",
"icons": {"128": "icon128.png"},
"background": {"scripts":["background.js"]},
"permissions": [
"activeTab",
"webRequest",
"*://www.quora.com/*",
"webRequestBlocking"
]}
background.js
let host = "https://www.quora.com";
let post = "?share=1";
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
return {redirectUrl: host + details.url.match(/^https?:\/\/[^\/]+[\S\s]*)/)[1] + post};
},
{
urls: [
"*://www.quora.com/*",
]
},
["blocking"]
);
Building a Chrome Extension for Gmail, trying to retrieve only emails addressed to me. I use the gapi thread API Explorer at the bottom of this page to test. It returns the inbox-only items as expected as you can see at the bottom of the image below. I copy and paste the request URL https://www.googleapis.com/gmail/v1/users/me/threads?=to%3Adan%40pledgmail.com+in%3Ainbox&access_token= + thisToken
from the API Explorer above into my background.js code below, but I am returned emails that I have sent in addition to those I have received.
Note: I do change the "key" in the request URL from the API Explorer to "access_token", else no request I make works.
(In case my code doesn't give it away, I'm a newbie. Any help is sincerely appreciated, and I am grateful for your time.)
Google API Explorer results (expected)
My code from background.js with copied request URL
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
thisToken = token
chrome.runtime.onMessage.addListener(
function(request,sender,sendResponse){
var gapiRequestAllThreadsToSelf = "https://www.googleapis.com/gmail/v1/users/me/threads?=to%3Adan%40pledgmail.com+in%3Ainbox&access_token=" + thisToken
var getAllThreadsToSelf = function (gapiRequestURL)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", gapiRequestURL, false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
var threadsToSelf = getAllThreadsToSelf(gapiRequestAllThreadsToSelf)
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {data: threadsToSelf}, function(response) {
});
});
}
);
});
}
})
Return with unexpected 9 emails instead of 6 (top 3 are emails I sent)
{
"threads": [
{
"id": "14e69c9075bd53",
"snippet": "Thank you!",
"historyId": "8573"
},
{
"id": "14e69be815c6a0",
"snippet": "Thaaaanks",
"historyId": "8550"
},
{
"id": "14e644211d19b0",
"snippet": "Reply to this email, Danny boy",
"historyId": "8481"
},
{
"id": "14e1c4702de573",
"snippet": "Hey guys, Here is the gmail Chrome extension I am working on. This is the basic mvp I'm iterating",
"historyId": "8328"
},
{
"id": "14e13259f00f0e",
"snippet": "Hello Daniel Klos, Thanks for buying from Chrome Web Store using Google Wallet! Chrome Web Store will",
"historyId": "8431"
},
{
"id": "14e12da5ca9c16",
"snippet": "Here are your account details. Sign in ยป Your billing setup is complete. See your account details",
"historyId": "6181"
},
{
"id": "14e12d1e3e41ba",
"snippet": "Hi Dan Welcome to your Gmail inbox Save everything With up to 30GB of space, you'll never need to",
"historyId": "2678"
},
{
"id": "14e12d1e1be7b3",
"snippet": "Hi Dan Get the official Gmail app The best features of Gmail are only available on your phone and",
"historyId": "6114"
},
{
"id": "14e12d1e19e865",
"snippet": "Hi Dan Work smarter with Gmail and Google Apps Manage Calendar meetings Google Calendar makes",
"historyId": "2682"
}
],
"resultSizeEstimate": 9
}
My manifest.json for good measure
{
"manifest_version": 2,
"key": "redacted",
"name": "redacted",
"description": "Description",
"version": "0.0.2.0",
"default locale": "en",
"icons": { "128": "imgs/pledge_pin.png"},
"content_scripts" : [
{
"matches": ["*://mail.google.com/mail/*"],
"js": ["js/jquery.js", "js/compose.js", "bower_components/jqnotifybar/jquery.notifyBar.js"],
"css": ["css/stylesheet.css", "bower_components/jqnotifybar/css/jquery.notifyBar.css"]
}
],
"background": {
"scripts": ["scripts/background.js"]
},
"permissions": [
"identity"
],
"oauth2": {
"client_id": "redacted",
"scopes": ["https://www.googleapis.com/auth/gmail.modify"]
}
}
Doing threads.list() will return threads where any message in the thread matches the criteria. If you only want messages that match a specific criteria then do messages.list() instead.
There is a typo in your code which makes your code act differently than API Explorer. You are missing 'q' in the query string which makes the result includes all emails without filter.
var gapiRequestAllThreadsToSelf = "https://www.googleapis.com/gmail/v1/users/me/threads?q=to%3Adan%40pledgmail.com+in%3Ainbox&access_token=" + thisToken
Try this out to get rid of the nuisance.