the Slack bot that finds you a spot!
/parkingspots | /parkme |
---|---|
![]() ![]() |
![]() ![]() |
Ever needed a parking spot and wondered if your co-workers had any available? Did you get the monthly parking pass at work and wish you could make some extra cash when you weren't using your spot?
Meet parkMeBot and consider your parking problem handled!
This Slack API integration lets you find an open parking spot at work (or someone to rent your spot) on the date of your choice through our matching algorithm. Those days of fruitlessly asking around the office one person at a time are gone!
Just enter your respective slash command (/parkme to find a spot or /rentspot to put yours up for rent) and parkMeBot will let you know when its found you a match.
If you'd rather not wait, you can also get a CSV file full of parking spot requests or offers in the parking pool through our convenient /parkingspots command.
-
/parkingspots command triggers a workflow to get a CSV-formatted report of all parking spot requests or offers currently in the order pool uploaded to the #parking channel.
-
/parkme command triggers request-submitting user flow and lets you know instantly if a matching offer is found, or later when one becomes available.
-
/rentspot, the previous command's complement, launches the offer-submitting flow and also notifies you immediately if there's a match or later on when we receive a matching request.
parkMeBot posts messages to channel (when invoked by the /parkingspots slash command) with an interactive component containing two buttons to choose your report type from either requests or offers.
Also uses interactive select dropdown menu in response message to /parkme and /rentspot slash commands, so user can pick from range of dates to submit their respoective parking spot bid or offer.
We created a bot user so we could encapsulate our parking solution in one workspace entity capable of multiple functions and provides users a single access point for their parking needs. This creates a better user experience by personifying our application in a polished client-facing A.I.
-
Designed with User Experience first
Our workspace bot attempts to solve a real-life problem that plagues our car-centric culture, especially in the local SF Bay area where parking is in high demand and prices are even higher.
Bot user-flow interaction is meant to be as frictionless as possible, minimizing the number of steps needed to submit an order for your parking needs and providing user-friendly confirmations when a user has taken an action interacting with our bot while making asynchronous calls in the background. It also updates a user when it has finished a task and uploads a file to a channel for easy centralized access within the Slack application.
Slash commands seemed like the clear way to go in this case since they can be invoked from any channel without needing the bot to be a member and listening in on that channel. This option also fit in well with our goal of designing and implementing a ubiquitous parking solution that can serve all the members of the workspace from anywhere in the Slack application.
-
Persisting orders
The parkMeBot aims to be the ongoing one-stop solution for all your parking spot-trading needs at work. As such, saving orders in a database for later querying and retrieval became necessary. This decision is key as we built with scale in mind. There might not be any matching orders at any given time when a user submits an order so the need for persistence became clear in order to better serve the workspace's members over multiple interactions.
Using a NoSQL database became the clear choice over SQL in this context because we don't foresee needing table joins since we're only storing order objects. It also anticipates scaling with a database solution that has sharding built in.
Reliability was definitely a concern and while SQL would win here, we traded that for faster performance and the use of a cost-saving cloud-based storage solution in MongoLab.
The task of QA'ing this application will mostly be manual. I developed locally using localtunnel to be able to receive commands and actions to the Node server running on my local machine and tested the functionality of the code one piece at a time in isolation for typical unit testing before moving on to full integration testing.
My testing and development made heavy use of the logging functions made available by the Tracer module in order to know what was happening at every part of the code. I also made routine use of passing error-handling callbacks to my asynchronous calls that would typically log errors first before any other logic.
If i'm not able to access the source code while testing, as might be the case when testing someone else's production code, I would rely on manual testing through the UI. In my testing, I would intentionally try to break the application by trying all kinds of input and going through the flow repeatedly while trying every possible selection and clicking every possible interactive component in order to see the limits of the application.
For me, it's important to go through every possible unique user flow from end to end in order to fully test anything. This is key because any flow you don't vet will certainly be discovered by a user, given enough time running on production.
With that in mind, i've outlined the key assertions I would make while testing this application:
-
User flows
-
when user invokes bid/offer submitting flow in a channel, it should:
- receive POST request to /command subendpoint
- respond with POST to same channel with:
- a friendly message
- one interactive select component with list of date options
- receive POST request to /actions endpoint with all necessary data
- respond to user action with confirmation message
- query database for matching order and return in confirmation message
- asynchronously insert_ order into database
-
when user invokes parking spot report request flow in channel, it should:
- receive POST request to /command subendpoint
- respond with POST to same channel with:
- a friendly message
- one interactive select component with two buttons for order type selection
- receive POST request to /actions endpoint with all necessary data (including report TYPE value)
- respond to user action with confirmation message
- asynchronously generate CSV correct report of orders (requests or offers) and save file locally
- asynchronously upload CSV file to specified upload channel
- notify user in channel report was requested when report has finished uploading
-
-
Database
-
when inserting an order, it should:
- require a userId value
- require a date value
- require a direction value
- persist order in database (able to retrieve)
-
when querying the order pool, it should:
- search by date first
- match only opposite direction of incoming order
- return only one match or empty array if none found
- if no match, should not remove any orders from order pool
- if matched, remove BOTH orders from order pool
- both matched orders are no longer retrievable from database
- update ALL master and slave databases (in case of db redundancy)
-
-
Better flow for user experience
Improving the direct messaging functionality following a successful match after invoking /parkme or /rentspot command is the first thing I would work on if I had more time for this project. It makes for a great user experience to get a private message or invited to a private channel with just the other user you were matched with. The backend side of the application successfully persists orders and finds correct matches in the database, but unfortunately the user-facing functionality for this finishing touch in UX is not where I would ideally like it to be.
-
Using local time vs UTC
This was an issue I wrestled with in building this application and found it hard to justify using UTC time when it could easily interfere with generating list of days for a local user to pick from to submit a request/offer on. If a user on PST time submitted a bid/offer after 5pm, the calendar day generating algorithm would skip a day in its process of generating the next 7 days to pick from.
As the bot application scales, this range of days would be expanded out to months and involve a more complex interactive calendar component that doesn't currently exist. If I had more time to work on this application, this would be the first thing I would build in order to give my users a wider array of choices.
-
Indexing & Searching
As of right now, we are only querying the database when a new order comes in. This minimizes our searches to only when the database state changes. We could stand to improve our matching algorithm however, if we were dealing with large amounts of data.
The matching algorithm currently searches by date field in the database, which is a string comprised of day and date for human readability. Given the current state of the app having only a one week range for its time window, this algorithm is simple and effective. Searching for the date makes it easy to find an order's complement since only orders on that date can match with our incoming order at the time of query and there would be a much smaller pool of orders on any given date.
That leaves us with just having to match the opposite direction of the incoming order in order from among the pool of date-matched orders. We could add indexing on this field in order to make it faster to search by date, and would be the first improvement I would make on the matching algorithm and database querying.
I have also included an id field in our model that is composed of the desired date of the order as an ISO string and UNIX timestamp of when order was submitted. This was forward thinking on how to deal with the issue of multiple matching orders in database. I would complete this implementation by simply resolving using timestamp, implementing a first-in-first-out (FIFO) policy. This field could also be a possible indexing candidate
node 8.9.4
npm 6.2.0
From the command terminal, clone the repository to your local directory...
$ git clone https://www.gihub.com/izzydoesit/parkMeBot.git
$ cd parkMeBot
Then run npm commands to install all dependencies and run the development server.
$ npm install
$ npm run dev
- Babel/ES6 - JavaScript ES6 compiler
- Body Parser - Node.js request body parsing middleware
- Botkit - bot making toolkit
- Config - application configuration
- CSV Write Stream - CSV encoder stream
- ESLint - JavaScript Linter (configured w/ AirBnB style guide)
- Express - web application framework
- Mkdirp - directory creating tool
- Mongoose - database framework
- Morgan - HTTP request logger middleware
- Request - HTTP request client
- Tracer - logging library
- MongoLab hosted database
This project is licensed under the Apache 2.0 License - see LICENSE.md file for details