Giter Club home page Giter Club logo

sparkles's Introduction

✨ Sparkles ✨

Sparkles was a simple little Slack application for recognizing your teammates and friends in the form of meaningless internet points sparkles. Unfortunately, this original version was a bit heavy in terms of the permissions required from Slack and I never found the time to rework its permissions or how data is stored, nor did I have a lot of incentive to do so (very few people installed the application, and my own workplace only had it installed for a short period of time).

I've left the code here as an archived repository so that folks can take a look, but the application is no longer available to be installed via the Slack app marketplace. I may bring Sparkles back when Slack opens up their new app development platform, but this particular Rails implementation is unlikely to return.

Usage

Tip: You can always view usage information in Slack using /sparkle help

Give a sparkle to somebody

To sparkle someone, use /sparkle immediately followed by a username and, optionally, a reason:

/sparkle @davidcelis [reason]

Reasons don't require any particular format, but if you want them to flow better when reading them later, it's best to start any reason with a coordinating conjunction (that's just a fancy way to say words like "for", "because", or "so").

/sparkle @davidcelis for being so awesome

View your team's leaderboard

Once Sparkles has been installed to your slack workspace, any member of your team can go to sparkles.lol to sign into the web frontend and view the team leaderboard!

You can also view the leaderboard in Slack using the /sparkle command. Some Slack teams get pretty big, though, so leaderboards viewed this way are kept to the top ten sparklers.

/sparkle stats

View your sparkles

When looking at the team leaderboard on sparkles.lol, you can click anybody's username to see a complete history of their sparkles. This includes all kinds of handy information! You can see who gave each sparkle, what their reasons were, the Slack channel where it happened, and even a handy permalink back to the original message in Slack where Sparklebot worked its magic.

Like with the full leaderboard, you can also see anybody's ten most recent sparkles right within slack:

/sparkle stats @davidcelis

Adjust your experience with Sparkles

There are a couple options you can configure when using Sparkles to personalize your experience:

/sparkle settings

Running this command will open a modal with the settings you can adjust. As a regular teammate, you can opt out of leaderboard functionality, hiding point totals when using /sparkle or the web front-end. If you think the leaderboard takes away from the fun of Sparkles and would rather have a more zen experience, turn it off!

If you're an admin for your Slack team, you have a couple additional options:

  • You can disable the leaderboard team-wide. You can still view stats on an individual basis and see the usual detail per-sparkle, but the leaderboard will become a simple team directory and point totals won't be shown anywhere.
  • You can select a public channel to act as your Sparkle feed! Any sparkles given in public channels will be shared to this channel so that nobody needs to feel out of the Sparkle loop.

Self-hosting

Both the slack application and sparkles.lol are free to use! Except for the reasons given for sparkles, we never store messages from Slack itself. However, maybe you work for an organization with strict rules about what data is allowed into or out of Slack. If that's the case, Sparkles is easy to host yourself! In fact, with only a little bit of configuration, you can host Sparkles on Heroku or with Dokku.

Requirements

Sparkles is a Rails application using the following dependencies:

  • Ruby 3.0.3 (and Bundler)
  • Node v12.16.2 (and Yarn)
  • PostgreSQL 13.4
  • Redis 6.2.6

At least, these are the versions in use upon writing this README. Depending on the dependency, they may be updated randomly; the publicly available Sparkles application is hosted via Dokku, so dependency management is less of a concern. For this reason, I highly recommend hosting via a service like Heroku or Dokku so that you can take advantage of buildpacks.

Create a Slack application

Slack recently made it easy to create a preconfigured Slack application using App Manifests. Visit https://api.slack.com/apps?new_app=1 and choose the option to create an app from an app manifest. Choose a development workspace (it's best to choose one that you control so that you can install Sparkles yourself without requiring review from a workspace admin) and paste in the contents of our example app_manifest.yml file, making sure to replace example.com in any URL with the domain where you're planning to host Sparkles.

Generate an encrypted credentials file

Newer Rails applications use encrypted YAML files to store application secrets. You'll need to generate one with your own credentials:

$ bin/rails credentials:edit

This will create two new files: config/credentials.yml.enc and master.key. Keep the former in version control and keep the latter excluded. Our standard .gitignore file handles this for you.

When you run the above command, you should find yourself in a text editor for the config/credentials.yml.enc file; paste the following in, adding adding the required values:

# Found on your Slack application's "Basic Information" page
slack:
  client_id: # or set a SLACK_CLIENT_ID environment variable
  client_secret: # or set a SLACK_CLIENT_SECRET environment variable
  signing_secret: # or set a SLACK_SIGNING_SECRET environment variable

# An ingestion URL for error handling using Sentry. This isn't required, but
# Sentry has a generous free tier, so you may as well sign up and set up a
# quick project at http://sentry.io/signup/
#
# Example: https://[email protected]/1234567
sentry_dsn: # optional, a SENTRY_DSN environment variable works as well

# Used as the base secret for all MessageVerifiers in Rails, including the one
# protecting cookies. Generate one by running `rails secret`.
secret_key_base: # required

Deploy the Rails application

You can deploy Sparkles however you like; the only two required processes are the Rails server (Puma, by defualt) to handle web requests, and Sidekiq to handle background job processing. Sparkles includes a Procfile to declare these processes, so deployment to a service like Heroku or Dokku is easy. However you deploy, you'll need to define three environment variables:

  • DATABASE_URL: A connection string for a PostgreSQL database. This is defined automatically for you if you're using something like Heroku or Dokku and add the PostgreSQL plugin/resource.
  • REDIS_URL: A connection string for a Redis database. This is defined automatically for you if you're using something like Heroku or Dokku and add the Redis plugin/resource.
  • RAILS_MASTER_KEY: The contents of the master.key file you generated earlier.
  • SLACK_CLIENT_ID (optional): The Client ID of your Slack application if you'd rather provide it in the environment instead of the credentials.yml.enc file
  • SLACK_CLIENT_SECRET (optional): The Client Secret of your Slack application if you'd rather provide it in the environment instead of the credentials.yml.enc file
  • SLACK_SIGNING_SECRET (optional): The Signing Secret of your Slack application if you'd rather provide it in the environment instead of the credentials.yml.enc file

Development View performance data on Skylight

Encountered a bug? Have an idea for something that Sparkles doesn't do yet? Want to help make the app faster? Feel free to file an issue. Or, if you're a developer yourself, fork the repository, make some changes, and open a Pull Request!

Acknowledgements

Sparles is inspired by an old Hubot script that was very popular at GitHub and gave us a fun, silly way to show appreciation to fellow hubbers. Everybody loved sparkles except @scottjg and @dreww.

sparkles's People

Contributors

davidcelis avatar nearbycoder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sparkles's Issues

Allow teams to disable the leaderboard

Point bots like Sparkles can sometimes result in a popularity contest, and that's no fun. It's also possible for people to join a team that has used Sparkles for a while and feel like they'll never catch up. Sparkles should be lighthearted and fun, and for some teams, that's going to mean not keeping track of point totals. We should allow people to disable the leaderboard features for Sparkles, which would change a few key functionalities:

  • In Slack, /sparkle @user [reason] would stop printing out @user's total number of sparkles
  • In Slack, /sparkle stats without a username would no longer show the top 10 leaderboard, and would instead default to showing the caller their own sparkles (e.g. me running /sparkle stats would just be equivalent to /sparkle stats @davidcelis)
  • On the web, the Leaderboard index page would become a simple directory and not show point totals

I think the easiest way to do this may be to have a command like /sparkle settings and, maybe eventually, a simple settings page on the web-frontend.

One open question I want to consider before implementing anything is whether this should be a setting we give to individual users to tailor their own experience, or if this should be a setting we give to workspace admins to disable or enable the leaderboard for the whole team. It would probably also be possible to do both, allowing users to change their own experience in a way that removes the potential for competition, or allow the workspace owners to disable it whole-sale.

Allow people to give sparkles by using emoji reactions

People should be able to give someone a quick sparkle by leaving an emoji reaction on one of their messages. If someone reacts to a message with the ❇️ emoji, we'll get a reaction_added event with this payload:

{
  "type": "reaction_added",
  "user": "// Sparkler ID",
  "reaction": "sparkle",
  "item_user": "// Sparklee ID",
  "item": {
    "type": "message",
    "channel": "// Channel ID",
    "ts": "// Message ID"
  },
  "event_ts": "1360782804.083113"
}

We'll need to be able to keep track of whether or not a Sparkle was the result of a reaction. We should add two new attribute to the Sparkle model:

# Stores the `ts` of the message that someone reacted to, creating this sparkle
add_column :sparkles, :reaction_ts, :string

# Stores the `ts` of the message Sparklebot posted to confirm the sparkle
add_column :sparkles, :confirmation_ts, :string

class Sparkle
  def reaction?
    reaction_ts.present?
  end
end

Flow

We get one ❇️ emoji reaction on a message:

  1. We receive a reaction_added event
  2. We call conversations.history?channel={item.channel}&latest={item.ts}&limit=1
  3. We check for the presence of a thread_ts in the response (thread_ts = response.body["messages"].first["thread_ts"] || item.ts)
  4. Pass item.ts to chat.getPermalink so that our Sparkle's permalink will be the message that got a reaction
  5. Create a Sparkle with reaction_ts set to item.ts
  6. Use the thread_ts we kept a reference to so that we can reply with confirmation from within a new or existing thread, keeping another reference to the ts of the resulting reply. Example:
Aw yeah! <@slack_sparklee_id> got a sparkle for their <message|permalink> from <@slack_sparkler_id>. They've got N sparkles now :sparkles:
  1. Update the Sparkle we created, setting reaction_confirmation_ts to the ts of our confirmation response

For each follow-up ❇️ emoji reaction:

  1. We receive a reaction_added event
  2. Check Sparkle.where(slack_team_id:, slack_channel_id:, slack_sparkler_id:, reaction_ts:)
  3. Simply duplicate an existing sparkle and set its slack_sparkler_id accordingly
  4. Use our confirmation_ts with the chat.update method so that we can update our response:
Aw yeah! <@slack_sparklee_id> got 2 sparkles for their <message|permalink> from <@slack_sparkler_id> and <@slack_sparkler_id>. They've got N sparkles now :sparkles:

Add option to attach feedback as a message attachment instead of additional channel messages

Is your feature request related to a problem? Please describe.

To avoid too much message noise, it'd be nice to optionally attach the confirmation of sparkles to the original message instead of creating a new message from the bot.

Describe the solution you'd like

See https://api.slack.com/messaging/composing/layouts#building-attachments

But basically, adding an attachment to any message that is triggering sparkles.

Describe alternatives you've considered

The existing method of providing an additional message can be noisy, but is a fine alternative, so maybe this is optional behavior?

Additional context

n/a

Convert reasons from Slack markdown to renderable HTML

Right now, we display sparkles' reasons as the original, raw text. This means any emoji or other formatting are displayed as markdown instead of translated into HTML and rendered with the formatting intact.

In the interest of not writing our own Slack markdown parser, this gem might be our best bet: https://github.com/rutan/slack_markdown

To make this work properly and render custom emoji, we'll need to supply the original_emoji_set option to the Markdown formatter. We already require the emoji:read scope when installing Sparkles into a workspace, so we should be able to use the emoji.list API endpoint and provide the result as the original_emoji_set.

However, the slack_markdown gem doesn't take emoji aliases into account:

https://github.com/rutan/slack_markdown/blob/master/lib/slack_markdown/filters/emoji_filter.rb#L28-L30

As an example, every Slack workspace has this default emoji definition:

{
  "shipit": "alias:squirrel"
}

We can use the fork present at davidcelis/slack_markdown, but I've also opened a pull request at rutan/slack_markdown#5; if the author accepts the change and cuts a new release, we can go back to using the stock gem.

Live-updating leaderboard using Hotwire

If we're on the first page, we could make our leaderboard live-update using Hotwire. No need to do this any time soon, but maybe there are some people out there who love Sparkles so much that they want to have it up on a screen permanently and not have to refresh the page. Mostly it would just be an excuse to learn Hotwire 😅

Stop Sparklebot from having to join every public channel

Right now, Sparklebot joins every public (non-shared) channel as they're created. A frustrating problem with the Slack API means that we aren't notified when a channel becomes private, so this was to work around a potential issue: some public channel where people have been given sparkles becomes private, we lose access to it, and people try to view sparkles that were given in that channel but we can no longer see it.

However, we never actually fetch information about channels outside of events and syncing, so maybe this isn't really a problem that would cause breakage with the app.

I think a better and safer workaround that wouldn't require joining channels would be to rely on our daily sync. When the SyncSlackTeamWorker runs, detect channels that we have stored locally but which no longer appear in the results of conversations.list, updating them to be marked as private. We won't get updates to those channels unless Sparklebot is invited, but we'll at least hide the channel name appropriately and just refer to it as "a secret place" where stats are visible.

Clean up the test suite

Some of the test suite, especially tests involving building BlockKit views (e.g. tests for StatsWorker and the settings slash command) are very verbose and could maybe be pared down to be cleaner. These two classes build up big BlockKit views on their own and we may benefit from presenter objects that are designed to translate things into BlockKit (Sparkle records should definitely have a cleaner way to translate into BlockKit).

Additionally, although StatsWorker is now tested, we could still use some integration tests for the leaderboard on the Rails frontend.

Sparkle associations do not obey the underlying team

Description

Technically, the sparklee and sparkler associations for our Sparkle model use only the users.slack_id column, which means they are prone to break if we encounter users in different teams that share an ID. Ideally we could define these associations to query on both users.slack_team_id and users.slack_id, but Rails does not provide a fully-supported way to define an association with multiple foreign keys.

We should continue to reference Teams, Users, and Channels by their Slack IDs, but because Sparkle is an internal model, the easiest thing to do would be to change its references to use our internal identifiers, which is just Rails' default auto-incrementing id column. We should add sparklee_id, sparkler_id, and channel_id to the sparkles table, populate it based on the contents of slack_team_id, slack_sparklee_id, slack_sparkler_id, and slack_channel_id and then drop those columns.

Steps to Reproduce

To reproduce the behavior:

  1. Sync a user from team T123 that has the id U123
  2. Sync a user from team T456 that has the id U123
  3. Load the sparkles association for the second user
  4. The results will include sparkles for both users

Expected behavior

The sparkles association for the second user should only contain that user's sparkles.

Having a dedicated channel to share sparkle information in

Is your feature request related to a problem? Please describe.

N/A

Describe the solution you'd like

Would be great if you could have a channel where all Sparkle information was aggregated to so that others could see the sparkles that were sent as they were coming in, instead of needing to be in the direct channel the Sparkle was given.

Describe alternatives you've considered

N/A

Additional context

Depending on if you have plans for making Sparkles potentially private and public I could see this not being a great solution for private sparkles where maybe the message is intended more directly for the receiver of the sparkle.

Team admins are unable to un-set their sparkle feed channel

Description

We allow team admins to set a feed channel that we cross-post all public Sparkles in. Unfortunately, there seems to be a limitation with the channels_select BlockKit element: there's no null option. Once a channel is selected, there's no way to unset it.

Steps to Reproduce

To reproduce the behavior:

  1. Run /sparkle settings as an admin
  2. Select a feed channel
  3. Press the "Submit" button
  4. Run /sparkle settings again
  5. There is no way to unselect a channel or clear the field

Expected behavior

There is either a way to clear the Slack Feed Channel field or to submit some kind of null value.

[Leaderboard Feature] List amount of sparkles given from a user

Is your feature request related to a problem? Please describe.

N/A

Describe the solution you'd like

Would be nice to have a list of sparkles that a user has given vs. received as kinda a way to gauge the engagement of users within the app. Not really a point war but would maybe help in showing participation.

Describe alternatives you've considered

Possibly make this a command just for the current individual to see a list of who they have sent a sparkle to?

Additional context

N/A

We need to un-set the `slack_feed_channel_id` field if the channel is archived/deleted

Description

We allow team admins to set a feed channel that all sparkles are cross-posted into. However, when that channel is archived or deleted, we will continue to try to post all sparkles into it and receive an error. We should run a quick check in the channel_archived and channel_deleted events to see if that channel is the team's Slack Feed Channel; if so, we should just un-set it.

Steps to Reproduce

To reproduce the behavior:

  1. Run /sparkle settings
  2. Choose a channel to act as the Sparkle Feed Channel
  3. Archive or delete the channel
  4. Run /sparkle @user to try giving a sparkle
  5. Sparklebot will error

Expected behavior

We un-set the slack_feed_channel_id field for the team and /sparkle @user succeeds

Allow giving multiple people a sparkle in one command

If multiple people helped me out with something, I'd like to be able to give them all a sparkle at once!

Passing multiple User IDs

Users should be able to do something like /sparkle @user1 @user2 @user3 for helping me out and have Sparklebot create an identical sparkle for everyone specified.

Passing a user group

The /sparkle command should also support user groups; if provided a user group, we'll get a string like this on the backend: /sparkle <!subteam^SAZ94GDB8> for helping me out. Calling this should have the same result as calling /sparkle @user1 @user2 @etc for helping me out

To make this work, we'll need to add the usergroups:read bot scope so that we can figure out which users belong to the group; anybody who wants to use this feature will have to reinstall Sparkles 🙃

Providing a proper error response for trying to /sparkle @here|@channel|@everyone

If we get <!here>, <!channel> or <!everyone> as the argument to /sparkle, we return the generic "I didn't understand your command" text along with showing the help command text. We should catch these and provide a more reasonable error like "Sorry, but using @here/@channel/@everyone is pretty disruptive, and I want to make sure that Sparkles stays fun for everyone."

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.