Skip to content

Commit

Permalink
initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
rauchg committed Jan 26, 2015
0 parents commit c75f8ce
Show file tree
Hide file tree
Showing 23 changed files with 2,821 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node
node_modules
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.gitignore
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

6TO5 = ./node_modules/.bin/6to5

all: node

node: lib
@mkdir -p node/assets/
@rm -rf node/assets/*
@cp -r lib/assets node/
@for path in lib/*.js; do \
file=`basename $$path`; \
$(6TO5) "lib/$$file" > "node/$$file"; \
done
105 changes: 105 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

# slackin

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

It prompts users to join by emailing them an
invite to your organization (all of it or just
one guest channel) through the Slack API.

You get:

- A landing page you can point users to
(`http://slack.yourdomain.com`)
- An `<iframe>` badge to embed on any website
that shows connected users in *realtime* with socket.io.
- A SVG badge that works well from static mediums
(like GitHub README pages)

## How to use

### Server

`deploy to heroku button here`

To launch it:

```bash
$ npm install -g slackin
$ slackin --token "token" --org socketio
```

The available options are:

- `--port [port]` – What port to bind to (defaults to `3000`)
- `--token [token]` (required) – API token for your org. Get it
[here](https://api.slack.com/web).
- `--org [org]` (required) – Organization subdomain (//**this**.slack.com)
- `--channel [chan]` – If you want users to join *just one guest channel*
within your organization, provide it.
- `--silent` - If provided, no errors or warnings are printed out.

### Realtime Badge

[![](https://cldup.com/IaiPnDEAA6.gif)](http://slack.socket.io)

```html
<script async defer src="http://slackin.yourhost.com/slackin.js"></script>
```

or for the large version, append `?large`:

```html
<script async defer src="http://slackin.yourhost.com/slackin.js?large"></script>
```

### SVG

[![](https://cldup.com/jWUT4QFLnq.png)](http://slack.socket.io)

```html
<img src="http://slackin.yourhost.com/badge.svg">
```

### Landing page

[![](https://cldup.com/WIbawiqp0Q.png)](http://slack.socket.io)

Point to `http://slackin.yourhost.com`.

**Note:** the image for the logo of the landing page
is retrieved from the Slack API. If your organization
doesn't have one configured, it won't be shown.

## API

Requiring `slackin` as a module will return
a `Function` that creates a `HTTP.Server` instance
that you can manipulate.

```js
require('slackin')({
token: 'yourtoken', // required
interval: 1000,
org: 'your-slack-subdomain', // required
channel: 'channel' // for single channel mode,
silent: false // suppresses warnings
}).listen(3000);
```

This will show response times from Slack and how many
online users you have on the console.

By default logging is enabled.

## Credits

- The SVG badge generation was taken from the
excellent [shields](https://github.com/badges/shields) project.
- The button CSS is based on
[github-buttons](https://github.com/mdo/github-buttons).

## License

MIT
24 changes: 24 additions & 0 deletions bin/slackin
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env node

// es6 polyfills
require('6to5/register');

var pkg = require('../package');
var program = require('commander');
var slackin = require('../node');

program
.version(pkg.version)
.option('-p, --port [port]', 'Port to listen on', 3000)
.option('-t, --token [token]', 'Token (https://api.slack.com/web)')
.option('-o, --org [org]', 'Organization subdomain ({xxx}.slack.com)')
.option('-c, --channel [chan]', 'Invite users only to this channel')
.option('-i, --interval [int]', 'How frequently we poll Slack', 1000)
.option('-s, --silent', 'Do not print out warns or errors')
.parse(process.argv);

var port = program.port;
slackin(program).listen(port, function(err){
if (err) throw err;
if (!program.silent) console.log('%s – listening on *:%d', new Date, port);
});
226 changes: 226 additions & 0 deletions lib/assets/badge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
(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;
}
}
}

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

var 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 ? 171 : 114) + '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){
// 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';
}
});
}

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

// container div
var div = document.createElement('div');
div.className = '__slackin';
div.style.border = '1px solid #D6D6D6';
div.style.padding = '0';
div.style.margin = '0';
div.style.lineHeight = '0';
div.style.backgroundColor = '#FAFAFA';
div.style.width = '250px';
div.style.height = '124px';
div.style.position = 'absolute';
div.style.left = '-10000px';
div.style.borderRadius = '4px';
div.style.padding = '4px';

// new iframe
var ni = document.createElement('iframe');
ni.className = '__slackin';
ni.style.width = '250px';
ni.style.height = '124px';
ni.style.borderWidth = 0;
ni.src = iframe.src.replace(/\?.*/, '') + '/dialog';
ni.onload = function(){
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 = '7px';
a1.style.marginLeft = '-7px';
a2.style.borderWidth = '6px';
a2.style.marginLeft = '-6px';

// 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 + 9; // arrow height

var st = document.body.scrollTop;
var sl = document.body.scrollLeft;
var iw = window.innerWidth;
var ih = window.innerHeight;

// position vertically / arrows
if (iframePos.bottom + divHeight > st + ih) {
div.style.top = (iframePos.top - 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 = (iframePos.top + iframePos.height + 9) + '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 = (iframePos.left - 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);

})();
Loading

0 comments on commit c75f8ce

Please sign in to comment.