Introducing TwiLinks – a Chat app built using Apps Script

Preview tweets and user data when sharing links, along with the ability to follow a specific user or like a particular tweet.

Introducing TwiLinks – a Chat app built using Apps Script
TwiLinks — Unfurl Twitter links in Google Chat

Backstory

My itch to explore the new and improved Chat apps came after the latest push from the Google Workspace team showcasing the amazing bots apps and use cases they were able to build — more specifically, I was interested in link previewing

I started de/re-constructing the GitHub app and the more I dug deep, the more I was curious about getting multiple users engage with the same card components but while using their own credentials. Here's what I was thinking from a use case POV —

Single space. Multiple users. Say, if User1 were to share a link to a specific repo (which they'd already starred), it would unfurl allowing the rest of the users to star it (using their own GitHub account). Now, if User2 were to click on the Star button — what is it that User1, User2 and the rest of the users get to see?

This (weird) obsession took me to multiple places — at times, questioning if something was a bug or a feature, my own sanity, documentation et. al 😅

Finally wrapped my head around many of these concepts, much of which were already documented (I just needed to piece them together) and that's when I switched to building similar actions but for Twitter (ex: liking a tweet, following a user) — all within the realm of link unfurling!

Access

Follow the instructions laid out here and look for TwiLinks when finding apps in Google Chat.

You can either DM the app or even add it to a space directly.

Slash commands should help you get started easily —

Slash commands

Prerequisites

If you intend to build your own Chat app using Apps Script and use Twitter, here are some things that you'd absolutely need —

  1. Paid, Google Workspace account — hopefully, soon, even (normal, free) Gmail accounts would also be able to build these puppies 🤞🏾
  2. Twitter developer account
  3. Using OAuth2 for Apps Script library (must) > Twitter.gs (nice to have)
  4. Creating a Chat app using Google Apps Script (nice to have)

I made a choice to build the entire Chat app within the Workspace ecosystem and hence settled with Google Apps Script; however, you also have the options to build Chat apps externally by means of different endpoints (HTTPS, Google Cloud Pub/Sub, DialogFlow) too.

Architecture

I broke my Apps Script project into 4 segments —

  1. App.gs — this handles all the event related functions viz. onMessage, onAddToSpace etc.
  2. Cards.gs — helps create cards for all the different scenarios.
  3. Code.gs — handles all the config requests, liking a tweet or following a user using the Twitter API.
  4. Twitter.gs — takes care of the OAuth 2.0 approach for authorizing the app via a user's credentials.

App.gs

  1. onMessage — this function captures events that also provide with identifiers to recognise either a Slash commandevent.message.slashCommand — or if there are any URLs that match the ones which the app can help preview — event.message.matchedUrl
  2. onCardClick — I've intentionally blocked this function using getService().hasAccess() to first check if the user taking the action already has connected their Twitter account or otherwise. In all honesty, this isn't handled very well within the Chat ecosystem, as the user who's not authorized, would instead get to see an error in Red at the bottom of the card indicating that the bot wasn't able to process their request ☹️ maybe there's a better way to handle this but I for one couldn't find it
  3. onAddToSpace & onRemoveFromSpace — these functions really don't do as much except for sharing an intro/outro message

Cards.gs

The fun thing about building cards is that it's fully driven as part of a JSON structure and so, you could contextually modify them on the fly.

As an example: if a user is trying to view their own profile by DMing the Chat app, then there's no need to show them a follow button in the card; however, if they do so on a space or a group that has more than just the user in it, then that button should show-up.

Would also highly recommend reading through when to respond to an incoming event/message with either a new card or update the existing card — something you can configure in a card as part of actionResponse type.

Codebase

You can access the entire script from my GitHub repository here.

My priced possession would be that single RegEx statement which identifies if a URL is either a tweet or a profile 🤓

function checkMatchedUrl(url) {
  const urlPattern = new RegExp(/^https:\/\/twitter\.com\/([A-Za-z0-9_]{1,15})(?:\/status\/([0-9]{19}))?$/);
  const [matchedUrl, username, tweetId] = urlPattern.test(url) ? urlPattern.exec(url) : new Array(3).fill(null);
  const entity = !matchedUrl ? "UNDEFINED" : (matchedUrl.length > 40 ? "TWEET" : "USER");
  return {
    entity: entity,
    username: username,
    tweetId: tweetId
  }
}

Preview

Oopsie

While this chat app was published on the marketplace on 12-Jul-2022, I was quickly made aware / recommended to check with Twitter team on the usage of their logo for this app — should've done it before but it didn't occur to me just how important this step too is 😅

Nonetheless, I wrote to trademarks@twitter.com the very next day but besides an automated response indicating that someone from their brand team would get back to me in approximately 7-14 days, there hasn't been any news on that front yet (as on 1-Aug-2022).

Update: Twitter team reached out to me on 16-Aug-2022 and it was clear to me that I couldn't use their logo or "Twitter" / "Twitter for Google Chat" and so I rebranded the chat app to TwiLinks.