The digital portfolio of Omar Mashaal

Founder of Exhibitionist

slack bots

Slack bots

Automating love, money, and power

At the Museum of Old and New Art, we pride ourselves on inclusivity. Which means robots are fully fledged members of our team. They help us in the most important parts of life — #love, #money, and #power.


trip advisor

Our visitors can have quite polarising experiences, and these show up in our Trip Advisor reviews. We created a bot that listens for new reviews to be added, and then sends them to Slack.

X-ray is my scraping library of choice, and I am always itching for any excuse to use it. Below is an example of how we can pass a Trip Advisor page, and return an array of custom review data.

const xRay = require('x-ray')

// custom filters to convert class name to star value
// and sanatise the review markup
const x = xRay({
  filters: {
    stars: value => +value.slice(-2) / 10,
    more: value => {
      if (value.slice(-4) == 'More') value = value.slice(0, -4)
      return sanitiseString(value)

// scrape the most recent 3 pages
// and return a custom array
x(url, '.reviewSelector', [
    location: '.userLoc',
    id: '@data-reviewid',
    title: '.noQuotes',
    rating: '.ui_bubble_rating@class | stars',
    copy: '.partial_entry | more',
    image: '.ui_avatar img.basicImg@data-lazyurl',
    name: '.info_text div:first-of-type',
    link: '.quote a.title@href'
  .then(results => {

This will log an array of reviews, matching the custom object we set above...

  "copy": "Shittest museum Ive ever been to..the architecture, views and location were good, borderline stunning, the staff were quite good as well..the content was confusing, complicated, screwy, unclear, unimpressive..a lot of wasted space and disappointing displays..even with all the macabre innuendo it wasnt even impressive macabre..not...",
  "id": "709854175",
  "image": "",
  "link": "",
  "location": "Tasmania, Australia",
  "name": "MrsJennings6",
  "rating": 1,
  "title": "Mediocre"

From here, we temporarily store the data in Firebase so we can cross reference older reviews to ensure they are not posted twice. Our function is stored in Lambda, which runs every 15 mins.

rip advisor

rip advisor

rip advisor

cat review

rip advisor


mona tessitura

Our booking platform provides real-time, anonymous transactional updates via Slack. This insight offers a more tangible look into our visitors buying patterns, busy periods of the day/week, as well at acting as a bit of a sanity check - to ensure operations are running as expected.

import { WebClient } from '@slack/web-api'
const web = new WebClient(token)

const sendSlack = async ({ location, total }) => {
    channel: 'money',
    text: `${emoji} Someone from ${location} gave us $${total}`

export default sendSlack

slack money bot

slack money love


status bot

A core part of the digital team's remit is ensuring that websites aren't crashing. We created a custom frontend over the top of the open-source watchmen API. This offers us 24/7 monitoring of our internal web projects, as well as one's maintained by third parties.

slack devops bot

Website outages get posted to Slack in real-time using the watchmen slack plugin. So useful!

slack broken bot