Skip to content

Instantly share code, notes, and snippets.

@huan
Last active October 30, 2015 12:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huan/0cbba22b60439f19e860 to your computer and use it in GitHub Desktop.
Save huan/0cbba22b60439f19e860 to your computer and use it in GitHub Desktop.
version 2: use freshdesk api to submit new ticket, instead of forward gmail to support@freshdesk mail box. after done this, we will not be limited by the message bug of gmail (mime type error) and quota from google (send no more then 100 mail per day)
/**
*
* Classify BP mail by MikeBO - zixia 20151025
* This is a Google Apps Script
*
* Author: Zhuohuan LI <zixia@zixia.net>
* https://zixia.freshdesk.com/support/home
*
*/
'use strict'
var LABEL_BP = 'BizPlan'
var LABEL_NOT_BP = 'NotBP'
var LABEL_MIKEBO = 'Mike/MikeBo'
var LABEL_BUGBO = 'Mike/BugBo'
/**
*
* for DEBUG:
* 1. log or not
*
*/
var DEBUG=true
/**************************************************************************
*
* Main
*
*/
function bp2Ticket() {
/**
*
* for DEBUG:
*
*/
var DEBUG_SEARCH_PATTERN=false
//DEBUG_SEARCH_PATTERN=''
if (DEBUG && DEBUG_SEARCH_PATTERN) {
Logger.log('DEBUG_SEARCH_PATTERN: ' + DEBUG_SEARCH_PATTERN)
processSearchPattern('label:inbox ' + DEBUG_SEARCH_PATTERN)
return
}
var NEW_BP_PATTERNS = {
to_zixia_preangel_only: ' newer_than:1d NOT in:trash ' // 最新1天内
+ ' AND (label:inbox NOT label:' + LABEL_BP + ' NOT label:' + LABEL_NOT_BP + ') '
+ ' NOT (label:' + LABEL_BUGBO + ')'
+ ' AND (to:zixia@pre-angel.com NOT to:bp@pre-angel.com) '
+ ' AND ("从QQ邮箱发来的超大附件" OR (filename:pptx OR filename:ppt OR filename:pdf)) '
, to_bp_preangel_only: ' newer_than:1d NOT in:in:trash ' // 最新1天内
+ ' AND (label:inbox NOT label:' + LABEL_BP + ' NOT label:' + LABEL_NOT_BP + ') '
+ ' NOT (label:' + LABEL_BUGBO + ')'
+ ' AND (to:bp@pre-angel.com NOT to:zixia@pre-angel.com) '
+ ' AND (abu OR 阿布 OR bruce OR zixia OR lizh OR lizhuohuan OR zhuohuan OR 卓桓 OR 李卓桓 OR 卓恒 OR 李卓恒 OR 李总 OR 李老师) '
+ ' AND (has:attachment AND (filename:pptx OR filename:ppt OR filename:pdf OR filename:doc)) '
}
/**
*
* Loop BizPlan search patterns
*
*/
var n = 0
for (var name in NEW_BP_PATTERNS) {
DEBUG && Logger.log('process %s ->[ %s ]', name, NEW_BP_PATTERNS[name])
n += processSearchPattern(NEW_BP_PATTERNS[name])
}
DEBUG && Logger.log('process total mail: %s', Math.floor(n))
}
/**
*
* Do search & process
*
*/
function processSearchPattern(pattern) {
// how many emails will be processed by run once.
var NUM_PER_QUERY = 1
if (!pattern) {
Logger.log('Err: need search pattern')
return false
}
// search for mails
var threads = GmailApp.search(pattern, 0, NUM_PER_QUERY)
if (threads.length <= 0) {
DEBUG && Logger.log('No matched mail by search')
return
}
// get gmail labels
var labelBp = GmailApp.getUserLabelByName(LABEL_BP)
var labelNotBp = GmailApp.getUserLabelByName(LABEL_NOT_BP)
var labelMikeBo = GmailApp.getUserLabelByName(LABEL_MIKEBO)
var labelBugBo = GmailApp.getUserLabelByName(LABEL_BUGBO)
var numBp = 0
// find & process bp from mails
for (var i=0; i<threads.length; i++) {
var thread = threads[i]
// All mails had processed by mike tag MikeBo label.
thread.addLabel(labelMikeBo)
// Tag as bug first, remove after confirm success.
thread.addLabel(labelBugBo)
var messages = thread.getMessages()
var isBp = isNewBizPlan(messages)
DEBUG && Logger.log("MikeBo think %s from %s is %s."
, messages[0].getSubject().substring(0,9)
, messages[0].getFrom()
, isBp ? 'BP' : 'NotBP'
)
if (!isBp) {
thread.addLabel(labelNotBp)
continue
}
thread.addLabel(labelBp)
numBp++
try {
addToTicket(thread)
thread.removeLabel(labelBugBo)
} catch (e) {
Logger.log('addToTicket Err:' + e)
}
}
DEBUG && Logger.log('processed %s bps.', numBp)
return threads.length
}
/**************************************************************************
*
*
* Process Ticket - send mail to freshdesk
*
*/
function addToTicket(thread) {
// the first email from entrepreneur, normaly is BP
var message = thread.getMessages()[0]
var from = message.getFrom()
var cc = message.getCc()
var to = message.getTo()
var subject = message.getSubject()
var description = message.getBody()
var attachments = message.getAttachments()
return createFreshdeskTicket(from, to + ',' + cc, subject, description, attachments)
}
function isNewBizPlan(messages) {
/**
*
* 1. no trash message (fresh: had never been touched)
* 2. no other people reply (fresh: only sender self)
*
*/
var from = messages[0].getFrom()
for (var i=0; i<messages.length; i++) {
if (messages[i].isInTrash()) {
return false
}
if (messages[i].getFrom() != from) {
return false
}
}
return true
}
/**
*
* new freshdesk ticket!
*
*/
function createFreshdeskTicket(from, to, subject, description, attachments) {
var API_KEY = PropertiesService.getScriptProperties().getProperty('FreshDeskApiKey')
if (!API_KEY) throw new Error('FreshDeskApiKey not found in script properties.')
var ENDPOINT = Utilities.formatString('https://%s.freshdesk.com', 'zixia')
//Logger.log(to)
//return
var payload = [
['helpdesk_ticket[description_html]', description]
, ['helpdesk_ticket[subject]', subject]
, ['helpdesk_ticket[email]', from]
, ['cc_emails', to]
]
for (var i=0; i<attachments.length; i++) {
payload.push(
[
'helpdesk_ticket[attachments][][resource]'
, attachments[i]
]
)
}
var boundary = '-----CUTHEREelH7faHNSXWNi72OTh08zH29D28Zhr3Rif3oupOaDrj'
payload = makeMultipartBody(payload, boundary)
var headers = {
'Authorization': 'Basic ' + Utilities.base64Encode(API_KEY + ':X')
}
var options = {
contentType: "multipart/form-data; boundary=" + boundary
, headers: headers
, payload: payload
, method: 'post'
, muteHttpExceptions: true
}
var url = ENDPOINT + '/helpdesk/tickets.json'
var response = UrlFetchApp.fetch(url, options)
if (response.getResponseCode() != 200) {
throw new Error('UrlFetchApp: Freshdesk API failed! code: %s, content: %s'
, response.getResponseCode()
, response.getContentText()
)
}
}
/**
*
* helper function
*
*/
function makeMultipartBody(payload, boundary) {
var body = Utilities.newBlob('').getBytes()
// Logger.log(payload)
for (var i in payload) {
var [k, v] = payload[i]
// Logger.log('############ %s = %s', k, v)
// Logger.log('$$$$$$$$ %s', v.toString())
if (v.toString() == 'Blob'
|| v.toString() == 'GmailAttachment'
) {
// attachment
body = body.concat(
Utilities.newBlob(
'--' + boundary + '\r\n'
+ 'Content-Disposition: form-data; name="' + k + '"; filename="' + v.getName() + '"\r\n'
+ 'Content-Type: ' + v.getContentType() + '\r\n\r\n'
).getBytes())
body = body
.concat(v.getBytes())
.concat(Utilities.newBlob('\r\n').getBytes())
} else {
// string
body = body.concat(
Utilities.newBlob(
'--'+boundary+'\r\n'
+ 'Content-Disposition: form-data; name="' + k + '"\r\n\r\n'
+ v + '\r\n'
).getBytes()
)
}
}
// body = body.concat(Utilities.newBlob("\r\n--" + boundary + "--\r\n").getBytes())
body = body.concat(Utilities.newBlob('--' + boundary + "--\r\n").getBytes())
return body
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment