├── .babelrc ├── .gitignore ├── README.md ├── app ├── application.js ├── handlebars-helpers.js ├── slack-message-builder.js └── templates │ ├── attachment.hbs │ ├── attachment_author.hbs │ ├── attachment_field.hbs │ ├── attachment_fields.hbs │ ├── attachment_title.hbs │ └── message.hbs ├── build.js ├── dist ├── application.js └── style.css ├── index.html ├── module_wrappers ├── wrap-end.frag.js └── wrap-start.frag.js ├── package.json └── style ├── _attachment.scss ├── _attachment_field.scss ├── _message.scss └── style.scss /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | .sass-cache 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slack Message Builder 2 | 3 | **Slack now have a tool that does this https://api.slack.com/docs/messages/builder** 4 | 5 | See what the payload you send to a [Slack](https://slack.com) `chat.postMessage` will be displayed like. 6 | Slack Formatting documentation: https://api.slack.com/docs/formatting 7 | 8 | Give it a go: 9 | http://davestevens.github.io/slack-message-builder 10 | 11 | ## Development 12 | 13 | Written in ES6 (using Browserify) and Sass. 14 | 15 | ### Building 16 | 17 | ```npm install``` 18 | Install all required dependencies. 19 | 20 | ```npm run build``` 21 | Builds JS and CSS from `app` directory into `dist` directory. 22 | 23 | ```npm start``` 24 | Runs a local server. 25 | -------------------------------------------------------------------------------- /app/application.js: -------------------------------------------------------------------------------- 1 | var $ = require("jquery"); 2 | var SlackMessageBuilder = require("./slack-message-builder"); 3 | 4 | var builder = new SlackMessageBuilder({ 5 | $input: $(".input"), 6 | $output: $(".output") 7 | }); 8 | builder.bind_events(); 9 | -------------------------------------------------------------------------------- /app/handlebars-helpers.js: -------------------------------------------------------------------------------- 1 | var Handlebars = require("handlebars/runtime")["default"]; 2 | var $ = require("jquery"); 3 | 4 | module.exports = Handlebars; 5 | 6 | Handlebars.registerPartial({ 7 | message: require("./templates/message.hbs"), 8 | attachment: require("./templates/attachment.hbs"), 9 | attachment_author: require("./templates/attachment_author.hbs"), 10 | attachment_title: require("./templates/attachment_title.hbs"), 11 | attachment_fields: require("./templates/attachment_fields.hbs"), 12 | attachment_field: require("./templates/attachment_field.hbs") 13 | }); 14 | 15 | Handlebars.registerHelper("attachment_format", (name, attachment) => { 16 | var string = attachment[name]; 17 | var markdown = $.inArray(name, attachment.mrkdwn_in) > -1; 18 | return Handlebars.helpers.format(string, markdown); 19 | }); 20 | 21 | Handlebars.registerHelper("member_image", (item) => { 22 | // TODO: emoji_url || icon_url 23 | return item.icon_url; 24 | }); 25 | 26 | Handlebars.registerHelper("timestamp", () => { 27 | var date = new Date() 28 | var hours = date.getHours() 29 | var minutes = date.getMinutes() 30 | var meridiem = (hours > 11) ? "PM" : "AM" 31 | return `${hours % 12}:${minutes} ${meridiem}` 32 | }); 33 | 34 | Handlebars.registerHelper("format", (string, markdown) => { 35 | if (!string) { return ""; } 36 | // links 37 | string = string.replace(/<(.*?)>/g, "$1"); 38 | 39 | // replace newlines 40 | string = string.replace("\n", "
"); 41 | // replace spaces 42 | string = string.replace(/\s/g, " "); 43 | 44 | if (markdown) { 45 | // markdown 46 | //// bold * 47 | string = string.replace(/\*(.*?)\*/g, "$1"); 48 | //// italic _ 49 | string = string.replace(/_(.*?)_/g, "$1"); 50 | //// pre ``` 51 | string = string.replace(/```(.*?)```/g, "
$1
"); 52 | //// code ` 53 | string = string.replace(/`(.*?)`/g, "$1"); 54 | } 55 | 56 | return new Handlebars.SafeString(string); 57 | }); 58 | 59 | Handlebars.registerHelper("wrap_link", (item, href) => { 60 | if (!href) { return item; } 61 | return new Handlebars.SafeString("#{item}"); 62 | }); 63 | -------------------------------------------------------------------------------- /app/slack-message-builder.js: -------------------------------------------------------------------------------- 1 | var $ = require("jquery"); 2 | var Handlebars = require("./handlebars-helpers"); 3 | 4 | class SlackMessageBuilder { 5 | constructor(options) { 6 | this.$input = options.$input; 7 | this.$output = options.$output; 8 | 9 | this.defaults = { 10 | payload: { 11 | username: "incoming-webhook", 12 | icon_url: "https://avatars1.githubusercontent.com/u/287677?v=3&s=40", 13 | mrkdwn: true, 14 | attachments: [] 15 | }, 16 | attachment: { 17 | color: "#E3E4E6", 18 | mrkdwn_in: [] 19 | } 20 | }; 21 | } 22 | 23 | bind_events() { 24 | this.$input 25 | .find(".js-build") 26 | .on("click", this._parse_message.bind(this)); 27 | } 28 | 29 | // Private 30 | 31 | _parse_message(event) { 32 | event.preventDefault(); 33 | 34 | var $payload = this.$input.find(".js-payload"), 35 | payload = this._parse($payload.val()); 36 | if (!payload) { return; } 37 | $payload.val(JSON.stringify(payload, null, 2)); 38 | 39 | payload = $.extend({}, this.defaults.payload, payload); 40 | payload.attachments = this._extend_attachments(payload.attachments); 41 | 42 | var html = Handlebars.partials.message(payload); 43 | 44 | this.$output.append(html); 45 | this.$output.animate({ scrollTop: this.$output.get(0).scrollHeight }, 800); 46 | } 47 | 48 | _parse(input) { 49 | try { 50 | return JSON.parse(input); 51 | } 52 | catch(error) { 53 | // TODO: display error inline 54 | alert(`Error parsing JSON: ${error.message}`); 55 | } 56 | } 57 | 58 | _extend_attachments(attachments) { 59 | var defaults = this.defaults.attachment; 60 | return attachments.map(attachment => { 61 | return $.extend({}, defaults, attachment); 62 | }); 63 | } 64 | }; 65 | 66 | module.exports = SlackMessageBuilder; 67 | -------------------------------------------------------------------------------- /app/templates/attachment.hbs: -------------------------------------------------------------------------------- 1 |
2 |
{{ attachment_format "pretext" this }}
3 |
4 |
 
5 |
6 | {{> attachment_author}} 7 | {{> attachment_title}} 8 |
{{ attachment_format "text" this }}
9 | {{> attachment_fields}} 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /app/templates/attachment_author.hbs: -------------------------------------------------------------------------------- 1 | {{#if author_name}} 2 |
3 | {{#if author_icon}} 4 | 10 | {{/if}} 11 | 12 | {{ wrap_link author_name author_link }} 13 | 14 |
15 | {{/if}} 16 | -------------------------------------------------------------------------------- /app/templates/attachment_field.hbs: -------------------------------------------------------------------------------- 1 | 2 |
{{ title }}
3 |
{{ value }}
4 | 5 | -------------------------------------------------------------------------------- /app/templates/attachment_fields.hbs: -------------------------------------------------------------------------------- 1 | {{#if fields}} 2 |
3 | 4 | 5 | 6 | {{#each fields}} 7 | {{> attachment_field}} 8 | {{/each}} 9 | 10 | 11 |
12 |
13 | {{/if}} 14 | -------------------------------------------------------------------------------- /app/templates/attachment_title.hbs: -------------------------------------------------------------------------------- 1 | {{#if title}} 2 |
{{ wrap_link title title_link }}
3 | {{/if}} 4 | -------------------------------------------------------------------------------- /app/templates/message.hbs: -------------------------------------------------------------------------------- 1 |
2 | 6 | {{ username }} 7 | BOT 8 | {{ timestamp }} 9 |
{{ format text mrkdwn }}
10 | {{#each attachments}} 11 | {{> attachment}} 12 | {{/each}} 13 |
14 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "app", 3 | "paths": { 4 | "text": "../node_modules/requirejs-text/text", 5 | "handlebars": "../node_modules/handlebars/dist/handlebars", 6 | "jquery": "../node_modules/jquery/dist/jquery" 7 | }, 8 | "name": "../node_modules/almond/almond", 9 | "include": "slack_message_builder", 10 | "out": "dist/slack_message_builder.min.js", 11 | "wrap": { 12 | "startFile": "module_wrappers/wrap-start.frag.js", 13 | "endFile": "module_wrappers/wrap-end.frag.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Slack Message Builder 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /module_wrappers/wrap-end.frag.js: -------------------------------------------------------------------------------- 1 | // wrap-end.frag.js 2 | return require("slack_message_builder"); 3 | })); 4 | -------------------------------------------------------------------------------- /module_wrappers/wrap-start.frag.js: -------------------------------------------------------------------------------- 1 | // wrap-start.frag.js 2 | (function (root, factory) { 3 | if (typeof define === 'function') { 4 | define(factory); 5 | } else if (typeof exports === 'object') { 6 | module.exports = factory(); 7 | } else { 8 | root.SlackMessageBuilder = factory(); 9 | } 10 | }(this, function () { 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slack-message-builder", 3 | "version": "0.0.0", 4 | "description": "Slack Message Builder", 5 | "author": "Dave Stevens ", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/davestevens/slack-message-builder.git" 9 | }, 10 | "license": "MIT", 11 | "scripts": { 12 | "start": "http-server", 13 | "build": "npm run build-js && npm run build-css", 14 | "build-js": "browserify app/application.js -o dist/application.js", 15 | "watch-js": "watchify app/application.js -o dist/application.js", 16 | "build-css": "compass compile --sass-dir style --css-dir dist", 17 | "watch-css": "compass watch --sass-dir style --css-dir dist" 18 | }, 19 | "devDependencies": { 20 | "handlebars": "^3.0.0", 21 | "http-server": "^0.7.3", 22 | "jquery": "^2.1.1" 23 | }, 24 | "dependencies": { 25 | "babel-core": "^6.1.2", 26 | "babel-preset-es2015": "^6.1.2", 27 | "babelify": "^7.2.0", 28 | "browserify": "^12.0.1", 29 | "browserify-handlebars": "^1.0.0", 30 | "foundation-sites": "^5.5.3", 31 | "watchify": "^3.6.0" 32 | }, 33 | "browserify": { 34 | "transform": [ 35 | "babelify", 36 | "browserify-handlebars" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /style/_attachment.scss: -------------------------------------------------------------------------------- 1 | .attachment { 2 | &__pretext { 3 | } 4 | 5 | &__wrapper { 6 | padding: 5px 0; 7 | display: table; 8 | width: 100%; 9 | } 10 | 11 | &__bar { 12 | width: 4px; 13 | @include border-radius(8px); 14 | display: table-cell; 15 | } 16 | 17 | &__content { 18 | display: table-cell; 19 | padding-left: 12px; 20 | } 21 | 22 | &__author { 23 | &--icon { 24 | width: 16px; 25 | height: 16px; 26 | float: left; 27 | margin-top: 2px; 28 | margin-right: 5px; 29 | } 30 | 31 | &--name { 32 | font-weight: bold; 33 | } 34 | } 35 | 36 | &__title { 37 | font-weight: bold; 38 | } 39 | 40 | &__content { 41 | } 42 | 43 | &__fields { 44 | display: inline-block; 45 | width: 100%; 46 | 47 | table { 48 | width: inherit; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /style/_attachment_field.scss: -------------------------------------------------------------------------------- 1 | .attachment_field { 2 | &__title { 3 | font-weight: bold; 4 | } 5 | 6 | &__value { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /style/_message.scss: -------------------------------------------------------------------------------- 1 | .message { 2 | padding: 3px 48px; 3 | font-size: 15px; 4 | line-height: 22px; 5 | color: #3d3c40; 6 | position: relative; 7 | 8 | &__image { 9 | position: absolute; 10 | left: 0; 11 | top: 3px; 12 | width: 36px; 13 | height: 36px; 14 | border: 0; 15 | @include border-radius(4px); 16 | } 17 | 18 | &__sender { 19 | font-weight: bold; 20 | } 21 | 22 | &__label { 23 | &--bot { 24 | color: #797979; 25 | font-size: 12px; 26 | background: #f3f3f3; 27 | padding: 2px; 28 | } 29 | } 30 | 31 | &__timestamp { 32 | font-size: 12px; 33 | color: #babbbf; 34 | } 35 | 36 | &__content { 37 | } 38 | 39 | &__attachment { 40 | max-width: 520px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /style/style.scss: -------------------------------------------------------------------------------- 1 | @import "../node_modules/foundation-sites/scss/foundation"; 2 | @import "compass/css3/border-radius"; 3 | @import "message"; 4 | @import "attachment"; 5 | @import "attachment_field"; 6 | 7 | html, body { 8 | font-family: Helvetica, sans-serif; 9 | padding: 0; 10 | margin: 0; 11 | } 12 | 13 | a { 14 | text-decoration: none; 15 | } 16 | 17 | .container { 18 | width: 100%; 19 | height: 100%; 20 | max-width: initial; 21 | } 22 | 23 | .input { 24 | height: 100%; 25 | background-color: #ccc; 26 | padding: 6px 10px; 27 | 28 | .text { 29 | height: calc(100% - 50px); 30 | 31 | textarea { 32 | width: 100%; 33 | height: 100%; 34 | font-family: monospace; 35 | } 36 | } 37 | 38 | .build { 39 | height: 50px; 40 | 41 | .button { 42 | width: 100%; 43 | margin: 0; 44 | } 45 | } 46 | } 47 | 48 | .output { 49 | height: 100%; 50 | overflow-x: scroll; 51 | padding: 6px 10px; 52 | } 53 | --------------------------------------------------------------------------------