Last active
October 30, 2015 12:40
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* Classify BP mail by MikeBO - zixia 20151025 | |
* This is a Google Apps Script | |
* | |
* Author: Zhuohuan LI <[email protected]> | |
* 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:[email protected] NOT to:[email protected]) ' | |
+ ' 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:[email protected] NOT to:[email protected]) ' | |
+ ' 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