PlusPlus is a Slack app for giving your teammates karma. React to a message with 🔥 or
type @plusplus @sam ++, and PlusPlus keeps score per workspace, with a leaderboard and
a little snark. It's written in Go and runs as a single container backed by DynamoDB.
- Applies karma from emoji reactions (
reaction_added/reaction_removed): 👏 🙌 ⭐ 🔥 🏆 give 1–5, 🙄 👎 🤡 💩 🗑️ take 1–5. Each reactor's strongest reaction on a message counts. Reactions to a typed karma message (@plusplus @sam ++) go to the people it credited, not the poster. Channels and workspaces can remap tiers to their own emojis with/plusplus-settings emoji. - Applies karma from
@user ++text: viaapp_mention(@plusplus @sam ++) everywhere, and from plain channel messages (messageevents) when the app has the*:historyscopes (self-hosted). - Supports
+and-runs with discord-karma parity rules (min 2 symbols, cap at 6 symbols => max delta 5). - Slash commands:
/plusplus-leaderboard/plusplus-settings reply_mode thread|channel/plusplus-settings emoji [workspace] [<tier> :emoji: | reset]- Add
help(or anything unknown) to either command for usage.
- DynamoDB storage with workspace (
team_id) isolation.
Install the hosted app from the Slack Marketplace, or from pluspluskarma.dev.
Want to run your own instance? Published multi-arch container images
(ghcr.io/jordan-simonovski/plusplus) are built on every push to main. See
docs/self-hosting.md for the full operator guide:
required environment variables, the image name and tags, and DynamoDB table
setup (let the app create them, or pre-create with least-privilege IAM).
PORT(default:8080)AWS_REGION(default:ap-southeast-2)AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY(DynamoDB credentials; standard AWS SDK chain)DYNAMODB_ENDPOINT(optional; set tohttp://localhost:8000for DynamoDB Local, leave unset in prod)DYNAMODB_KARMA_TABLE(default:plusplus_karma)DYNAMODB_SETTINGS_TABLE(default:plusplus_channel_settings)DYNAMODB_WORKSPACES_TABLE(default:plusplus_workspaces)MAX_KARMA_PER_ACTION(default:5; a whole number of at least 1)SLACK_SIGNING_SECRET(required; the server will not start without it)SLACK_BOT_TOKEN(required unlessSLACK_CLIENT_IDis set; single-workspace / dev only; used to post via the Slack Web API when OAuth is not configured). WhenSLACK_CLIENT_IDis set (multi-tenant OAuth), per-workspace tokens are authoritative and this value is ignored.PUBLIC_BASE_URL(required whenSLACK_CLIENT_IDis set; fixes the OAuthredirect_urito a trusted origin instead of inferring it from request headers)SLACK_OAUTH_SCOPES(optional; bot scopes/slack/installrequests. Defaults to the Marketplace set without*:history. Self-hosted installs that want plain@sam ++addchannels:history,groups:history.)
Tables are created automatically on startup (on-demand / pay-per-request billing) if they do not already exist. The IAM principal therefore needs dynamodb:CreateTable and dynamodb:DescribeTable in addition to the item operations (see deployment section).
Contributions are welcome. See CONTRIBUTING.md for how to run it, the checks a pull request must pass, and when to add a changeset. Coding agents should start at AGENTS.md.
- Go (the version in
go.mod) - Docker + Docker Compose
make upThis starts:
dynamodb(DynamoDB Local) onlocalhost:8000(in-memory)- app on
localhost:8080(tables auto-created on boot)
make downmake runmake check # format check, vet, lint and race tests (what CI runs)
make fmt
make test-integration- Create a Slack app from a manifest using
slack-app-manifest.self-hosted.yaml(replace<your-host>/<public-url>), then install it to your workspace. It includes reactions plus the*:historyscopes for plain@sam ++. Dropchannels:history,groups:historyand themessage.*events if you only want reactions and@plusplusmentions. (The Marketplace app usesslack-app-manifest.yaml.) - Set local env vars:
SLACK_SIGNING_SECRETSLACK_BOT_TOKEN
For local callbacks, use a tunnel (for example ngrok):
ngrok http 8080How the hosted app is deployed (DynamoDB, Railway, Terraform and its CI) is in
docs/operations.md. The one-off Postgres (Supabase) to DynamoDB
migration tool, cmd/migrate, was removed after the cutover; it is in commit 01f05bb
if you still need it.
MIT; see LICENSE.md.