├── lib
├── popup.js
├── assets
│ ├── checkbox.png
│ ├── verdana.ttf
│ ├── checkbox-checked.svg
│ ├── checkbox.svg
│ ├── slack.svg
│ ├── iframe.js
│ ├── client.js
│ ├── iframe-button.css
│ ├── badge.js
│ └── superagent.js
├── log.js
├── badge.js
├── iframe.js
├── slack-invite.js
├── slack.js
├── index.js
└── splash.js
├── .travis.yml
├── test
├── mocha.opts
├── setup.js
├── slack-invite.js
└── index.js
├── .gitignore
├── .bluemix
├── toolchain.png
├── toolchain_dark.png
├── locales.yml
├── nls
│ └── messages.yml
├── toolchain.yml
├── pipeline.yml
├── icon.svg
├── deploy.json
└── toolchain.svg
├── Procfile
├── Dockerfile
├── gulpfile.babel.js
├── license.md
├── app.json
├── package.json
├── bin
└── slackin
├── readme.md
├── web.config
├── azuredeploy.json
├── scripts
└── azuredeploy.sh
└── HISTORY.md
/lib/popup.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - stable
4 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --compilers js:babel-register
2 | --require ./test/setup
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules
3 |
4 | # logs
5 | npm-debug.log
6 |
--------------------------------------------------------------------------------
/.bluemix/toolchain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rauchg/slackin/HEAD/.bluemix/toolchain.png
--------------------------------------------------------------------------------
/lib/assets/checkbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rauchg/slackin/HEAD/lib/assets/checkbox.png
--------------------------------------------------------------------------------
/lib/assets/verdana.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rauchg/slackin/HEAD/lib/assets/verdana.ttf
--------------------------------------------------------------------------------
/.bluemix/toolchain_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rauchg/slackin/HEAD/.bluemix/toolchain_dark.png
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | import nock from 'nock';
2 |
3 | nock.disableNetConnect();
4 | // allow websockets
5 | nock.enableNetConnect(/127\.0\.0\.1:\d+/);
6 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:slim
2 |
3 | ENV PORT 3000
4 |
5 | ADD . /srv/www
6 |
7 | WORKDIR /srv/www
8 |
9 | RUN npm install --unsafe-perm
10 |
11 | EXPOSE 3000
12 |
13 | CMD ./bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
14 |
--------------------------------------------------------------------------------
/lib/assets/checkbox-checked.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/assets/checkbox.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.bluemix/locales.yml:
--------------------------------------------------------------------------------
1 | ---
2 | root:
3 | $ref: ./nls/messages.yml
4 | de:
5 | $ref: ./nls/messages_de.yml
6 | en-AA:
7 | $ref: ./nls/messages_en_AA.yml
8 | en-RR:
9 | $ref: ./nls/messages_en_RR.yml
10 | en-ZZ:
11 | $ref: ./nls/messages_en_ZZ.yml
12 | es:
13 | $ref: ./nls/messages_es.yml
14 | fr:
15 | $ref: ./nls/messages_fr.yml
16 | it:
17 | $ref: ./nls/messages_it.yml
18 | ja:
19 | $ref: ./nls/messages_ja.yml
20 | ko:
21 | $ref: ./nls/messages_ko.yml
22 | pt-BR:
23 | $ref: ./nls/messages_pt_BR.yml
24 | zh:
25 | $ref: ./nls/messages_zh.yml
26 | zh-HK:
27 | $ref: ./nls/messages_zh_HK.yml
28 | zh-TW:
29 | $ref: ./nls/messages_zh_TW.yml
30 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp'
2 | import babel from 'gulp-babel'
3 | import rimraf from 'gulp-rimraf'
4 |
5 | const paths = {
6 | in: {
7 | js: 'lib/*.js',
8 | assets: 'lib/assets/*'
9 | },
10 | out: {
11 | js: 'dist',
12 | assets: 'dist/assets'
13 | }
14 | }
15 |
16 | gulp.task('transpile', () => {
17 | return gulp.src(paths.in.js)
18 | .pipe(babel())
19 | .pipe(gulp.dest(paths.out.js))
20 | })
21 |
22 | gulp.task('assets', () => {
23 | return gulp.src(paths.in.assets)
24 | .pipe(gulp.dest(paths.out.assets))
25 | })
26 |
27 | gulp.task('clean', () => {
28 | return gulp.src(paths.out.js, {
29 | read: false
30 | })
31 | .pipe(rimraf())
32 | })
33 |
34 | gulp.task('default', ['transpile', 'assets'])
35 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Guillermo Rauch
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/test/slack-invite.js:
--------------------------------------------------------------------------------
1 | import nock from 'nock';
2 | import assert from 'assert';
3 | import invite from '../lib/slack-invite';
4 |
5 | describe('slack-invite', () => {
6 | describe('.invite()', () => {
7 | var opts;
8 |
9 | before(() => {
10 | opts = {
11 | channel: 'mychannel',
12 | email: 'user@example.com',
13 | org: 'myorg',
14 | token: 'mytoken'
15 | };
16 | });
17 |
18 | it("succeeds when ok", (done) => {
19 | nock(`https://${opts.org}.slack.com`).
20 | post('/api/users.admin.invite').
21 | reply(200, { ok: true });
22 |
23 | invite(opts, (err) => {
24 | assert.equal(err, null);
25 | done();
26 | });
27 | });
28 |
29 | it("passes along an error message", (done) => {
30 | nock(`https://${opts.org}.slack.com`).
31 | post('/api/users.admin.invite').
32 | reply(200, {
33 | ok: false,
34 | error: "other error"
35 | });
36 |
37 | invite(opts, (err) => {
38 | assert.notEqual(err, null);
39 | assert.equal(err.message, "other error");
40 | done();
41 | });
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/lib/log.js:
--------------------------------------------------------------------------------
1 | import dbg from 'debug'
2 | const debug = dbg('slackin')
3 |
4 | export default function log (slack, silent){
5 | // keep track of elapsed time
6 | let last
7 |
8 | out('fetching')
9 |
10 | // attach events
11 | slack.on('ready', () => out('ready'))
12 | slack.on('retry', () => out('retrying'))
13 |
14 | slack.on('fetch', () => {
15 | last = new Date
16 | out('fetching')
17 | })
18 |
19 | slack.on('data', online)
20 |
21 | // log online users
22 | function online (){
23 | out('online %d, total %d %s',
24 | slack.users.active,
25 | slack.users.total,
26 | last ? `(+${new Date - last}ms)` : '')
27 | }
28 |
29 | // print out errors and warnings
30 | if (!silent) {
31 | slack.on('error', (err) => {
32 | console.error('%s – ' + err.stack, new Date)
33 | })
34 |
35 | slack.on('ready', () => {
36 | if (!slack.org.logo && !silent) {
37 | console.warn('\u001b[92mWARN: no logo configured\u001b[39m')
38 | }
39 | })
40 | }
41 |
42 | function out (...args){
43 | if (args) {
44 | args[0] = `${new Date} – ${args[0]}`
45 | }
46 |
47 | if (silent) return debug(...args)
48 | console.log(...args)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/badge.js:
--------------------------------------------------------------------------------
1 | import svg from 'vd'
2 |
3 | const title = 'slack'
4 | const color = '#E01563'
5 | const pad = 8 // left / right padding
6 | const sep = 4 // middle separation
7 |
8 | export default function badge ({ total, active }){
9 | let value = active ? `${active}/${total}` : ('' + total || '–')
10 | let lw = pad + width(title) + sep // left side width
11 | let rw = sep + width(value) + pad // right side width
12 | let tw = lw + rw // total width
13 |
14 | return svg(`svg xmlns="http://www.w3.org/2000/svg" width=${tw} height=20`,
15 | svg(`rect rx=3 width=${tw} height=20 fill=#555`),
16 | svg(`rect rx=3 x=${lw} width=${rw} height=20 fill=${color}`),
17 | svg(`path d="M${lw} 0h${sep}v20h-${sep}z" fill=${color}`),
18 | svg('g text-anchor=middle font-family=Verdana font-size=11',
19 | text({ str: title, x: Math.round(lw / 2), y: 14 }),
20 | text({ str: value, x: lw + Math.round(rw / 2), y: 14 })
21 | )
22 | )
23 | }
24 |
25 | // generate text with 1px shadow
26 | function text ({str, x, y}){
27 | return [
28 | svg(`text fill=#010101 fill-opacity=.3 x=${x} y=${y + 1}`, str),
29 | svg(`text fill=#fff x=${x} y=${y}`, str)
30 | ]
31 | }
32 |
33 | // π=3
34 | function width (str){
35 | return 7 * str.length
36 | }
37 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Slackin",
3 | "description": "Let people invite themselves to your Slack",
4 | "logo": "https://i.imgur.com/odDO1Fx.png",
5 | "repository": "https://github.com/rauchg/slackin",
6 | "keywords": ["node", "slack"],
7 | "env": {
8 | "SLACK_SUBDOMAIN": {
9 | "description": "Your Slack's subdomain (**this**.slack.com)",
10 | "required": true
11 | },
12 | "SLACK_API_TOKEN": {
13 | "description": "A Slack API token (find it on https://api.slack.com/web)",
14 | "required": true
15 | },
16 | "GOOGLE_CAPTCHA_SECRET": {
17 | "description": "Google captcha secret key",
18 | "required": true
19 | },
20 | "GOOGLE_CAPTCHA_SITEKEY": {
21 | "description": "Google captcha site key",
22 | "required": true
23 | },
24 | "SLACK_COC": {
25 | "description": "A URL to a Code of Conduct people must agree on before joining.",
26 | "required": false
27 | },
28 | "SLACK_CHANNELS": {
29 | "description": "Comma-separated list of single guest channels to invite them to (leave blank for a normal, all-channel invite). In order to make this work, you have to have a paid account. You'll only be able to invite as many people as your number of paying members times 5.",
30 | "required": false
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/iframe.js:
--------------------------------------------------------------------------------
1 | import dom from 'vd'
2 | import { readFileSync as read } from 'fs'
3 |
4 | const logo = read(__dirname + '/assets/slack.svg').toString('base64')
5 | const js = read(__dirname + '/assets/iframe.js').toString()
6 | const css = read(__dirname + '/assets/iframe-button.css').toString()
7 |
8 | export default function iframe ({ path, active, total, large }){
9 | let str = ''
10 | if (active) str = `${active}/`
11 | if (total) str += total
12 | if (!str.length) str = '–'
13 |
14 | let opts = { 'class': large ? 'slack-btn-large' : '' }
15 | let div = dom('span.slack-button', opts,
16 | dom('a.slack-btn href=/ target=_blank',
17 | dom('span.slack-ico'),
18 | dom('span.slack-text', 'Slack')
19 | ),
20 | dom('a.slack-count href=/ target=_blank', str),
21 | dom('style', css),
22 | dom.style().add('.slack-ico', {
23 | 'background-image': `url(data:image/svg+xml;base64,${logo})`
24 | }),
25 | dom('script', `
26 | data = {};
27 | data.path = ${JSON.stringify(path)};
28 | data.total = ${total != null ? total : 'null'};
29 | data.active = ${active != null ? active : 'null'};
30 | `),
31 | dom('script', js)
32 | )
33 |
34 | return div
35 | }
36 |
37 | function gradient (css, sel, params){
38 | ['-webkit-', '-moz-', ''].forEach(p => {
39 | css.add(sel, {
40 | 'background-image': `${p}linear-gradient(${params})`
41 | })
42 | })
43 | }
44 |
--------------------------------------------------------------------------------
/lib/slack-invite.js:
--------------------------------------------------------------------------------
1 | import request from 'superagent'
2 |
3 | export default function invite ({ org, token, email, channel }, fn){
4 | let data = { email, token }
5 |
6 | if (channel) {
7 | data.channels = channel
8 | data.ultra_restricted = 1
9 | data.set_active = true
10 | }
11 |
12 | request
13 | .post(`https://${org}.slack.com/api/users.admin.invite`)
14 | .type('form')
15 | .send(data)
16 | .end(function (err, res){
17 | if (err) return fn(err)
18 | if (200 != res.status) {
19 | fn(new Error(`Invalid response ${res.status}.`))
20 | return
21 | }
22 |
23 | // If the account that owns the token is not admin, Slack will oddly
24 | // return `200 OK`, and provide other information in the body. So we
25 | // need to check for the correct account scope and call the callback
26 | // with an error if it's not high enough.
27 | let {ok, error: providedError, needed} = res.body
28 | if (!ok) {
29 | if (providedError === 'missing_scope' && needed === 'admin') {
30 | fn(new Error(`Missing admin scope: The token you provided is for an account that is not an admin. You must provide a token from an admin account in order to invite users through the Slack API.`))
31 | } else if (providedError === 'already_invited') {
32 | fn(new Error('You have already been invited to Slack. Check for an email from feedback@slack.com.'))
33 | } else if (providedError === 'already_in_team') {
34 | fn(new Error(`Sending you to Slack...`))
35 | } else {
36 | fn(new Error(providedError))
37 | }
38 | return
39 | }
40 |
41 | fn(null)
42 | })
43 | }
44 |
--------------------------------------------------------------------------------
/lib/assets/slack.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.bluemix/nls/messages.yml:
--------------------------------------------------------------------------------
1 | ---
2 | template.name: "Deploy a Slackin server"
3 | template.description: "With this toolchain, you can deploy a Slackin server.This toolchain is preconfigured for continuous delivery, source control, issue tracking, and online editing.\n\nThis toolchain uses tools that are part of the Continuous Delivery service. If an instance of that service isn't already in the selected organization, when you click **Create**, it is automatically added with the free [Lite](/catalog/services/continuous-delivery/) plan selected.\n\nTo get started, click **Create**.\n\nFor step-by-step instructions, follow the [tutorial](https://www.ibm.com/devops/method/tutorials/tutorial_toolchain_flow)."
4 | template.gettingStarted: "**Your toolchain is ready!**"
5 | deploy.title: "Sample Deploy Stage"
6 | deploy.description: "sample toolchain"
7 | deploy.longDescription: "The Delivery Pipeline automates continuous deployment."
8 | deploy.appDescription: "The name of your Slackin server"
9 | deploy.appName: "App name"
10 | deploy.slackTeamId: "Slack Team Name"
11 | deploy.slackApiToken: "Slack Legacy Token"
12 | deploy.captchaSecret: "Google reCaptcha Secret"
13 | deploy.captchaSiteKey: "Google reCaptcha Site Key"
14 | region: "Region"
15 | organization: "Organization"
16 | space: "Space"
17 | prodStage: "Production stage"
18 | headerSVG.issueTracker: "ISSUE TRACKER"
19 | headerSVG.gitHub1: "GitHub"
20 | headerSVG.think: "THINK"
21 | headerSVG.code: "CODE"
22 | headerSVG.deliver: "DELIVER"
23 | headerSVG.run: "RUN"
24 | headerSVG.repository: "REPOSITORY"
25 | headerSVG.gitHub2: "GitHub"
26 | headerSVG.pipeline: "PIPELINE"
27 | headerSVG.bluemix: "BLUEMIX"
28 | headerSVG.ibmCloud: "IBM Cloud"
29 | headerSVG.webIde: "WEB IDE"
30 |
31 |
--------------------------------------------------------------------------------
/.bluemix/toolchain.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 | messages:
3 | $i18n: locales.yml
4 | template:
5 | name:
6 | $ref: "#/messages/template.name"
7 | description:
8 | $ref: "#/messages/template.description"
9 | header: ''
10 | icon: icon.svg
11 | required:
12 | - slackin-build
13 | - slackin-repo
14 | info:
15 | git url: >-
16 | [https://github.com/rauchg/slackin](https://github.com/rauchg/slackin)
17 | git branch: >-
18 | [master](https://github.com/rauchg/slackin/tree/master)
19 | toolchain:
20 | name: 'slackin-toolchain-{{timestamp}}'
21 | template:
22 | getting_started:
23 | $ref: "#/messages/template.gettingStarted"
24 | services:
25 | slackin-repo:
26 | service_id: githubpublic
27 | parameters:
28 | repo_name: '{{toolchain.name}}'
29 | repo_url: 'https://github.com/rauchg/slackin'
30 | type: clone
31 | has_issues: true
32 | enable_traceability: true
33 | slackin-build:
34 | service_id: pipeline
35 | parameters:
36 | services:
37 | - slackin-repo
38 | name: '{{services.slackin-repo.parameters.repo_name}}'
39 | ui-pipeline: true
40 | configuration:
41 | content:
42 | $text: pipeline.yml
43 | env:
44 | SLACKIN_REPO: slackin-repo
45 | CF_APP_NAME: '{{form.pipeline.parameters.prod-app-name}}'
46 | PROD_SPACE_NAME: '{{form.pipeline.parameters.prod-space}}'
47 | PROD_ORG_NAME: '{{form.pipeline.parameters.prod-organization}}'
48 | PROD_REGION_ID: '{{form.pipeline.parameters.prod-region}}'
49 | SLACK_SUBDOMAIN: '{{form.pipeline.parameters.slack-teamid}}'
50 | SLACK_API_TOKEN: '{{form.pipeline.parameters.slack-apitoken}}'
51 | GOOGLE_CAPTCHA_SECRET: '{{form.pipeline.parameters.captcha-secret}}'
52 | GOOGLE_CAPTCHA_SITEKEY: '{{form.pipeline.parameters.captcha-sitekey}}'
53 |
54 | execute: true
55 | webide:
56 | service_id: orion
57 | form:
58 | pipeline:
59 | parameters:
60 | prod-app-name: '{{services.slackin-repo.parameters.repo_name}}'
61 | schema:
62 | $ref: deploy.json
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "slackin",
3 | "version": "0.14.0",
4 | "description": "Public Slack organizations made easy",
5 | "repository": "rauchg/slackin",
6 | "main": "dist/index",
7 | "files": [
8 | "dist",
9 | "bin"
10 | ],
11 | "babel": {
12 | "presets": [
13 | "es2015"
14 | ]
15 | },
16 | "dependencies": {
17 | "args": "1.3.0",
18 | "babel-core": "6.3.26",
19 | "babel-eslint": "6.0.4",
20 | "babel-polyfill": "6.3.14",
21 | "babel-preset-es2015": "6.3.13",
22 | "babel-register": "6.9.0",
23 | "body-parser": "1.10.2",
24 | "cors": "2.7.1",
25 | "debug": "3.0.1",
26 | "email-regex": "1.0.0",
27 | "express": "4.15.4",
28 | "gulp-babel": "6.1.1",
29 | "gulp-rimraf": "0.2.0",
30 | "gulp": "3.9.0",
31 | "hostenv": "1.0.1",
32 | "opentype.js": "0.4.4",
33 | "socket.io": "2.0.3",
34 | "superagent": "3.6.0",
35 | "vd": "0.1.0"
36 | },
37 | "license": "MIT",
38 | "devDependencies": {
39 | "eslint": "2.12.0",
40 | "eslint-config-default": "0.2.0",
41 | "mocha": "2.2.4",
42 | "nock": "2.17.0",
43 | "supertest": "0.15.0"
44 | },
45 | "engines": {
46 | "node": "6.11.1"
47 | },
48 | "bin": {
49 | "slackin": "./bin/slackin"
50 | },
51 | "eslintConfig": {
52 | "extends": "default",
53 | "parser": "babel-eslint",
54 | "rules": {
55 | "no-var": 0
56 | }
57 | },
58 | "scripts": {
59 | "test": "mocha && eslint lib/**",
60 | "postinstall": "gulp",
61 | "build": "gulp",
62 | "start": "chmod +x bin/slackin && ./bin/slackin"
63 | },
64 | "now": {
65 | "type": "npm",
66 | "files": [
67 | "bin",
68 | "lib",
69 | "gulpfile.babel.js"
70 | ],
71 | "env": [
72 | "SLACK_API_TOKEN",
73 | "SLACK_SUBDOMAIN",
74 | "GOOGLE_CAPTCHA_SECRET",
75 | "GOOGLE_CAPTCHA_SITEKEY"
76 | ]
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/bin/slackin:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var pkg = require('./../package')
4 | var args = require('args')
5 | var slackin = require('./../dist').default
6 |
7 | args
8 | .option(['p', 'port'], 'Port to listen on [$PORT or 3000]', require('hostenv').PORT || process.env.PORT || 3000)
9 | .option(['h', 'hostname'], 'Hostname to listen on [$HOSTNAME or 0.0.0.0]', require('hostenv').HOSTNAME || process.env.WEBSITE_HOSTNAME || '0.0.0.0')
10 | .option(['c', 'channels'], 'One or more comma-separated channel names to allow single-channel guests [$SLACK_CHANNELS]', process.env.SLACK_CHANNELS)
11 | .option(['i', 'interval'], 'How frequently (ms) to poll Slack [$SLACK_INTERVAL or 5000]', process.env.SLACK_INTERVAL || 5000)
12 | .option(['P', 'path'], 'Path to serve slackin under', '/')
13 | .option(['s', 'silent'], 'Do not print out warns or errors')
14 | .option(['x', 'cors'], 'Enable CORS for all routes')
15 | .option(['C', 'coc'], 'Full URL to a CoC that needs to be agreed to')
16 | .option(['S', 'css'], 'Full URL to a custom CSS file to use on the main page')
17 | .option(['?', 'help'], 'Show the usage information')
18 |
19 | var flags = args.parse(process.argv, {
20 | value: ' ',
21 | help: false
22 | })
23 |
24 | var org = args.sub[0] || process.env.SLACK_SUBDOMAIN
25 | var token = args.sub[1] || process.env.SLACK_API_TOKEN
26 | var emails = process.env.EMAIL_SLACK_LIST || ''
27 |
28 | var gcaptcha_secret = args.sub[2] || process.env.GOOGLE_CAPTCHA_SECRET
29 | var gcaptcha_sitekey = args.sub[3] || process.env.GOOGLE_CAPTCHA_SITEKEY
30 |
31 |
32 |
33 | if (flags.help) {
34 | args.showHelp()
35 | }
36 |
37 | if (!org || !token || !gcaptcha_sitekey || !gcaptcha_secret) {
38 | args.showHelp()
39 | } else {
40 | flags.org = org
41 | flags.token = token
42 | flags.emails = emails
43 | flags.gcaptcha_secret = gcaptcha_secret
44 | flags.gcaptcha_sitekey = gcaptcha_sitekey
45 | }
46 |
47 | var port = flags.port
48 | var hostname = flags.hostname
49 |
50 | slackin(flags).listen(port, hostname, function (err) {
51 | if (err) throw err
52 | if (!flags.silent) console.log('%s – listening on %s:%d', new Date, hostname, port)
53 | })
54 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## Features
4 |
5 | - A landing page you can point users to fill in their emails and receive an invite (`https://slack.yourdomain.com`)
6 | - An `