Integrate incoming Twilio SMS to Intercom

I use Twilio often to send automatic text messages, in this case, I was using it to send to clients with a reminder text message for their upcoming webinar. Originally I was having the text message replies give an automatic bounce back and then send me an email with the text message body. The problem with that is I wasn’t able to easily reply to these text messages. Since we already use Intercom chat on our websites I wanted to find a way to integrate replies on the SMS’s to our Intercom chat to allow us to easily respond. To do so I found a repo of someone that came up with a proof of concept how this could be done. The code had been created in 2015 so it no longer worked and lacked some functionality.

It was coded in Ruby in which I have never used until today and it was a good reason to learn it. I started with first modifying the code just to work. As it was written any incoming message would fire a webhook in Twilio that would call the Ruby app and create a new user and conversation in Intercom. When the admin replies the reply would go into a text message back to the user.

What I found orignally was each reply from the user on SMS would create a whole new thread in Intercom which made it really hard to follow. I got around this by first looking for a conversation from the user and if it existed then it would reply to that conversation instead of creating a new one. Another issue I noticed is it would send a SMS to ever user in intercom as long as they had a phone number defined. To get around this I tagged every new user with a SMS tag and that way when we responded we would only send a SMS to a user with that Tag.

Take a look at my code edits in the fork here https://github.com/coreyjansen/intercom-twilio-demo

Installation:

I chose to run this ruby code on a Google Virtual Machine and used a ngrok tunnel to allow it to be accessible from the web over https

Twilio Webhook Setup:

Add your webhook into the Twilio number you want to use under the messaging tab. You can see mine in the image above

Intercom Webhook Setup:

  1. Navigate to the Intercom Developers Hub https://developers.intercom.com/
  2. Create a new Intercom App
  3. Assign your Intercom App to your workspace
  4. Grab your token from the app attached to your workplace and save it, you will need this
  5. Create a webhook like the following

Feel free to critique my code and fork it and make any changes, as I mentioned this is the first time I have used Ruby so I am sure there is alot of best practices that were not followed and I would love to learn how I can improve this!

Continue Reading

Change Template Tracking URL in Google Ads To Include Campaign Name, Medium, MatchType, Keyword, Ad Group… Automatically Through Google Script

Use the following script to change all of your Google Ads Tracking Template to include all the Campaign Name, Ad Group, Medium, MatchType, Keyword Used automatically using Google Script. 

I used this script originally when I was using CallRail and wanted to automatically pull in the Keyword, Campaign Name into the CallRail Report. Instead of sitting there and changing each ad Final URL I used this script. 

function  main()
{
  //{CampaignName} ---> Campaignlevel Template
  //{AdGroupName} ----> AdgroupLevel Template
 

 
// Enter your template with at least one of {CampaignName} or {AdGroupName}
  var TrackingTemplate="{lpurl}?matchtype={matchtype}&network={network}&device={device}&adposition={adposition}&keyword={keyword}&utm_source=google&utm_medium=cpc&utm_adgroup={AdGroupName}&utm_campaign={CampaignName}";  //Example
  
var _CAMPAIGN_CONTAINS="";               //Filter by Campaign name   
var _ADGROUP_CONTAINS="";               //Filter by Adgroup name 
var STATUS="ENABLED";                    //ENABLED, PAUSED


// Hit Preview to see the changes/logs. 


//////////////////////////////////////////////  
if(TrackingTemplate.search("{CampaignName}")>0&&TrackingTemplate.search("{AdGroupName}")==-1)
{
var TempSplit=TrackingTemplate.split("&");
for(var i in TempSplit)
{
 if(TempSplit[i].split("=").indexOf("{CampaignName}")>0)
  {var No=i;
   break;
  }
 }
var Temp=TempSplit[No].split("=");

 var campaignIterator=_CAMPAIGN_CONTAINS==""?AdWordsApp.campaigns().withCondition("Status = "+STATUS).get():AdWordsApp.campaigns().withCondition("Name contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Status = "+STATUS).get();

if(!campaignIterator.hasNext()){Logger.log("No Campaigns matched with this condition")}
while(campaignIterator.hasNext())
  {
  
  var campaign=campaignIterator.next();
  Temp[1]=campaign.getName(); 
  TempSplit.splice(No,1,Temp.join("="));
  var campaigntemplate=TempSplit.join("&");;
  campaign.urls().setTrackingTemplate(campaigntemplate);
  }
  
}  

if(TrackingTemplate.search("{AdGroupName}")>0)
{
var CampaignCondition=false;  
var TempSplit=TrackingTemplate.split("&");
for(var i in TempSplit)
{
 if(TempSplit[i].split("=").indexOf("{AdGroupName}")>0)
  {var No=i;}
  if(TempSplit[i].split("=").indexOf("{CampaignName}")>0)
  {var Cn=i;CampaignCondition=true;}
}
var Temp=TempSplit[No].split("=");
  
if(_ADGROUP_CONTAINS==""&&_CAMPAIGN_CONTAINS=="")
{var adgroupIterator=AdWordsApp.adGroups().withCondition("Status = "+STATUS).get();}
  else if(_ADGROUP_CONTAINS==""&&_CAMPAIGN_CONTAINS!=="")
var adgroupIterator=AdWordsApp.adGroups().withCondition("Name contains '"+_ADGROUP_CONTAINS+"'").withCondition("Status = "+STATUS).get();
 else if(_ADGROUP_CONTAINS!==""&&_CAMPAIGN_CONTAINS!=="")
var adgroupIterator=AdWordsApp.adGroups().withCondition("CampaignName contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Name contains '"+_ADGROUP_CONTAINS+"'").withCondition("Status = "+STATUS).get();  
  var adgroupIterator=AdWordsApp.adGroups().withCondition("CampaignName contains '"+_CAMPAIGN_CONTAINS+"'").withCondition("Name contains '"+_ADGROUP_CONTAINS+"'").withCondition("Status = "+STATUS).get();
  if(!adgroupIterator.hasNext()){Logger.log("No Campaigns/Adgroups matched with this condition")}
  if(CampaignCondition==false){
  while(adgroupIterator.hasNext())
  {   
  var adgroup=adgroupIterator.next();
  Temp[1]=adgroup.getName();
  TempSplit.splice(No,1,Temp.join("="));
  var adgrouptemplate=TempSplit.join("&");
    adgroup.urls().setTrackingTemplate(adgrouptemplate);
  }
  } else{
    var TempCamp=TempSplit[Cn].split("=");
    while(adgroupIterator.hasNext())
  {   
  var adgroup=adgroupIterator.next();
  Temp[1]=encodeURIC(adgroup.getName());
  TempCamp[1]=encodeURIC(adgroup.getCampaign().getName()); 
  TempSplit.splice(No,1,Temp.join("="));
  TempSplit.splice(Cn,1,TempCamp.join("="));  
  var adgrouptemplate=TempSplit.join("&");
    adgroup.urls().setTrackingTemplate(adgrouptemplate);
  }
  }
} else {Logger.log("Enter at least one of the {CampaignName} or {AdGroupName}")}    

}    


function encodeURIC( r ) {
return r.replace(/\W+/g, "");
}
Continue Reading