Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# build output
dist


# dependencies
node_modules
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ RUN npm install --unsafe-perm

EXPOSE 3000

CMD ./bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN
CMD ./bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
web: bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN
web: bin/slackin --coc "$SLACK_COC" --channels "$SLACK_CHANNELS" --port $PORT $SLACK_SUBDOMAIN $SLACK_API_TOKEN $GOOGLE_CAPTCHA_SECRET $GOOGLE_CAPTCHA_SITEKEY
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# slackin
# Slackin with Google Recaptcha integration

A little server that enables public access to a Slack server. Like Freenode, but on Slack.

Expand All @@ -8,6 +8,7 @@ It provides
emails and receive an invite (`https://slack.yourdomain.com`)
- An `<iframe>` badge to embed on any website
that shows connected users in *realtime* with socket.io.
- Google reCaptcha integration to discourage spammers from hitting the invite limit
- A SVG badge that works well from static mediums
(like GitHub README pages)

Expand All @@ -19,7 +20,7 @@ Read more about the [motivations and history](http://rauchg.com/slackin) behind

#### Heroku

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/rauchg/slackin/tree/master)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/avatsaev/slackin/tree/master)

#### Azure

Expand All @@ -32,7 +33,7 @@ Read more about the [motivations and history](http://rauchg.com/slackin) behind
#### Cloud Foundry

##### IBM Bluemix
[![Deploy to Bluemix](https://bluemix.net/deploy/button.png)](https://bluemix.net/deploy?repository=https://github.com/rauchg/slackin)
[![Deploy to Bluemix](https://bluemix.net/deploy/button.png)](https://bluemix.net/deploy?repository=https://github.com/avatsaev/slackin)

For other CloudFoundry providers, [follow these instructions.](https://github.com/pivotal-cf/slackin/wiki/Cloud-Foundry)

Expand Down
8 changes: 8 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
"description": "A Slack API token (find it on https://api.slack.com/web)",
"required": true
},
"GOOGLE_CAPTCHA_SECRET": {
"description": "Google captcha secret key",
"required": true
},
"GOOGLE_CAPTCHA_SITEKEY": {
"description": "Google captcha site key",
"required": true
},
"SLACK_COC": {
"description": "A URL to a Code of Conduct people must agree on before joining.",
"required": false
Expand Down
11 changes: 9 additions & 2 deletions bin/slackin
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,29 @@ args
.option(['?', 'help'], 'Show the usage information')

var flags = args.parse(process.argv, {
value: '<team-id> <api-token>',
value: '<team-id> <api-token> <google-captcha-secret> <google-captcha-sitekey>',
help: false
})

var org = args.sub[0] || process.env.SLACK_SUBDOMAIN
var token = args.sub[1] || process.env.SLACK_API_TOKEN

var gcaptcha_secret = args.sub[2] || process.env.GOOGLE_CAPTCHA_SECRET
var gcaptcha_sitekey = args.sub[3] || process.env.GOOGLE_CAPTCHA_SITEKEY



if (flags.help) {
args.showHelp()
}

if (!org || !token) {
if (!org || !token || !gcaptcha_sitekey || !gcaptcha_secret) {
args.showHelp()
} else {
flags.org = org
flags.token = token
flags.gcaptcha_secret = gcaptcha_secret
flags.gcaptcha_sitekey = gcaptcha_sitekey
}

var port = flags.port
Expand Down
250 changes: 250 additions & 0 deletions dist/assets/badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
(function (){

// give up and resort to `target=_blank`
// if we're not modern enough
if (!document.body.getBoundingClientRect
|| !document.body.querySelectorAll
|| !window.postMessage) {
return
}

// search for a script tag pointing to slackin.js
function search (){
var replaced = 0
var scripts = document.querySelectorAll('script')
var script
for (var i = 0; i < scripts.length; i++) {
script = scripts[i]
if (!script.src) continue
if (/\/slackin\.js(\?.*)?$/.test(script.src)) {
// replace script with iframe
replace(script)

// we abort the search for subsequent
// slackin.js executions to exhaust
// the queue
return true
}
}
}

var LARGE // boolean for large/small mode, from query param

// replace the script tag with an iframe
function replace (script){
var parent = script.parentNode
if (!parent) return

LARGE = /\?large/.test(script.src)
var iframe = document.createElement('iframe')
var iframePath = '/iframe' + (LARGE ? '?large' : '')
iframe.src = script.src.replace(/\/slackin\.js.*/, iframePath)
iframe.style.borderWidth = 0
iframe.className = '__slackin'

// a decent aproximation that we adjust later
// once we have the knowledge of the actual
// numbers of users, based on a user count
// of 3 digits by 3 digits
iframe.style.width = (LARGE ? 190 : 140) + 'px'

// height depends on target size
iframe.style.height = (LARGE ? 30 : 20) + 'px'

// hidden by default to avoid flicker
iframe.style.visibility = 'hidden'

parent.insertBefore(iframe, script)
parent.removeChild(script)

// setup iframe RPC
iframe.onload = function (){
setup(iframe)
}
}

// setup an "RPC" channel between iframe and us
function setup (iframe){
var id = Math.random() * (1 << 24) | 0
iframe.contentWindow.postMessage('slackin:' + id, '*')
window.addEventListener('message', function (e){
if (typeof e.data !== 'string') return

// show dialog upon click
if ('slackin-click:' + id === e.data) {
showDialog(iframe)
}

// update width
var wp = 'slackin-width:' + id + ':'
if (wp === e.data.substr(0, wp.length)) {
var width = e.data.substr(wp.length)
iframe.style.width = width + 'px'

// ensure it's shown (since first time hidden)
iframe.style.visibility = 'visible'
}

// redirect to URL
var redir = 'slackin-redirect:' + id + ':'
if (redir === e.data.substr(0, redir.length)) {
location.href = e.data.substr(redir.length)
}
})
}

// show the dialog around the iframe
// by, yes, creating a new iframe
var showing = false
function showDialog (iframe){
if (showing) return
showing = true

if (LARGE) {
unitSize = '14px'
arrowHeight = 13
} else {
unitSize = '10px'
arrowHeight = 9
}

// container div
var div = document.createElement('div')
div.className = '__slackin'
div.style.fontSize = unitSize
div.style.border = '.1em solid #D6D6D6'
div.style.padding = '0'
div.style.margin = '0'
div.style.lineHeight = '0'
div.style.backgroundColor = '#FAFAFA'
div.style.width = '25em'
div.style.height = '15.5em'
div.style.position = 'absolute'
div.style.left = '-10000px'
div.style.top = '-10000px'
div.style.borderRadius = '.4em'
div.style.padding = '.4em'
div.style.boxSizing = 'content-box'

// new iframe
var ni = document.createElement('iframe')
ni.className = '__slackin'
ni.style.width = '25em'
ni.style.height = '15.5em'
ni.style.borderWidth = 0
ni.src = iframe.src.replace('iframe', 'iframe/dialog')
ni.onload = function (){
setup(ni)
window.addEventListener('scroll', dposition)
window.addEventListener('resize', dposition)
position()
}

// arrows
var a1 = document.createElement('div')
var a2 = document.createElement('div');
[a1, a2].forEach(function (a){
a.style.border = 'solid transparent'
a.style.pointerEvents = 'none'
a.style.width = '0'
a.style.height = '0'
a.style.margin = '0'
a.style.padding = '0'
a.style.position = 'absolute'
a.style.display = 'inline'
})

a1.style.borderColor = 'rgba(214, 214, 214, 0)'
a2.style.borderColor = 'rgba(250, 250, 250, 0)'

a1.style.borderWidth = '.7em'
a1.style.marginLeft = '-.65em'
a2.style.borderWidth = '.6em'
a2.style.marginLeft = '-.6em'

// append
div.appendChild(a1)
div.appendChild(a2)
div.appendChild(ni)
document.body.appendChild(div)

function position (){
[div, a1, a2].forEach(function (el){
el.style.left = ''
el.style.right = ''
el.style.bottom = ''
el.style.top = ''
})

var divPos = div.getBoundingClientRect()
var iframePos = iframe.getBoundingClientRect()
var divHeight = divPos.height + arrowHeight

var st = document.body.scrollTop
var sl = document.body.scrollLeft
var iw = window.innerWidth
var ih = window.innerHeight
var iframeTop = iframePos.top + st
var iframeLeft = iframePos.left + sl

// position vertically / arrows
if (st + iframePos.bottom + divHeight > st + ih) {
div.style.top = (iframeTop - divHeight) + 'px'
a1.style.top = a2.style.top = '100%'

a1.style.borderBottomColor = 'rgba(214, 214, 214, 0)'
a2.style.borderBottomColor = 'rgba(250, 250, 250, 0)'
a1.style.borderTopColor = '#d6d6d6'
a2.style.borderTopColor = '#fafafa'
} else {
div.style.top = (iframeTop + iframePos.height + arrowHeight) + 'px'
a1.style.bottom = a2.style.bottom = '100%'

a1.style.borderTopColor = 'rgba(214, 214, 214, 0)'
a2.style.borderTopColor = 'rgba(250, 250, 250, 0)'
a1.style.borderBottomColor = '#d6d6d6'
a2.style.borderBottomColor = '#fafafa'
}

// position horizontally
var left = iframePos.left
+ Math.round(iframePos.width / 2)
- Math.round(divPos.width / 2)
if (left < sl) left = sl
if (left + divPos.width > sl + iw) {
left = sl + iw - divPos.width
}
div.style.left = left + 'px'

a1.style.left =
a2.style.left = (iframeLeft - left + Math.round(iframePos.width / 2)) + 'px'
}

// debounced positionining
var timer
function dposition (){
clearTimeout(timer)
timer = setTimeout(position, 100)
}

function hide (){
showing = false
window.removeEventListener('scroll', dposition)
window.removeEventListener('resize', dposition)
document.body.removeChild(div)
document.documentElement.removeEventListener('click', click, true)
}

function click (ev){
if ('__slackin' != ev.target.className) {
hide()
}
}

document.documentElement.addEventListener('click', click, true)
}

var found = search()
if (!found) setTimeout(search, 5000)

})()
1 change: 1 addition & 0 deletions dist/assets/checkbox-checked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added dist/assets/checkbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions dist/assets/checkbox.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading