Importing Gmail text to a specific Google Sheets - spreadsheet

I receive everyday the same email from an app I've made. Those emails have the same text except for some numbers (for example 2 instead of 9). I'm trying to build a script that automatically compiles my Google Sheets report.
function myFunction() {
var thread = GmailApp.getUserLabelByName("").getThreads(0,1)[0]; // get first thread in inbox
var message = thread.getMessages()[0]; // get first message
Logger.log(message.getBody()); // log contents of the body
}
but it doesn't work.
What am I doing wrong?

The following script works for me. Note that due to the time it takes to execute and to stop duplicate results, I change the label after it is moved to the spreadsheet.
function myFunction() {
var ss = SpreadsheetApp.openById("Insert Sheet ID");
var sheet = ss.getSheetByName("Email Import");
var label = GmailApp.getUserLabelByName("Label");
var labelNew = GmailApp.getUserLabelByName("Label Moved");
var threads = label.getThreads();
for (var i=0; i<threads.length; i++)
{
var messages = threads[i].getMessages();
for (var j=0; j<messages.length; j++)
{
var sub = messages[j].getBody();
sheet.appendRow([sub])
}
threads[i].addLabel(labelNew);
threads[i].removeLabel(label);
}
}

Related

How to get the row reference for the current cell in Google Scripts?

EDITED POST
I am a script newbie so bear with me... :)
I am getting the script to send an email using the info in the 28th thru 32nd column. I need to get the value of those cells in the current row.
With the script below, it works perfect if it is row 4 and I write 4.
function SendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Form Responses 1');
var sheet2=ss.getSheetByName('TEMPLATE');
var subject = sheet2.getRange(2,1).getValue();
var addressee=sheet1.getRange(4,28).getValue();
var emailAddress = sheet1.getRange(4,29).getValue();
var room=sheet1.getRange(4,30).getValue();
var date=sheet1.getRange(4,31).getValue();
var time=sheet1.getRange(4,32).getValue();
var message = sheet2.getRange(2,2).getValue();
message=message.replace("<addressee>",addressee).replace("<room>",room).replace("<date>",date).replace("<time>",time);
MailApp.sendEmail(emailAddress, subject, message);
};
How do I get it to recognize what row is active and use that value? U sed the suggestion from "Huaye sun" as follows but I get the following error... "Exception: Failed to send email: no recipient"
The email address it should grab is in fact there. What am I missing?
function SendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet1=ss.getSheetByName('Form Responses 1');
var sheet2=ss.getSheetByName('TEMPLATE');
var subject = sheet2.getRange(2,1).getValue();
var activeCell = sheet1.getActiveCell();
var currentRow = activeCell.getRow();
var addressee=sheet1.getRange(currentRow,28).getValue();
var emailAddress = sheet1.getRange(currentRow,29).getValue();
var room=sheet1.getRange(currentRow,30).getValue();
var date=sheet1.getRange(currentRow,31).getValue();
var time=sheet1.getRange(currentRow,32).getValue();
var message = sheet2.getRange(2,2).getValue();
message=message.replace("<addressee>",addressee).replace("<room>",room).replace("<date>",date).replace("<time>",time);
MailApp.sendEmail(emailAddress, subject, message);
};

Blazor Server: Attempting to take files from InputFile into an Email attachment

I am currently trying to get the files received from InputFile and attach it as an email attachment. I followed this website to get my InputFile with the progress bar: https://www.meziantou.net/file-upload-with-progress-bar-in-blazor.htm.
I have tried various options such as converting the file to byte array, using memory stream, and using a file stream but I do not have a path to copy the file too. Here's my code currently on what I am trying to accomplish. The Email is sent through SMTP client and that works perfectly without the attachments.
private async ValueTask LoadFiles(InputFileChangeEventArgs e)
{
var files = e.GetMultipleFiles(maximumFileCount: 100);
filesList = e.GetMultipleFiles(maximumFileCount: 100);
var startIndex = uploadedFiles.Count;
// Add all files to the UI
foreach (var file in files)
{
var progress = new FileUploadProgress(file.Name, file.Size);
uploadedFiles.Add(progress);
}
await using var timer = new Timer(_ => InvokeAsync(() => StateHasChanged()));
timer.Change(TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(500));
// Upload files
byte[] buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(4096);
try
{
foreach (var file in files)
{
MemoryStream ms = new MemoryStream();
using var stream = file.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024);
while (await stream.ReadAsync(buffer) is int read && read > 0)
{
uploadedFiles[startIndex].UploadedBytes += read;
file.OpenReadStream().CopyTo(ms);
var fileBytes = ms.ToArray();
Attachment fileAttch = new Attachment(new MemoryStream(fileBytes), file.ContentType);
message.Attachments.Add(fileAttch);
var readData = buffer.AsMemory().Slice(0, read);
}
startIndex++;
}
}
finally
{
System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
// Update the UI with the final progress
StateHasChanged();
}
}
When using the debugger, I noticed that the try block breaks whenever I try to copy the file into MemoryStream. I am not sure why. Any help or solutions would be greatly appreciated.
Thank you
I have tried copying the file/buffer into the memory stream but the try block breaks. I have tried to use file stream without success. I am either missing something I am unaware of or I am not implementing the code correctly.

Skip adding empty tables to PDF when parsing XHTML using ITextSharp

ITextSharp throws an error when you attempt to create a PdfTable with 0 columns.
I have a requirement to take XHTML that is generated using an XSLT transformation and generate a PDF from it. Currently I am using ITextSharp to do so. The problem that I am having is the XHTML that is generated sometimes contains tables with 0 rows, so when ITextSharp attempts to parse them into a table it throws and error saying there are 0 columns in the table.
The reason it says 0 columns is because ITextSharp sets the number of columns in the table to the maximum of the number of columns in each row, and since there are no rows the max number of columns in any given row is 0.
How do I go about catching these HTML table declarations with 0 rows and stop them from being parsed into PDF elements?
I've found the piece of code that is causing the error is within the HtmlPipeline, so I could copy and paste the implementation into a class extending HtmlPipeline and overriding its methods and then do my logic to check for empty tables there, but that seems sloppy and inefficient.
Is there a way to catch the empty table before it is parsed?
=Solution=
The Tag Processor
public class EmptyTableTagProcessor : Table
{
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent)
{
if (currentContent.Count > 0)
{
return base.End(ctx, tag, currentContent);
}
return new List<IElement>();
}
}
And using the Tag Processor...
//CSS
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);
//HTML
var fontProvider = new XMLWorkerFontProvider();
var cssAppliers = new CssAppliersImpl(fontProvider);
var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
tagProcessorFactory.AddProcessor(new EmptyTableTagProcessor(), new string[] { "table" });
var htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.SetTagFactory(tagProcessorFactory);
//PIPELINE
var pipeline =
new CssResolverPipeline(cssResolver,
new HtmlPipeline(htmlContext,
new PdfWriterPipeline(document, pdfWriter)));
//XML WORKER
var xmlWorker = new XMLWorker(pipeline, true);
using (var stringReader = new StringReader(html))
{
xmlParser.Parse(stringReader);
}
This solution removes the empty table tags and still writes the PDF as a part of the pipeline.
You should be able to write your own tag processor that accounts for that scenario by subclassing iTextSharp.tool.xml.html.AbstractTagProcessor. In fact, to make your life even easier you can subclass the already existing more specific iTextSharp.tool.xml.html.table.Table:
public class TableTagProcessor : iTextSharp.tool.xml.html.table.Table {
public override IList<IElement> End(IWorkerContext ctx, Tag tag, IList<IElement> currentContent) {
//See if we've got anything to work with
if (currentContent.Count > 0) {
//If so, let our parent class worry about it
return base.End(ctx, tag, currentContent);
}
//Otherwise return an empty list which should make everyone happy
return new List<IElement>();
}
}
Unfortunately, if you want to use a custom tag processor you can't use the shortcut XMLWorkerHelper class and instead you'll need to parse the HTML into elements and add them to your document. To do that you'll need an instance of iTextSharp.tool.xml.IElementHandler which you can create like:
public class SampleHandler : iTextSharp.tool.xml.IElementHandler {
//Generic list of elements
public List<IElement> elements = new List<IElement>();
//Add the supplied item to the list
public void Add(IWritable w) {
if (w is WritableElement) {
elements.AddRange(((WritableElement)w).Elements());
}
}
}
You can use the above with the following code which includes some sample invalid HTML.
//Hold everything in memory
using (var ms = new MemoryStream()) {
//Create new PDF document
using (var doc = new Document()) {
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
//Sample HTML
string html = "<table><tr><td>Hello</td></tr></table><table></table>";
//Create an instance of our element helper
var XhtmlHelper = new SampleHandler();
//Begin pipeline
var htmlContext = new HtmlPipelineContext(null);
//Get the default tag processor
var tagFactory = iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory();
//Add an instance of our new processor
tagFactory.AddProcessor(new TableTagProcessor(), new string[] { "table" });
//Bind the above to the HTML context part of the pipeline
htmlContext.SetTagFactory(tagFactory);
//Get the default CSS handler and create some boilerplate pipeline stuff
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
var pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new ElementHandlerPipeline(XhtmlHelper, null)));//Here's where we add our IElementHandler
//The worker dispatches commands to the pipeline stuff above
var worker = new XMLWorker(pipeline, true);
//Create a parser with the worker listed as the dispatcher
var parser = new XMLParser();
parser.AddListener(worker);
//Finally, parse our HTML directly.
using (TextReader sr = new StringReader(html)) {
parser.Parse(sr);
}
//The above did not touch our document. Instead, all "proper" elements are stored in our helper class XhtmlHelper
foreach (var element in XhtmlHelper.elements) {
//Add these to the main document
doc.Add(element);
}
doc.Close();
}
}
}

Multiple HTTP Requests in WinRT / Win8

Is it possible to send more than two HTTP requests concurrently in WinRT? I'm trying to load multiple JSON documents from a server and HttpWebRequest fails to respond after the second call. Here is a sample snippet that illustrates this:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
const string url = "http://www.bom.gov.au/fwo/IDV60901/IDV60901.94868.json";
const int iterations = 3;
var tasks = new List<Task>();
var ticks = DateTime.Now.Ticks;
for (var i = 0; i < iterations; i++)
{
// Create unique URL by appending a generated number.
var uniqueUrl = string.Format("{0}?v={1}", url, (i + ticks));
// Create the request.
var request = WebRequest.CreateHttp(uniqueUrl);
// Create the async task and store it for later.
var task = request.GetResponseAsync();
tasks.Add(task);
}
// Await all tasks in collection.
await Task.WhenAll(tasks);
Debugger.Break(); // <----- This will never break when iterations > 2
}
Put this code in a blank MainPage.xaml.cs and play around with the iterations value. If you set it to 2, then it works. Anything above that, it will fail.
NOTE :: Do not use Fiddler when testing this. Fiddler does something funny and it allows all these connections to go through. I don't know how nor why. You can test this yourself. If you run the code above with fiddler open, then success.
NOTE :: This is not real code. I'm only using this example to illustrate the issue.
I haven't tried using the WebClient API in WinRT, I've only used the HttpClient API (which I'm using quite extensively in my application).
This code works:
const string url = "http://www.bom.gov.au/fwo/IDV60901/IDV60901.94868.json";
const int iterations = 10;
var tasks = new List<Task<HttpResponseMessage>>();
var ticks = DateTime.Now.Ticks;
for (var i = 0; i < iterations; i++)
{
// Create unique URL by appending a generated number.
var uniqueUrl = string.Format("{0}?v={1}", url, (i + ticks));
var handler = new HttpClientHandler();
var client = new HttpClient(handler)
{
BaseAddress = new Uri(uniqueUrl)
};
var task = client.GetAsync(client.BaseAddress);
tasks.Add(task);
}
// Await all tasks in collection.
await Task.WhenAll(tasks);
It is a bit more tedious to get out the response body though as you need to do an async read of all the responses like so:
var responseTasks = tasks.Select(task => task.Result.Content.ReadAsStringAsync());
await Task.WhenAll(responseTasks);
Then you can iterate through the responseTask objects and take their result.

Which RadioButton in a group is checked (Google AppScript)

Using Google AppScript. How could I find the checked RadioButton in a group? If it requires handler then with server one.
Many Thanks
there is an open issue on this but there is also a nice workaround ;-) (found by Romain Vialard, GAS TC)
here is a slightly modified version of his script adapted to run on a spreadsheet :
function radiotest() {
var app = UiApp.createApplication();
var panel = app.createVerticalPanel();
var radioValue = app.createTextBox();
radioValue.setId("radioValue").setName("radioValue");
// var radioValue = app.createHidden().setName("radioValue") ;// choose the one you like
for(var i = 1; i < 10; i++){
var name = 'choice '+i;
var handler = app.createClientHandler().forTargets(radioValue).setText(name);
panel.add(app.createRadioButton('radioButtonGroup',name).addValueChangeHandler(handler));
}
panel.add(radioValue);
var Valide=app.createButton("Valide").setId("val");
panel.add(Valide)
app.add(panel);
//
var handler = app.createServerHandler("valide"); // this is the server handler
handler.addCallbackElement(radioValue)
Valide.addClickHandler(handler);
//
SpreadsheetApp.getActiveSpreadsheet().show(app);// show app
}
//
function valide(e){ ;// This function is called when key "validate" is pressed
var sh = SpreadsheetApp.getActiveSheet();
var RadioButton = e.parameter.radioValue;
sh.getRange('A1').setValue(RadioButton);
var app = UiApp.getActiveApplication();
return app;
}​
Note that the radioValue item can be either a textbox or a hidden text, both possibilities are in the script/