Trigger Google Apps Script by email - google-apps-script

I'm looking for examples of a pattern where a demon script running within a GoogleAppsForBusiness domain can parse incoming email messages. Some messages will will contain a call to yet a different GAScript that could, for example, change the ACL setting of a specific document.
I'm assuming someone else has already implemented this pattern but not sure how I go about finding examples.
thx

You can find script examples in the Apps Script user guide and tutorials. You may also search for related discussions on the forum. But I don't think there's one that fits you exactly, all code is out there for sure, but not on a single script.
It's possible that someone wrote such script and never published it. Since it's somewhat straightforward to do and everyone's usage is different. For instance, how do you plan on marking your emails (the ones you've already read, executed, etc)? It may be nice to use a gmail filter to help you out, putting the "command" emails in a label right away, and the script just remove the label (and possibly set another one). Point is, see how it can differ a lot.
Also, I think it's easier if you can keep all functions in the same script project. Possibly just on different files. As calling different scripts is way more complicated.
Anyway, he's how I'd start it:
//set a time-driven trigger to run this function on the desired frequency
function monitorEmails() {
var label = GmailApp.getUserLabelByName('command');
var doneLabel = GmailApp.getUserLabelByName('executed');
var cmds = label.getThreads();
var max = Math.min(cmds.length,5);
for( var i = 0; i < max; ++i ) {
var email = cmds[i].getMessages()[0];
var functionName = email.getBody();
//you may need to do extra parsing here, depending on your usage
var ret = undefined;
try {
ret = this[functionName]();
} catch(err) {
ret = err;
}
//replying the function return value to the email
//this may make sense or not
if( ret !== undefined )
email.reply(ret);
cmds[i].removeLabel(label).addLabel(doneLabel);
}
}
ps: I have not tested this code

You can create a google app that will be triggered by an incoming email message sent to a special address for the app. The message is converted to an HTTP POST which your app receives.
More details here:
https://developers.google.com/appengine/docs/python/mail/receivingmail
I havn't tried this myself yet but will be doing so in the next few days.

There are two ways. First you can use Google pub/sub and handle incomming notifications in your AppScrit endpoint. The second is to use the googleapis npm package inside your AppScript code an example here. Hope it helps.
These are the steps:
made a project on https://console.cloud.google.com/cloudpubsub/topicList?project=testmabs thing?
made a pubsub topic
made a subscription to the webhook url
added that url to the sites i own, i guess? I think I had to do DNS things to confirm i own it, and the error was super vague to figure out that was what i had to do, when trying to add the subscription
added permission to the topic for "gmail-api-push#system.gserviceaccount.com" as publisher (I also added ....apps.googleusercontent.com and youtrackapiuser.caps#gmail.com but i dont think I needed them)
created oauth client info and downloaded it in the credentials section of the google console. (oauthtrash.json)

Related

Writing a script to Clear Data at a specific time, Archive the cleared data and if possible move certain fields to a separate sheet

I have a google form that I am trying to have people answer questions to each day. It is mandatory that this form is completed before they come to the facility. I need a script to clear the data in the Response Tab at midnight each night and I need that data that is being cleared to be archived in a separate sheet if possible. A bonus feature would if as the responses come in each day, the could populate on a separate sheet with 4 fields, "Student ID", "Last Name", "First Name" and "Cleared"
I know I'm asking a lot! The most important thing is the clearing daily and being able to archive the previous day's data (even if it has to be a tab in that file)
To be fair I'm also very new to using scripts if I have to do anything special besides add script I might need some guidance there as well.
Any help anyone could give would be awesome!!!
As Nabnub has already indicated, this is really too much for one question. However, the appointment scheduling system that I made for my tutoring website, (see tutoringbyroger.com), does something quite similar to what you would need, so I will give you some quick pointers below on what worked for me:
You will need to open up the form where the information comes in and go into the script editor from there. Once in there, this function should get you started:
function closeForm(e){
// First we will get the information they entered and parse it
// into an array of strings:
var responsesArray = e.response.getItemResponses();
var txtResponsesArray = [];
var len = responsesArray.length;
for(var i=0; i < len; ++i){
txtResponsesArray[i] = responsesArray[i].getResponse();
}
// We can now do stuff with the strings in txtResponsesArray, like add them to
// a spreadsheet or something.
}
In the script editor you will need to click on triggers, and add a trigger to run the above function when the form is closed. That way, you can capture the data and process it the instant it is submitted.
Create a separate function whose sole purpose is to clear out the information that you would like to clear. This can be from the form or the spreadsheet, or both. It can be in the same script file, just outside of the closeForm() function.
Create another trigger to run the function that does the deleting when you would like that task done.
Anyhow, that is a taste of what you will need to get started. Here are some relevant links to some documentation you might find useful:
How to open a form. (Note: the ID comes from the url of the form when you edit it)
How to open a spreadsheet.
I hope that gives you some idea of where to start.

Chromeosdevices deviceid vs serial number

The Chromeosdevices API relies on the deviceID parameter to find devices on the back end. That has caused a bunch of confusion and frustration on my end. Initially, I thought the deviceID was the serial # of the device. We typically do all searches for device on Google's Admin console using the serial #'s, so it just made sense. I realize now that the deviceID is not the serial #.
Is there a way, then, to translate serial # to deviceId? I'm thinking I may need to export out the entire directory in some table, and then do look ups using the serial # as the reference key. But, it would be nice to figure out a programmatic way to do it. I tried searching stack overflow to no avail.
Thanks,
Figured this out. What a headache. First, if you're developing an Apps Script, there is no need to access the Admin Directory API via UrlFetchApp and trying to get it to work with Oauth2 libraries. All you need is the direct API, which I wish was illustrated in Google's API document and API explorer. Instead, use Admin SDK directly. You can create a script file with a bunch of helper functions to call from your main script:
function getDeviceId(serialnum) {
var optionalArgs = {
projection: 'BASIC',
query: serialnum,};
var chromebook = (AdminDirectory.Chromeosdevices.list("my_customer", optionalArgs)) ;
var chromebookDevID = chromebook.chromeosdevices[0].deviceId;
return chromebookDevID ;
}
For example, you call this function, pass in the serial number, and it will return the chromebook's deviceId as such
var deviceId = getDeviceId('5CD81072C4');
From this, you can also have helper functions to enable / disable chromebooks:
function disableChromeBook(deviceId) {
AdminDirectory.Chromeosdevices.action({"action": "disable"}, "my_customer", deviceId) ;
}
function enableChromeBook(deviceId) {
AdminDirectory.Chromeosdevices.action({"action": "reenable"}, "my_customer", deviceId)
}
I found the Google API document to be really obtuse. I hope this helps someone else out. Took me awhile.

Basic UiApp failing with error "ReferenceError: "UIApp" is not defined."

I am creating some google app scripts for my company to use to generate random tests for employee training. I already have the basic scripts written to grab a list of questions from a google sheet, randomize them, grab the first 10 questions, etc. That all works fine. I decided it might be better to re-do the whole thing using a UiApp instead of just separate scripts. That is where the problem comes in. I did a simple bare bones UiApp to test with, published it and tried to hit the URL and that's where I encounter this error. I searched for this error and all I could find was some discussion about this being part of google apps premiere(which should have been folded into regular google apps around 2010). I've been staring at this so long I've frustrated myself. It should be something very simple and yet it's refusing to work. I'm assuming I am doing something wrong at a basic level but I've reached the point where my brain refuses to see it.
Here is the basic script I started with:
function doGet(e) {
var app = UiApp.createApplication();
var mainPanel = app.createVerticalPanel().setId('mainPanel');
mainPanel.add(app.createLabel('test'));
return app;
}
I save it, publish it and go to the URL and that's when I get the above error message. I know it's something simple but I've reached the point of frustration and simply can't see it.
Update: to reflect comments
Another possibility for WebApps not updating is not publishing a new version and only checking the exec URL. For instant changes to the code, always check the dev URL. The exec will only change after saving a version in Manage Versions and re-publishing the app.
First Answer:
I think your question title says it all.
UIApp is not defined, but Class is UiApp. JS is case sensitive. I copied and pasted the code exactly as it is in your question and received no errors. I did have to add one line to make the label show up.
function doGet(e) {
var app = UiApp.createApplication();
var mainPanel = app.createVerticalPanel().setId('mainPanel');
mainPanel.add(app.createLabel('test'));
app.add(mainPanel); // <-- I added this line to see the label
return app;
}

Need help parsing through a page & getting all comments found into an Array using a RegEx pattern in Google Apps Script?

My problem lies in 2 parts however I'm hoping solving 1 will fix the other. I've been trying to parse through a page and get all the comments found within a forum thread.
The comments are found using a RegEx pattern and the idea is that whatever lies in the comment will be read into an array until there aren't any more comments left. Each comment div follows this format
<div id="post_message_480683" style="margin-right:2px;"> something </div>
I'm trying to locate up to "post_message_[some number]" since each number seems to be generated randomly and then get whatever is between that particular div. My 1st problem is my RegEx just doesn't seem to be working I've tried a few but none yielded any results (except for when I insert the post message no. in manually), Here's the code so far:
function GetPosts() {
var posts = new Array(60);
var url = "http://forums.blackmesasource.com/showthread.php?p=480683";
var geturl = UrlFetchApp.fetch(url).getContentText().toString();
var post_match = geturl.match(/<div id="post_message_(.+)" style="margin-right:2px;">(\w.+)<\/div>/m);
Logger.log(post_match);
}
Edit: I initially tried getting this info via GAS's Xml.Parse() class but after grabbing the URL I just didn't know what to do since suffixing
.getElement().getElement('div') (I also tried .getElements('div') and other variations with 'body' & 'html')
would cause an error. Here is the last code attempt I tried before trying the RegEx route:
function TestArea() {
var url = "http://forums.blackmesasource.com/showthread.php?p=480683";
var geturl = UrlFetchApp.fetch(url).getContentText().toString();
//after this point things stop making sense
var parseurl = Xml.parse(geturl, true);
Logger.log(geturl);
//None of this makes sense because I don't know HOW!
//The idea: Store each cleaned up Message Div in an Array called posts
//(usually it's no more than 50 per page)
//use a for loop to write each message into a row in GoogleSpreasheet
for (var i = 0; i <= parseurl - 1; i++) {
var display = parseurl[i];
Logger.log(parseurl); }
}
Thanks for reading!
In general like the comment points out - be aware of parsing HTML with RegEx.
In my past personal experience, I've used Yahoo's YQL platform to run the HTML through and using XPath on their service. Seems to work decently well for simple reliable markup. You can then turn that into a JSON or XML REST service that you can grab via UrlFetch and work on that simplified response. No endorsement here, but this might be easier than to bring down the full raw HTML into Google Apps Script. See below for the YQL Console. I also don't know what their quotas are - you should review that.
Of course, the best course is to convince the site owner to provide an RSS feed or an API.

Changing the state of apps script webapp

I'm making standalone web app in Google Apps Script. I have somekind of task flow in the app. First search something from spreadsheet, then do some selections what to do with the collected data and after that fill spreadsheet row with some userinputs. So I need to run between few states.
I'm fairly sure I don't know enough about web tech so this might be very stupid question but here it goes.
I know that the e.parameters comes from the url like this www.google.com&value=helloworld
, e.parameters.value returns helloworld.
So now the problem: How do I set parameter e values in doGet(e) and call this same page or even different page/script in same script project? In otherwords how do I call self&value=helloworld ?
Is there some other way to do this? In GWT I just store everything to database and keep session in cookies. But cookies are not allowed so how to keep the state of the webapp in google apps script?
EDIT: This is how I pass the parameters for doGet(e).
function doGet(e) {
var app = UiApp.createApplication();
var newValue = 0;
if(e.parameter == undefined || e.parameter.value == undefined){
newValue = 1;
}else{
newValue = 1+parseInt(e.parameter.value);
}
var link = 'https://script.google.com/a/macros/domain.com/s/<insertidhere>/dev';
var anchor = app.createAnchor('Next',link+'?&value='+newValue);
anchor.setTarget('_self');
app.add(anchor);
var label = app.createLabel(newValue);
app.add(label);
return app;
}
The link format has changed a bit so instead of concatenate &value=... you need to add ? first like this ?&value1=helloworld&value2=....
Failing to use ? led me to think that there is bug and I need to use old format for the link and using that old format forbit any other than the script owner using it.
Hopefully this helps somebody to solve similar problems.
You've almost answered yourself. Append the URL paramenters to the end of your web app's URL and you can access them in your script.
Let's say you have a URL like
http://script.google.com/.../exec
When you hit the URL
http://script.google.com/.../exec?value=helloworld
,
inside doGet, you can read these URL parameters as
e.parameter.value;
IMO - instead of using ? to separate multiple parameters try keeping all the values in a single anchor with some other character separator of your choice such as # since after the first ? its possibly ignoring the other one and everything afterwards. Would be great to hear back on what you find.