Skip to content
This repository has been archived by the owner on Nov 21, 2020. It is now read-only.

Commit

Permalink
feat(nylus): add nylus gmail integration
Browse files Browse the repository at this point in the history
close #25
  • Loading branch information
munkhorgil authored and batamar committed Oct 10, 2019
1 parent ecac3e9 commit db8b3f7
Show file tree
Hide file tree
Showing 28 changed files with 1,723 additions and 84 deletions.
7 changes: 6 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# RABBITMQ
RABBITMQ_HOST=amqp://localhost
RABBITMQ_HOST=amqp://localhost

# Nylas
NYLAS_CLIENT_ID=
NYLAS_CLIENT_SECRET=
NYLAS_WEBHOOK_CALLBACK_URL=
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"botbuilder": "^4.4.0",
"botbuilder-adapter-facebook": "^1.0.3",
"botkit": "^4.0.2",
"crypto": "^1.0.1",
"debug": "^4.1.1",
"dotenv": "^4.0.0",
"express": "^4.16.4",
Expand All @@ -49,6 +50,8 @@
"meteor-random": "^0.0.3",
"migrate": "^1.6.2",
"mongoose": "^5.2.16",
"nylas": "^4.7.0",
"querystring": "^0.2.0",
"request": "^2.88.0",
"requestify": "^0.2.5"
},
Expand Down
2 changes: 1 addition & 1 deletion src/callpro/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const init = async app => {
path: '/integrations-api',
method: 'POST',
body: {
action: 'create-or-update-customer',
action: 'get-create-update-customer',
payload: JSON.stringify({
integrationId: integration.erxesApiId,
primaryPhone: numberFrom,
Expand Down
1 change: 1 addition & 0 deletions src/debuggers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const debugIntegrations = debug('erxes-integrations:integrations');
export const debugFacebook = debug('erxes-integrations:facebook');
export const debugGmail = debug('erxes-integrations:gmail');
export const debugCallPro = debug('erxes-integrations:callpro');
export const debugNylas = debug('erxes-integrations:nylas');
export const debugExternalRequests = debug('erxes-integrations:external-requests');

export const debugRequest = (debugInstance, req) =>
Expand Down
2 changes: 1 addition & 1 deletion src/facebook/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export const getOrCreateCustomer = async (pageId: string, userId: string, kind:
path: '/integrations-api',
method: 'POST',
body: {
action: 'create-or-update-customer',
action: 'get-create-update-customer',
payload: JSON.stringify({
integrationId: integration.erxesApiId,
firstName: fbUser.first_name || fbUser.name,
Expand Down
2 changes: 1 addition & 1 deletion src/gmail/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ const init = async app => {
return next(new Error('Account not found'));
}

return res.json(account.uid);
return res.json(account.email);
});

app.post('/gmail/send', async (req, res, next) => {
Expand Down
1 change: 1 addition & 0 deletions src/gmail/loginMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const loginMiddleware = async (req, res) => {
await Accounts.create({
name: email,
uid: email,
email,
kind: 'gmail',
token: access_token,
tokenSecret: credentials.refresh_token,
Expand Down
26 changes: 16 additions & 10 deletions src/gmail/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const Customers = model<ICustomerDocument, ICustomerModel>('customers_gma
export interface IConversation {
to: string;
from: string;
threadId: string;
content: string;
customerId: string;
erxesApiId: string;
Expand All @@ -42,6 +43,7 @@ export const conversationSchema = new Schema({
_id: field({ pkey: true }),
to: { type: String, index: true },
from: { type: String, index: true },
threadId: { type: String, index: true },
content: String,
customerId: String,
erxesApiId: String,
Expand All @@ -57,7 +59,7 @@ export const Conversations = model<IConversationDocument, IConversatonModel>('co
export interface IConversationMessage extends IMailParams {
conversationId: string;
erxesApiMessageId: string;
createdAt: string;
createdAt: Date;
}

export interface IConversationMessageDocument extends IConversationMessage, Document {}
Expand All @@ -70,24 +72,28 @@ export const attachmentSchema = new Schema({
attachmentId: String,
});

const emailSchema = {
_id: false,
name: String,
email: String,
};

export const conversationMessageSchema = new Schema({
_id: field({ pkey: true }),
conversationId: String,
erxesApiMessageId: String,
labelIds: [String],
messageId: { type: String, unique: true },
threadId: String,
subject: String,
body: String,
to: String,
cc: String,
bcc: String,
to: [emailSchema],
cc: [emailSchema],
bcc: [emailSchema],
from: [emailSchema],
references: String,
headerId: String,
from: String,
threadId: String,
labelIds: [String],
reply: [String],
messageId: { type: String, unique: true },
textHtml: String,
textPlain: String,
attachments: [attachmentSchema],
createdAt: field({ type: Date, index: true, default: new Date() }),
});
Expand Down
42 changes: 29 additions & 13 deletions src/gmail/receiveEmails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,24 +123,40 @@ const processReceivedEmails = async (

const email = extractEmailFromString(from);

const customer = await createOrGetCustomer(email, integration.erxesApiId, integration._id);
const conversation = await createOrGetConversation(
const integrationIds = {
id: integration._id,
erxesApiId: integration.erxesApiId,
};

// Customer ========
const customer = await createOrGetCustomer(email, integrationIds);

// Conversation =========
const conversationDoc = {
email,
reply,
integration.erxesApiId,
integration._id,
customer.erxesApiId,
subject,
receivedEmail,
);
integrationIds,
reply,
customerErxesApiId: customer.erxesApiId,
};

await createOrGetConversationMessage(
const conversation = await createOrGetConversation(conversationDoc);

// Conversation message ==========
const conversationIds = {
id: conversation._id,
erxesApiId: conversation.erxesApiId,
};

const messageDoc = {
messageId,
conversation.erxesApiId,
customer.erxesApiId,
conversation._id,
updatedMessage,
);
conversationIds,
message: updatedMessage,
customerErxesApiId: customer.erxesApiId,
};

await createOrGetConversationMessage(messageDoc);
});
};

Expand Down
6 changes: 2 additions & 4 deletions src/gmail/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const encodeBase64 = (subject: string) => {
* @see {https://tools.ietf.org/html/rfc2822}
*/
const createMimeMessage = (mailParams: IMailParams): string => {
const { bcc, cc, to, textHtml, headerId, references, from, subject, attachments } = mailParams;
const { bcc, cc, to, body, headerId, references, from, subject, attachments } = mailParams;

const nl = '\n';
const boundary = '__erxes__';
Expand Down Expand Up @@ -43,9 +43,7 @@ const createMimeMessage = (mailParams: IMailParams): string => {

mimeBase.push('Content-Type: multipart/mixed; boundary=' + boundary + nl);
mimeBase.push(
['--' + boundary, 'Content-Type: text/html; charset=UTF-8', 'Content-Transfer-Encoding: 8bit' + nl, textHtml].join(
nl,
),
['--' + boundary, 'Content-Type: text/html; charset=UTF-8', 'Content-Transfer-Encoding: 8bit' + nl, body].join(nl),
);

if (attachments && attachments.length > 0) {
Expand Down
93 changes: 59 additions & 34 deletions src/gmail/store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { fetchMainApi } from '../utils';
import { ConversationMessages, Conversations, Customers } from './models';
import { extractEmailFromString } from './util';
import { buildEmail } from './util';

interface IIntegrationIds {
id: string;
erxesApiId: string;
}

const createOrGetCustomer = async (email: string, integrationIds: IIntegrationIds) => {
const { erxesApiId, id } = integrationIds;

const createOrGetCustomer = async (email: string, integrationIdErxesApiId: string, integrationId: string) => {
let customer = await Customers.findOne({ email });

if (!customer) {
Expand All @@ -11,7 +18,7 @@ const createOrGetCustomer = async (email: string, integrationIdErxesApiId: strin
email,
firstName: '',
lastName: '',
integrationId,
integrationId: id,
});
} catch (e) {
throw new Error(e.message.includes('duplicate') ? `Concurrent request: customer duplication` : e);
Expand All @@ -22,13 +29,13 @@ const createOrGetCustomer = async (email: string, integrationIdErxesApiId: strin
path: '/integrations-api',
method: 'POST',
body: {
action: 'create-or-update-customer',
action: 'get-create-update-customer',
payload: JSON.stringify({
emails: [email],
firstName: '',
lastName: '',
primaryEmail: email,
integrationId: integrationIdErxesApiId,
integrationId: erxesApiId,
}),
},
});
Expand All @@ -44,15 +51,17 @@ const createOrGetCustomer = async (email: string, integrationIdErxesApiId: strin
return customer;
};

const createOrGetConversation = async (
email: string,
reply: string[],
integrationIdErxesApiId: string,
integrationId: string,
customerId: string,
subject: string,
receivedEmail: string,
) => {
const createOrGetConversation = async (args: {
email: string;
subject: string;
reply: string[];
receivedEmail: string;
integrationIds: IIntegrationIds;
customerErxesApiId: string;
}) => {
const { subject, reply, email, integrationIds, receivedEmail, customerErxesApiId } = args;
const { id, erxesApiId } = integrationIds;

let conversation;

if (reply) {
Expand All @@ -72,7 +81,7 @@ const createOrGetConversation = async (
conversation = await Conversations.create({
to: receivedEmail,
from: email,
integrationId,
integrationId: id,
});
} catch (e) {
throw new Error(e.message.includes('duplicate') ? 'Concurrent request: conversation duplication' : e);
Expand All @@ -86,8 +95,8 @@ const createOrGetConversation = async (
body: {
action: 'create-or-update-conversation',
payload: JSON.stringify({
customerId,
integrationId: integrationIdErxesApiId,
customerId: customerErxesApiId,
integrationId: erxesApiId,
content: subject,
}),
},
Expand All @@ -104,24 +113,40 @@ const createOrGetConversation = async (
return conversation;
};

const createOrGetConversationMessage = async (
messageId: string,
conversationErxesApiId: string,
customerErxesApiId: string,
conversationId: string,
data: any,
) => {
const createOrGetConversationMessage = async (args: {
messageId: string;
message: any;
customerErxesApiId: string;
conversationIds: {
id: string;
erxesApiId: string;
};
}) => {
const { messageId, message, customerErxesApiId, conversationIds } = args;
const { id, erxesApiId } = conversationIds;

const conversationMessage = await ConversationMessages.findOne({ messageId });

if (!conversationMessage) {
data.from = extractEmailFromString(data.from);
data.to = extractEmailFromString(data.to);
const doc = {
conversationId: id,
messageId,
threadId: message.threadId,
headerId: message.headerId,
labelIds: message.labelIds,
reference: message.reference,
to: buildEmail(message.to),
from: buildEmail(message.from),
cc: buildEmail(message.cc),
bcc: buildEmail(message.bcc),
subject: message.subject,
body: message.textHtml,
reply: message.reply,
attachments: message.attachments,
customerId: customerErxesApiId,
};

const newConversationMessage = await ConversationMessages.create({
conversationId,
customerId: customerErxesApiId,
...data,
});
if (!conversationMessage) {
const newConversationMessage = await ConversationMessages.create(doc);

try {
const apiMessageResponse = await fetchMainApi({
Expand All @@ -131,9 +156,9 @@ const createOrGetConversationMessage = async (
action: 'create-conversation-message',
metaInfo: 'replaceContent',
payload: JSON.stringify({
conversationId: conversationErxesApiId,
conversationId: erxesApiId,
customerId: customerErxesApiId,
content: data.subject,
content: doc.subject,
}),
},
});
Expand Down
Loading

0 comments on commit db8b3f7

Please sign in to comment.