Google App Script / Gmail add-on : How to display a window with text and buttons over Gmail? - google-app-engine

I made a Google App script to answer automatically to my emails (a kind of clever email robot assistant). Nevertheless, I would like to check each answer made by the robot before sending.
So I would like to have a window over Gmail showing the user email and the robot answer, and two buttons "send" "skip". In this way, I could check the answer prepared by the robot and either send it or skip it (or potentially edit it).
How to display a window with text, editText and buttons over GMail from Google App Script ?
Thanks !
Regards.

You have to check Gmail Add-on : https://developers.google.com/gsuite/add-ons/gmail
For a first start you can check the codelab from Google, it will give you the code to set a first add-on in 5 minutes then you can adapt it to your needs : https://codelabs.developers.google.com/codelabs/apps-script-intro/
Stéphane

An easy solution would be to have the robot save the e-mail as 'draft'. That way, you can easily check the emails before manually sending them.
If you are still interested in creating the gmail add-on (which could display the original email, response, and buttons for sending or editing), you may be interested in building card-based interfaces. These will appear to the right of your Gmail web client, and will look like the following:
The code used to display such interface (with two buttons, one that automatically sends the email and another one that opens the editor on it) is the following:
function buildAddOn(e) {
// Activate temporary Gmail add-on scopes.
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
return buildDraftCard(getNextDraft());
}
function buildDraftCard(draft) {
if (!draft) {
var header = CardService.newCardHeader().setTitle('Nothing to see here');
return CardService.newCardBuilder().setHeader(header).build();
} else {
var header = CardService.newCardHeader()
.setTitle(draft.getMessage().getSubject());
var section = CardService.newCardSection();
var messageViewer = CardService.newTextParagraph()
.setText(draft.getMessage().getBody());
var sendButton = CardService.newTextButton()
.setText('Send')
.setOnClickAction(CardService.newAction()
.setFunctionName('sendMessage')
.setParameters({'draftId': draft.getId()})
);
var editButton = CardService.newTextButton()
.setText('Edit')
.setOnClickAction(CardService.newAction()
.setFunctionName('editMessage')
.setParameters({'draftId': draft.getId()})
);
var buttonSet = CardService.newButtonSet()
.addButton(sendButton)
.addButton(editButton);
section.addWidget(messageViewer);
section.addWidget(buttonSet)
return CardService.newCardBuilder()
.setHeader(header)
.addSection(section)
.build();
}
}
function sendMessage(e) {
GmailApp.getDraft(e.parameters.draftId).send();
return CardService.newActionResponseBuilder().setNavigation(
CardService.newNavigation()
.popToRoot()
.updateCard(buildDraftCard(getNextDraft()))
).build();
}
function editMessage(e) {
var messageId = GmailApp.getDraft(e.parameters.draftId).getMessageId();
var link = "https://mail.google.com/mail/#all/" + messageId;
return CardService.newActionResponseBuilder().setOpenLink(
CardService.newOpenLink()
.setUrl(link)
.setOnClose(CardService.OnClose.RELOAD_ADD_ON)
).build();
}
function getNextDraft() {
return GmailApp.getDrafts().pop()
}
And the appsscript.json configuration is the following:
{
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://mail.google.com/"
],
"gmail": {
"name": "Gmail Add-on Draft Autoresponse UI",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/label_googblue_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "buildAddOn"
}],
"openLinkUrlPrefixes": [
"https://mail.google.com/"
],
"primaryColor": "#4285F4",
"secondaryColor": "#4285F4"
}
}
Bear in mind however, that these interfaces at the moment still have some limitations. They can only be displayed whilst having a message open, and the HTML formatting of the email may look a bit off. You can find more information on how to test & run the code above by following this link.

Related

How to open mail app inbox directly in react native?

Using Linking.openURL('mailto:') with expo-linking opens the mail app with a 'compose mail' screen, how can i open the mail app directly on the inbox tab ?
Inspired by this stack overflow post, you can use the message:// url scheme to achieve this on iOS. If no content is provided, it defaults to the email inbox on iOS.
For android, things are a little trickier. You'll need expo-intent-launcher and a few extra params to complete the hand-off. A complete solution may look something like this:
import { startActivityAsync, ActivityAction } from 'expo-intent-launcher';
[...]
if (Platform.OS === "android") {
const activityAction = "android.intent.action.MAIN";
const intentParams: IntentLauncher.IntentLauncherParams = {
category: "android.intent.category.APP_EMAIL",
};
IntentLauncher.startActivityAsync(activityAction, intentParams);
}
else if (Platform.OS === "ios") {
Linking.openURL('message://');
}
Be sure to test on real devices if possible since the iPhone simulator doesn't have a mail client installed.
List of URL schemes on wikipedia
Update: If you don't mind an extra dependency, take a look at react-native-email-link which has the added benefit of allowing users to select from any installed email client. Neat!

Hide the default Gigya email share popup on click of the Gigya Share bar email icon

I am using the following code to implement the Set up Gigya share bar:
if ($window.gigya) {
// Step 1: Construct a UserAction object and fill it with data
var ua = new $window.gigya.socialize.UserAction();
ua.setLinkBack(articleUrl);
ua.setTitle($scope.title);
// Step 2: Define the Share Bar add-on's params object
var params = {
userAction: ua,
//shareButtons: 'linkedin,twitter,facebook,sina,email', // list of providers
shareButtons: 'linkedin,twitter,facebook,sina,email',
containerID: 'share-bar',
showCounts: 'none',
deviceType: 'auto',
cid: '',
wrap: true,
operationMode:'multiSelect',
onShareButtonClicked:function (e) {
console.log(e);
console.log(e.shareItem.provider);
if (e.shareItem.provider == 'email') {
var mailString = 'mailto:test#example.com'
$window.location.href = mailString;
}
}
};
// Step 3: Load the Share Bar add-on:
$window.gigya.socialize.showShareBarUI(params);
}
The above code displays the share bar provided by Gigya.
Now clicking the email option I am trying to open the default client mail (for example outlook). I see that the default email popup also get opened along with the outlook.
How to stop the default UI from opening in this case. I tried all the options but none are working for me.
Can anyone help me to know how to fix this issue.
I don't believe this behavior is supported. By design, the Share add-on, when sharing via email, constructs the actual message on the server using the UserAction passed in the request and then sends it from there. So, even if you got the default UI to not populate, how would you be getting the actual share data inside the 3rd party email program?

How to get notification from google drive sheet on edit?

I want to send notification to third party application when someone make changes in document stored in google drive.
can someone please help me that how to bound script with any document and when someone make changes in that script should run and send notification to third party application.
I have tried the following code But it is not working.
function onEdit(event){
var sheet = event.source.getActiveSheet();
var editedRow = sheet.getActiveRange().getRowIndex();
var editedolumn = sheet.getActiveRange().getColumnIndex();
var values = sheet.getSheetValues(editedRow, editedolumn, 1, 6);
Logger.log(values);
getSession();
}
function getSession(){
var payload =
{
"username" : "username",
"password" : "password",
};
var options =
{
"method" : "post",
"payload" : payload,
"followRedirects" : false
};
var login = UrlFetchApp.fetch("https://abcd.service-now.com/nav_to.do?uri=login.do" , options);
Logger.log(login);
var sessionDetails = login.getAllHeaders()['Set-Cookie'];
Logger.log(sessionDetails);
sendHttpPost(sessionDetails);
}
function sendHttpPost(data) {
var payload = {"category" : "network","short_description" : "Test"};
var headers = {"Cookie" : data}
var url = 'https://abcd.service-now.com/api/now/table/incident';
var options = {'method': 'post','headers': headers,'payload': payload,'json': true};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response.getContentText());
}
To send notification to third party application when someone make changes in document stored in google drive
Based from this Google Drive Help Forum, this feature hasn't been added yet. However, you may set notifications in a spreadsheet to find out when there's some modifications done in your spreadsheet. To set notifications in a spreadsheet:
Open the spreadsheet where you want to set notifications.
Click Tools > Notification rules.
In the window that appears, select when and how often you want to
receive notifications.
Click Save.
And, to bound script with any document
You may find the complete guide in Scripts Bound to Google Sheets, Docs, or Forms documentation. As mentioned,
To create a bound script, open a Google Sheets, Docs, or Forms file, then select Tools > Script editor. To reopen the script in the future, do the same thing. Because bound scripts do not appear in Google Drive, that menu is the only way to find or open the script.

using the googleapis library in dart to update a calendar and display it on a webpage

I am new to dart and I have been trying to figure out how to use the googleapis library to update a calendars events, then display the calendar/events on a webpage.
So far I have this code that I was hoping would just change the #text id's text to a list of events from the selected calendars ID:
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_io.dart';
final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
"private_key_id": "myprivatekeyid",
"private_key": "myprivatekey",
"client_email": "myclientemail",
"client_id": "myclientid",
"type": "service_account"
}
''');
const _SCOPES = const [CalendarApi.CalendarScope];
void main() {
clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
var calendar = new CalendarApi(http_client);
String adminPanelCalendarId = 'mycalendarID';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
querySelector("#text2").text = showEvents.toString();
});
});
}
But nothing displays on the webpage. I think I am misunderstanding how to use client-side and server-side code in dart... Do I break up the file into multiple files? How would I go about updating a calendar and displaying it on a web page with dart?
I'm familiar with the browser package, but this is the first time I have written anything with server-side libraries(googleapis uses dart:io so I assume it's server-side? I cannot run the code in dartium).
If anybody could point me in the right direction, or provide an example as to how this could be accomplished, I would really appreciate it!
What you might be looking for is the hybrid flow. This produces two items
access credentials (for client side API access)
authorization code (for server side API access using the user credentials)
From the documentation:
Use case: A web application might want to get consent for accessing data on behalf of a user. The client part is a dynamic webapp which wants to open a popup which asks the user for consent. The webapp might want to use the credentials to make API calls, but the server may want to have offline access to user data as well.
The page Google+ Sign-In for server-side apps describes how this flow works.
Using the following code you can display the events of a calendar associated with the logged account. In this example i used createImplicitBrowserFlow ( see the documentation at https://pub.dartlang.org/packages/googleapis_auth ) with id and key from Google Cloud Console Project.
import 'dart:html';
import 'package:googleapis/calendar/v3.dart';
import 'package:googleapis_auth/auth_browser.dart' as auth;
var id = new auth.ClientId("<yourID>", "<yourKey>");
var scopes = [CalendarApi.CalendarScope];
void main() {
auth.createImplicitBrowserFlow(id, scopes).then((auth.BrowserOAuth2Flow flow) {
flow.clientViaUserConsent().then((auth.AuthClient client) {
var calendar = new CalendarApi(client);
String adminPanelCalendarId = 'primary';
var event = calendar.events;
var events = event.list(adminPanelCalendarId);
events.then((showEvents) {
showEvents.items.forEach((Event ev) { print(ev.summary); });
querySelector("#text2").text = showEvents.toString();
});
client.close();
flow.close();
});
});
}

Process client side array in KRL raised to Kynetx app

Overview
I am working on building a Kynetx ruleset that will find a bunch of Facebook ids that are on the page and then use the Kynetx Facebook module to get the Facebook avatar associated with that Facebook id. I have the JS that creates an array of Facebook ids on the page and I can process an array in KRL to retrieve Facebook avatars. What I don't have is how to get an array from the client side to the server side in KRL.
How can I get the array from the client side to the server side of KRL?
You can take a JavaScript array and convert it into a string and it will work if you decode it on the server side of KRL.
Example app code => https://gist.github.com/722536
Example app bookmarklet => http://mikegrace.s3.amazonaws.com/forums/stack-overflow/send-array-to-kns-dev-bookmarklet.html
ruleset a60x442 {
meta {
name "array-passing-test"
description <<
array-passing-test
>>
author "Mike Grace"
logging on
}
rule start_your_engines {
select when pageview ".*"
{
notify("Running","...sending array to KNS") with sticky = true;
emit <|
app = KOBJ.get_application("a60x442");
var numbers = [1,2,3,4,5];
nums = JSON.stringify(numbers);
app.raise_event("process_array", {"numbers":nums});
$K("div.KOBJ_message").append("<br/>"+nums);
|>;
}
}
rule process_array {
select when web process_array
foreach event:param("numbers").decode() setting (number)
{
notify("number",number) with sticky = true;
}
}
}
Results of running app from bookmarklet on http://example.com/
Answer
Unfortunately, the KRL JS runtime doesn't yet support sending arrays to the server side. There is a way to accomplish what you are wanting to do though.
Example
I built an example app that runs on this page with a bookmarklet that gets the tags that the question is tagged with and sends them to the server to be processed and then they come back.
Example app code => https://gist.github.com/707561
Example app bookmarklet => http://mikegrace.s3.amazonaws.com/forums/stack-overflow/client-side-array-to-server-bookmarklet.html
Step by step explination of code example
collect text in JS array
convert array into csv string and append comma to make regex splitting easier
raise event to KNS with csv string
process rule pulls first value off
rest of the values are saved to a new variable
first value goes into a notify
postlude sends remaining values to itself
loops until done and returns directives back to the browser
Results of running app from bookmarklet:
You can also do arrays of hashes if you JSON.stringify the array of hashes.
Example app:
ruleset a60x449 {
meta {
name "pass-hash-in-web-event-test"
description <<
pass-hash-in-web-event-test
>>
author "Mike Grace"
logging on
}
rule start_this_party {
select when pageview ".*"
{
notify("Now running","Building arrays to send to KNS") with sticky = true;
emit <|
var data = {};
data.userData = JSON.stringify(
[
{"name":"MikeGrace","id":234232344},
{"name":"TelegramSam","id":234089790234},
{"name":"Alex","id":2300234234234}
]
);
app = KOBJ.get_application("a60x449");
app.raise_event("process_me_data", data);
|>;
}
}
rule process_arrays_of_data {
select when web process_me_data
foreach event:param("userData").decode() setting (user)
pre {
userName = user.pick("$.name");
userId = user.pick("$.id");
output =<<
<p>
userName: #{userName}<br/>
userId: #{userId}<br/>
</p>
>>;
}
{
append("body", output);
}
}
}
Results of running on example.com

Resources