ARTICLE AD BOX
Set a format or you'll get minimal data back
The key problem here is that I had to provide a format when calling get_user_message, otherwise that call still returned a "bare" message with just the IDs.
I went for this:
updated_message = gmail.get_user_message('me', message.id, format: :full)Set the right format for the fields you want to use
What makes this even more confusing is that the format you choose affects which fields will be populated. For the raw field, I'd have needed a format of :raw, for example. It took me a long time to track down the list of supported formats, but I eventually found them here: https://developers.google.com/workspace/gmail/api/reference/rest/v1/Format
Extract the data correctly - handle multi-part
Once you have a populated message, you have to extract the data from it correctly.
If all you want is to call .raw, then choose a format of :raw and you'll get data back.
But for data that you can interact with more easily (the :full format), actually extracting the body is more complicated.
To begin with, you'll need to call updated_message.payload to get the Message Part. Confusingly, though, if you call updated_message.payload.body.data, that'll probably also be blank.
That field will contain data if the message is a single-part type such as text/plain. But in my experience multi-part messages are more common, so you'll need to iterate over the parts by calling parts.
Here's some code which extracts the body in either case:
payload = updated_message.payload return payload.body.data if payload.parts.empty? payload.parts.map { |part| decode_part(part.body.data) }.join("\n")For the multi-part case, this code just concatenates all the parts together, which may not be what you want. In particular, note that this simple example doesn't handle attachments.
Decode the body - or not!
The docs for the data method say 'The body data of a MIME message part as a base64url encoded string.'
Theoretically, that means you'd need to call CGI.unescape(Base64.decode64(message_part.data)). But in my testing that's not what data returned and so you'll see that in the above we're just working with it directly.
