I am writing a Google App Script to parse and extract some content of some emails from Gmail. Unfortunately, I am having a quite hard time parsing the content of a message.
I am calling getPlainBody() on a GmailMessage to apply some parsing via a regex, however it doesn't seem to work properly. It looks like that the string returned by getPlainBody() is incomplete. Let me explain better.
When I apply the regex in this way it doesn't find anything.
function doGet(e) {
// code to get messages from GmailApp
const body = message.getPlainBody()
const result = body.match(/word/gm) // nothing found
const templ = HtmlService.createTemplateFromFile('messages');
templ.body = body
return templ.evaluate();
}
If I visit the Google Script App rendered by the code above, I copy the string value of body rendered via the template, I open the console on the Dev tools and I apply the exact same regex to the copied string, it's working.
Am I missing something? It looks like that the value of body is not "ready" when I try to access it with the match function.
After hours of debugging, I finally found the culprit: the debugger. The function getPlainBody() is working perfectly, it's the Google Script App debugger that is cleaning up the string returned by the function when showing it in the debug panel.
My body variable was full of \r and \n characters that were not showing on the debugging session. This explains why the regex was working fine
when tested on regex101: the string I was copying from the debugger wasn't the actual string assigned to body.
I have spotted the error when I have tried to console.log(body) and then noticed that the value was completely different from the one shown in the debugger.
I fixed the code with the following line:
const body = message.getPlainBody().replace(/\r?\n|\r/g, " ")
Related
Since I tend to ramble, first a short version and if you need more information read the long one.
TL;DR
Why is this:
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
returning this when I published as website and then open:
{"number":""}
when it should look more like this {"number":2451}
Full Version:
First of all, I learned to program back in uni for my Computer science degree (10 years ago) but since then I haven't done much programming so I am basically a newbie.
Now to the question. I have a very simple script that a created with the script editor from Google Sheets
function doGet(e) {
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
Browser.msgBox(jobsCreated);
var params = JSON.stringify({number:jobsCreated});
return ContentService.createTextOutput(params);
}
First I get the sheet I am working on
Then I select a cell from that sheet
now if I use a msgBox to make sure that I have the right number and run the script, it works and it shows the message.
next, I format the variable as JSON and finally I just create a text output.
Now I deploy as Web app
Execute as ME
Anyone, even anonymous
And when I access the website I can only see this:
{"number":""}
If I change the code and give jobsCreated and static value it works fine
var jobsCreated = 100;
{"number":100}
So my conclusion is that the problem is with accessing the value of the cell when running the script from the published link compare to running it directly from the editor, but I have no idea how to fix this.
A little bit more information, i am trying to use this for a counter called Smiirl, i got most of the information from here
https://medium.com/#m_nebra/bootstrapping-your-company-counter-22f5d4bc7dd4
try this:
function doGet(e) {
return ContentService.createTextOutput(Utilities.formatString('number: %s',SpreadsheetApp.getActiveSheet().getRange(12,2).getValue());
}
Ok as I replied to #Aerials, thank you again for your help btw. Seeing other codes that should work not working with my script, I decided to create a new sheet and script as a test and with the exact same code it works.
But now checking on it a little bit more, something that I didn't think it was a problem since it was getting the number without any problems. The cell it's being populated by a GoogleAnalytics add-on. Now when setting up the add-on again to get the information the script from the website returns an empty value again. SO it seems the issue is with the script getting the information from the sheet (only the published version) when its being populated by the add on
Your issue is in the use of JSON.stringify
In JSON, functions are not allowed as object values.
The JSON.stringify() function will omit or change to null any functions from a JavaScript object.
That said, you can do the following:
function doGet(e){
// Get the value of the range
var sheet = SpreadsheetApp.getActiveSheet();
var jobsCreated = sheet.getRange(12,2).getValue();
// JSON Stringify it
var params = JSON.stringify({"number" : number=jobsCreated});
// Return JSON string
return ContentService.createTextOutput(params);
}
Returns:
{"number":123} if jobsCreated is the number 123, or
{"number":"Mangos"} if jobsCreated is the string "Mangos".
See it the script deployed here, and the sheet to play with.
Note:
You should avoid using functions in JSON, the functions will lose their scope, and you would have to use eval() to convert them back into functions.
I want to do some text/paragraphs replacement in Google Docs that are automatically created by an add-on (autoCrat). I tried this successfully on a bound script but now that I want to try it on a standalone script, I get this error:
TypeError: Function getBody not found in the DOCUMENT-NAME object.
I don't understand.
Do I need to call a bound script from the standalone script or something like that?
(I hope not.)
The GAS documentation is not helping at all with this, at least with my understanding of what a standalone script is. Maybe it's a trivial error, but all the examples I found here are for bound scripts, which are not what I'm doing (I've already done a bound script and it's working fine).
This very simple code won't work for a standalone script and I don't understand why :
function Myfunction() {
var file = DriveApp.getFileById('doc-id');
var body = file.getBody();
Logger.log(body);
}
You are getting that error because the class returned by getfilebyID is of type File, not Document.
Try something like this:
let LogFile = DriveApp.getFileById('doc-id');
let LogDoc = DocumentApp.openById(LogFile.getId());
LogDoc.getBody()
So I've been at this for many hours. I'm creating a JSON export script that generates a JSON object from a sheet to be used by another web app.
No matter how I return it, it will always, 100% of the time, tell me the following:
The script completed but the returned value is not a supported return type.
Even by just copy pasting tutorial code, like the following, or as below:
var myDog = {"name": "Rhino", "breed": "pug", "age": 8}
var myJSON = JSON.stringify(myDog);
function doGet(request){
return ContentService.createTextOutput(myJSON).setMimeType(ContentService.MimeType.JSON);
}
It will always return me this error. Strangely, clicking the test button returns the entire object just fine in all cases..
Your script looks good. Make sure you have published a new version of the web app after changing the code.
When executing a script directly in the console in Chrome, I saw this:
Does anyone know what's the meaning of VM117:2
What does VM stand for ?
It is abbreviation of the phrase Virtual Machine.
In the Chrome JavaScript engine (called V8) each script has its own script ID.
Sometimes V8 has no information about the file name of a script, for example in the case of an eval. So devtools uses the text "VM" concatenated with the script ID as a title for these scripts.
Some sites may fetch many pieces of JavaScript code via XHR and eval it. If a developer wants to see the actual script name for these scripts she can use sourceURL. DevTools parses and uses it for titles, mapping etc.
Thanks to #MRB,
I revisited this problem, and found the solution today,
thanks to https://stackoverflow.com/a/63221101/1818089
queueMicrotask (console.log.bind (console, "Look! No source file info..."));
It will group similar elements, so make sure you add a unique identifier to each log line to be able to see all data.
Demonstrated in the following example.
Instead of
data = ["Apple","Mango","Grapes"];
for(i=0;i<10;i++){
queueMicrotask (console.log.bind (console, " info..."+i));
}
use
data = ["Apple","Mango","Grapes"];
for(i=0;i<data.length;i++){
queueMicrotask (console.log.bind (console, " info..."+i));
}
A better way would be to make a console.print function that does so and call it instead of console.log as pointed out in https://stackoverflow.com/a/64444083/1818089
// console.print: console.log without filename/line number
console.print = function (...args) {
queueMicrotask (console.log.bind (console, ...args));
}
Beware of the grouping problem mentioned above.
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.