Google GMail API - users.messages.import() not in INBOX Why? - gmail

I am importing directly a message using the GMAil API
When I look in to the GMail user account ( browser GMail app) I can see th message received in All Mails , but not in the INBOX
Google GMail API states : Users.messages.import()
Imports a message into only this user's mailbox, with standard email delivery scanning and classification similar to receiving via SMTP. Does not send a message
As the message is laying there I cannot get it from a Mail.app (OSX) or Outlook (MS) since it's not considered as a new message ... is that true ? anyway to solved this issue ?
Here is the function I am using to import a new message
unction gMailInserMessage (sender_name, sender_email, msg_text) {
// Create a new JWT client using the key file downloaded from the Google Developer Console
const jwtClient = new google.auth.JWT(
postoffice_key.client_email,
null,
postoffice_key.private_key,
_.values(config_key.scopes.postoffice),
config_key.contact_email // subject (or sub) impersonated user
);
return jwtClient.authorize().then(tokens => {
// Obtain a new gmail client, making sure we pass along the auth client
const gmail = google.gmail({
version: 'v1',
auth: jwtClient
});
const subject = '🤘 CONTACT 🤘';
const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
const messageParts = [
'From: ' + sender_name + '<' + sender_email + '>',
'To: Contact Box <' + config_key.contact_email + '>',
'Content-Type: text/html; charset=utf-8',
`Subject: ${utf8Subject}`,
'',
'A new contact message just to say hello.',
'So... <b>Hello!</b> 🤘❤️😎'
];
const message = messageParts.join('\n');
// The body needs to be base64url encoded.
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
//Make an authorized request to import a User Messages
return gmail.users.messages.import({
userId: 'me',
resource: {
raw: encodedMessage
}
});
}).then(res => {
// console.log('RES: ', res)
return res.data;
});
}
Here is the Response I get from the requets
RES: { status: 200,
statusText: 'OK',
headers:
{ 'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
pragma: 'no-cache',
expires: 'Mon, 01 Jan 1990 00:00:00 GMT',
date: 'Sun, 27 May 2018 10:00:12 GMT',
etag: '"Mr5Glppow16hK9x9KiNoxDVbWS4/98vlvSafvJVr4JD8evQ2SHoRCuQ"',
vary: 'Origin, X-Origin',
'content-type': 'application/json; charset=UTF-8',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '1; mode=block',
server: 'GSE',
'alt-svc': 'hq=":443"; ma=2592000; quic=51303433; quic=51303432; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="43,42,41,39,35"',
connection: 'close',
'transfer-encoding': 'chunked' },
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: 2147483648,
validateStatus: [Function],
headers:
{ Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=utf-8',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/31.0.2 (gzip)',
Authorization: 'Bearer ya29.GoEByAXKR5aJgJanVZebkHwurMcOn6FB4ymK9BZqmi_0K0uMPK_1AJAL-EQa0ajz1OBDoJE1cWAyRzprOYbDnJrnpusrPfFSL7HuBmMqXFULxEKECedOb5pKwkTFA9CffIZS1Fg1uwuGcsgUqO98XOkYeRh9ul7icvBpuzUgaML0SVJN',
'Content-Length': 397 },
method: 'post',
url: 'https://www.googleapis.com/gmail/v1/users/me/messages/import',
paramsSerializer: [Function],
data: '{"raw":"RnJvbTogTW1lIElzYWJlbGxlIER1Zm91ciA8ZHVmb3VyaXNhYmVsbGVAb3JhbmdlLmZyPgpUbzogQ29udGFjdCBCb3ggPHl2ZXNkdWZvdXJAbGVjaG9yb2Rlc2NoYXJlbnRlcy5vcmc-CkNvbnRlbnQtVHlwZTogdGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04Ck1JTUUtVmVyc2lvbjogMS4wClN1YmplY3Q6ID0_dXRmLTg_Qj84SitrbUNCRFQwNVVRVU5VSVBDZnBKZz0_PQoKVGhpcyBpcyBhIE5FVyBORVcgbWVzc2FnZSBqdXN0IHRvIHNheSBoZWxsby4KU28uLi4gPGI-SGVsbG8hPC9iPiAg8J-kmOKdpO-4j_CfmI4"}',
params: { internalDateSource: 'dateHeader' } },

Solved all the issues raised upon trying to use a Firebase function to handle a site Contact Form.
1 - Using GCP, in my Firebase project, I created a Service Account named 'postoffice', with Domain Wide Delegation, downloaded the credentials in JSON format into functions/postoffice-key.json
2 - I enabled GMail API for the project
3 - I handle the scopes and email addresses in another functions/config-key.json
4- In My GSuite Admin console ( Security>Advanced settings > Manage API Client Access , I added the clientId from the postoffice-key.json file with the authorized scopes
5 - I wrote a new Firebase HTTPS Function : newContactMessage()
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const { google } = require('googleapis');
const path = require('path');
const _ = require('lodash');
var Buffer = require('buffer/').Buffer // note: the trailing slash is important!
// KEY FILES
const postoffice_key = require('./postoffice-key.json');
const config_key = require('./config-key.json');
function gMailInsertContactMessage (sender_name, sender_email, msg_text) {
// Create a new JWT client using the key file downloaded from the Google Developer Console
const jwtClient = new google.auth.JWT(
postoffice_key.client_email,
null,
postoffice_key.private_key,
_.values(config_key.scopes.postoffice),
config_key.admin_email // subject (or sub) impersonated user
);
return jwtClient.authorize().then(tokens => {
// Obtain a new gmail client, making sure we pass along the auth client
const gmail = google.gmail({
version: 'v1',
auth: jwtClient
});
const subject = '🤘 CONTACT 🤘';
const utf8Subject = `=?utf-8?B?${Buffer.from(subject).toString('base64')}?=`;
const messageParts = [
'From: "Administrator" <' + config_key.admin_email + '>',
'To: "Contact Box" <' + config_key.contact_email + '>',
'Reply-To: "' + sender_name + '" <' + sender_email + '>',
'Content-Type: text/html; charset=utf-8',
'MIME-Version: 1.0',
`Subject: ${utf8Subject}`,
'',
'<h2>MESSAGE</h2>',
'<p>' + msg_text + '</p>'
];
const message = messageParts.join('\n');
// The body needs to be base64url encoded.
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
//Make an authorized request to import a User Messages
return gmail.users.messages.send({
userId: 'me',
resource: {
raw: encodedMessage
}
});
}).then(res => {
// console.log('RES: ', res)
return res.data;
});
}
// FIREBASE HTTP FUNCTIONS ==================
exports.newContactMessage = functions.https.onRequest((req, res) => {
const sender_name = 'Mrs Jenny Doe'
const sender_email = 'jenny.doe#example.com'
const sender_msg = 'Hello, how are you overthere ?'
gMailInsertContactMessage(sender_name, sender_email, sender_msg).then((res) => {
return { status: 200, infos: res };
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});
test request:
curl -v https://us-central1-myproject.cloudfunctions.net/newContactMessage
Firebase function returns :
{"status":200,"infos"
{"id":"163a5738080e3ee9","threadId":"163a5738080e3ee9","labelIds":["SENT"]}}
In the Mail.app on my computer, I can see the From: being the project owner ( Administrator ) the To: being the Contact box ( contact#mysite.org and the Reply-To: being the Contact Form sender email... easy to reply..!
and the message content !
Hope this help... ( should I edit the question title ? )

Related

MailChimp: set Language with nodejs SDK

I'm trying to set a suscriber's Language with MailChimp nodejs SDK while adding him/her to a list.
The nodejs SDK calls the API https://us2.api.mailchimp.com/3.0/lists/<list-ID>/members, and it successfully creates a new member. However, it doesn't set the user's language. I read online that I have to pass the Accept-Language header with my HTTP request, so it is what I did.
In order to be able to add a custom header using the SDK, I slightly edited the SDK to add a defaultHeaders option. With this modification, the header is correctly set, but unfortunately it doesn't change anything: the new member still doesn't have the language set.
Here is my code:
import mailchimp from "#mailchimp/mailchimp_marketing";
export const handler = async(event) => {
const params = JSON.parse(event.body);
mailchimp.setConfig({
apiKey: process.env.apiKey,
server: process.env.server,
defaultHeaders: {
'Accept-Language': event.headers['accept-language'],
},
});
const email = params.email;
const firstname = params.name.substring(0, params.name.indexOf(' ')) || "";
const lastname = params.name.substring(params.name.indexOf(' ') + 1) || "";
return await mailchimp.lists.addListMember(process.env.listId,
{
email_address: email,
status: 'subscribed',
email_type: 'html',
merge_fields: {
FNAME: firstname,
LNAME: lastname,
},
}
);
};
The generated request is the following:
Request {
method: 'POST',
url: 'https://us2.api.mailchimp.com/3.0/lists/<list-ID>/members',
header: {
'User-Agent': 'node-superagent/3.8.1',
Authorization: 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=',
'Accept-Language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7',
'Content-Type': 'application/json',
Accept: 'application/json'
},
writable: true,
cookies: '',
qs: {},
qsRaw: [],
data: {
email_address: '<the-tested-email-address>',
status: 'subscribed',
email_type: 'html',
merge_fields: { FNAME: 'Firstname', LNAME: 'Lastname' }
},
}
Still, the created member doesn't have the language set.
Please help me, it would really be appreciated 🙏
How can I set a new member's language when creating it using MailChimp API?

Dall-E API in Node.js always returns 400 Bad Request

I'm trying to implement OpenAI's Dall-E API in my Next.js project but every reload returns 400 Bad Request.
I'm following the steps exactly as they are in the documentation but I still get this error.
Im using Next.js 13's app directory. This is my page.tsx file below. I'm calling the predict function in an async function.
const { Configuration, OpenAIApi } = require("openai");
async function predict() {
const configuration = new Configuration({
apiKey: process.env.OPEN_AI,
});
const openai = new OpenAIApi(configuration);
const response = await openai.createImage(
{
prompt: "a white siamese cat",
},
{
validateStatus: function (status: number) {
return status < 500;
},
}
);
console.log(response);
}
export default async function Home() {
const response = await predict();
return (
<div className="flex h-full flex-col items-center justify-center px-8">
<h1>Get a picture of a cat</h1>
</div>
);
}
this is the response
status: 400,
statusText: 'BAD REQUEST',
headers: {
date: 'Wed, 11 Jan 2023 10:42:12 GMT',
'content-type': 'application/json',
'content-length': '172',
connection: 'keep-alive',
'access-control-allow-origin': '*',
'openai-version': '2020-10-01',
'openai-organization': 'user-***************',
'x-request-id': '6f3b88d1c538d56b102d76d2f1dc6aee',
'openai-processing-ms': '55',
'strict-transport-security': 'max-age=15724800; includeSubDomains'
},
I found out that my code is fine the issue was that my API Key was under a trial license with no available money to spend! I added my card and now I'm receiving 200.

'An error occurred while trying to authenticate: Failed to validate signature.' While using `oauth-1.0a` and nodejs

I'm getting this odd error in nodejs when I try to send a post request with oauth-1.0a and node.js.
Request:
Headers:
Authorization: OAuth <...>
Accept: 'application/xml',
Content-Type: 'application/xml'
Body:
<account>
<firstName>${first}</firstName>
<lastName>${last}</lastName>
<email>${email}</email>
<urlRedirect>${redirecturl}</urlRedirect>
</account>`
Response:
401
An error occurred while trying to authenticate: Failed to validate signature.
Code:
require('dotenv').config()
const request = require('request')
const OAuth = require('oauth-1.0a')
const crypto = require('crypto')
var first = "real";
var last = "person";
var email = "real#person.com";
var redirecturl = "http://google.com"
const oauth = OAuth({
version: '1.0a',
consumer: {
key: process.env.CONSUMERKEY,
secret: process.env.CONSUMERSECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto
.createHmac('sha1', key)
.update(base_string)
.digest('base64')
},
})
console.log(oauth)
const token = {
key: process.env.KEY,
secret: process.env.SECRET,
}
const request_data = {
url: `https://${process.env.CHURCHCODE}.fellowshiponeapi.com/v1/Accounts`,
method: 'POST',
data: `<account>
<firstName>${first}</firstName>
<lastName>${last}</lastName>
<email>${email}</email>
<urlRedirect>${redirecturl}</urlRedirect>
</account>`
}
const headers = Object.assign( oauth.toHeader(oauth.authorize(request_data, token)), {
Accept: 'application/xml',
'Content-Type': 'application/xml'
});
console.log(headers);
request(
{
url: request_data.url,
method: request_data.method,
headers: headers
},
function(error, response, body) {
console.log(response.statusCode)
console.log(response.statusMessage)
}
)
Why is this error occurring?
Edit: one of the more useful resources I used was this: https://oauth.net/core/1.0/#signing_process but I still can't figure this out.
Should also mention that this request does work, as in postman it successfully goes through.
You typically want to provide the token in a request using this header:
Authorization: Bearer <token>
Not
Authorization: OAuth <...>

SendGrid CORS error from Node.js server application

I'm just trying to get a simple example from the SendGrid docs working. This section of code is called from my Node.js + Express application:
function sendConfirmationEmail() {
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: 'my#email.com', // Change to your recipient
from: 'verifiedsender#gmail.com', // Change to your verified sender
subject: 'Hello, world!',
text: 'Thank you for your interest. We will let you know if we need any more information.',
html: '<strong>Thank you for your interest. We will let you know if we need any more information.</strong>',
};
sgMail
.send(msg)
.then(() => {
console.log('Email sent')
})
.catch((error) => {
console.error(error)
});
}
Seeing this logged:
> { Error: Forbidden
at axios.then.catch.error (node_modules/#sendgrid/client/src/classes/client.js:146:29)
at process._tickCallback (internal/process/next_tick.js:68:7)
code: 403,
message: 'Forbidden',
response:
{ headers:
{ server: 'nginx',
date: 'Sat, 27 Feb 2021 15:58:48 GMT',
'content-type': 'application/json',
'content-length': '281',
connection: 'close',
'access-control-allow-origin': 'https://sendgrid.api-docs.io',
'access-control-allow-methods': 'POST',
'access-control-allow-headers': 'Authorization, Content-Type, On-behalf-of, x-sg-elas-acl',
'access-control-max-age': '600',
'x-no-cors-reason': 'https://sendgrid.com/docs/Classroom/Basics/API/cors.html' },
body: { errors: [Array] } } }
Not sure why this is a CORS error, as it is not happening in the browser. Any ideas?
The suggested workaround from SendGrid is not helpful:
You can create a server-based application, which will protect your API keys from being released to the world. Languages like NodeJS, PHP, Ruby, Python, C#, Go, and Java, and others can be implemented to make calls to the API from the security of a locked down server environment.

how to send twilio sms using node https request

I'm trying to send an sms without the twilio sdk by using the nodejs https module however the twilio post api keeps responding with this error "400, Bad Request", which means I'm probably not crafting the request the right way. I've followed the nodejs docs https example, and also twilio's. I've also tried making curl post request and it works perfectly fine. Where I'm I getting it wrong. Here's my code
// Send an SMS message via Twilio
helpers.sendTwilioSms = (phone, message, callback) => {
// validate parameters
phone =
typeof phone == "string" && phone.trim().length == 10
? phone.trim().length
: false;
message =
typeof message == "string" &&
message.trim().length > 0 &&
message.trim().length <= 1600
? message.trim()
: false;
if (phone && message) {
// Configure the request payload
const payload = {
from: config.twilio.fromPhone,
to: `+234${phone}`,
body: message
};
// stringify payload using querystring module instead of JSON.stringify because the reqeust we'll be sending is not of application/json but 'application/x-www-form-urlencoded' form content-type as specified by Twilio
const stringPayload = querystring.stringify(payload);
// Configure the request details
var requestDetails = {
hostname: "api.twilio.com",
method: "POST",
path: `/2010-04-01/Accounts/${config.twilio.accountSid}/Messages.json`,
auth: `${config.twilio.accountSid}:${config.twilio.authToken}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(stringPayload)
}
};
// Instantiate the request
const req = https.request(requestDetails, res => {
// grab the status of the sent request
const status = res.statusCode;
console.log([
`(sendTwilioSms) making https post request`,
`(sendTwilioSms) response completed: ${res.complete}`,
`(sendTwilioSms) response statusCode: ${res.statusCode}`,
{ "(sendTwilioSms) response headers:": res.headers },
{ "(sendTwilioSms) response body:": res.body }
]);
// callback successfully if the request went through
if (status == 200 || status == 201) {
callback(false);
} else {
callback(500, {
Error: `Status code returned was ${status}: ${res.statusMessage}`
});
}
});
// Alert the user as to a change in their check status
workers.alertUserToStatusChange = newCheckData => {
const message = `Alert: Your check for ${newCheckData.method.toUpperCase()} ${
newCheckData.protocol
}://${newCheckData.url} is currently ${newCheckData.state}`;
helpers.sendTwilioSms(newCheckData.userPhone, message, err => {
if (!err) {
console.log(
"Success: User was aterted to a status change in their check, via sms: ",
msg
);
} else {
console.log(
"Error: Could not send sms alert to user who add a state change in their check"
);
}
});
Here's the Response:
[
'(workers) making check request',
'(workers) check response completed: false',
'(workers) check response statusCode: 200'
]
logging to file succeeded
Check outcome has not changed no alert needed
[
'(sendTwilioSms) making https post request',
'(sendTwilioSms) response completed: false',
'(sendTwilioSms) response statusCode: 400',
{
'(sendTwilioSms) response headers:': {
date: 'Fri, 17 Jan 2020 09:49:39 GMT',
'content-type': 'application/json',
'content-length': '127',
connection: 'close',
'twilio-request-id': 'RQ7ee0b52d100c4ac997222f235e760fb7',
'twilio-request-duration': '0.025',
'access-control-allow-origin': '*',
'access-control-allow-headers': 'Accept, Authorization, Content-Type, If-Match, '
+
'If-Modified-Since, If-None-Match, ' +
'If-Unmodified-Since',
'access-control-allow-methods': 'GET, POST, DELETE, OPTIONS',
'access-control-expose-headers': 'ETag',
'access-control-allow-credentials': 'true',
'x-powered-by': 'AT-5000',
'x-shenanigans': 'none',
'x-home-region': 'us1',
'x-api-domain': 'api.twilio.com',
'strict-transport-security': 'max-age=31536000'
}
},
{ '(sendTwilioSms) response body:': undefined }
]
Error: Could not send sms alert to user who add a state change in their check
Try with something like this:
// authentication
var authenticationHeader = "Basic "
+ new Buffer(config.twilio.accountSid
+ ":"
+ config.twilio.authToken).toString("base64");
// Configure the request details
var requestDetails = {
host: "api.twilio.com",
port: 443,
method: "POST",
path: `/2010-04-01/Accounts/${config.twilio.accountSid}/Messages.json`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": Buffer.byteLength(stringPayload),
"Authorization": authenticationHeader
}
};
and this:
// Instantiate the request
const req = https.request(requestDetails, res => {
// grab the status of the sent request
const status = res.statusCode;
res.setEncoding('utf8');
res.on('data', (chunk) => body += chunk);
res.on('end', () => {
console.log('Successfully processed HTTPS response');
// If we know it's JSON, parse it
if (res.headers['content-type'] === 'application/json') {
body = JSON.parse(body);
}
callback(null, body);
});
// callback successfully if the request went through
if (status == 200 || status == 201) {
callback(false);
} else {
callback(500, {
Error: `Status code returned was ${status}: ${res.statusMessage}`
});
}
});
I hope it works, I have not tested. If it doesn't let me know and I'll try on my side and post a complete tested code.

Resources