Skip to content

Commit

Permalink
slack plugin added
Browse files Browse the repository at this point in the history
  • Loading branch information
Shigeru Owada committed Oct 24, 2017
1 parent e08a5f7 commit 542d3b1
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
Expand Up @@ -191,6 +191,25 @@ will obtain OperatingState of a light and an airconditioner at once. Note that t

PropertyID cannot accept regular expression (because it can easily be many!)

## Slack Plugin

Slack plugin works after the bot token is specified to the settings. The token can be generated by:

1. Log in to your slack team in your browser.
2. Access **https://[Your Team].slack.com/apps/A0F7YS25R-bots**
3. Click 'Add Configuration' link
4. Set bot name (We recommend 'nanogw-bot') and click 'Add Bot Integration'
5. The token is shown in Integration Settings => API Token. Copy-paste it to NanoGW plugin's setting.
6. Add this bot to your favorite channel(s).

### GET|POST /v1/slack/post
with the parameter **text** to post to slack

### SUB /v1/slack/[free string]

If there is a mention or a private message to this bot, the first word is recognized as a command and any following string becomes the parameter.
For example, if the API client subscribes **/v1/slack/hello**, a string is published when there is a mention such as **@nanogw-bot hello slack!**

## Database Plugin

Database plugin provides an API for simple key-value database within GW.
Expand Down Expand Up @@ -282,6 +301,7 @@ Currently, v2 API should be manually designed by Node-RED. Please open /apps.htm
**npm**
[arped](https://www.npmjs.com/package/arped)
[body-parser](https://www.npmjs.com/package/body-parser)
[botkit](https://www.npmjs.com/package/botkit)
[echonet-lite](https://www.npmjs.com/package/echonet-lite)
[express](https://www.npmjs.com/package/express)
[inpath](https://www.npmjs.com/package/inpath)
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -6,6 +6,7 @@
"dependencies": {
"arped": "file:./local-npm/node-arped",
"body-parser": "^1.17.2",
"botkit": "^0.6.4",
"cryptico": "^1.0.2",
"echonet-lite": "0.0.19",
"express": "^4.15.2",
Expand Down
158 changes: 158 additions & 0 deletions v1/plugins/slack/index.js
@@ -0,0 +1,158 @@
var fs = require('fs');
const REQ_TIMEOUT = 30*1000 ;

let pluginInterface ;
let log = console.log ;

let slackBot ;

exports.init = function(pi){
pluginInterface = pi ;
log = pluginInterface.log ;

function initSlack(){
try {
const SLACK_TOKEN = pluginInterface.localStorage.getItem('bottoken',null) ;
if( SLACK_TOKEN == null )
return {error:'Please set Slack bot API token first.'} ;
const Botkit = require('botkit');
const controller = Botkit.slackbot();
slackBot = controller.spawn({
token: SLACK_TOKEN
}).startRTM(function(err,bot,payload) {
if (err){
log('Could not connect to Slack');
return ;
}
slackBot = bot ;

controller.hears([''],'direct_message,direct_mention,mention', (bot, message) => {
const cmd = message.text.split(' ')[0] ;
const params = message.text.slice(cmd.length).trim() ;
log(`Publish to topic ${cmd} : ${params}`) ;
pluginInterface.publish( cmd , {params:params}) ;
});
});
} catch( e ){
return {error:'Please set Slack bot API token first.'}
}
}


pluginInterface.on('SettingsUpdated' , newSettings =>{
if( newSettings.bottoken != null ){
pluginInterface.localStorage.setItem('bottoken',newSettings.bottoken) ;
newSettings.bottoken = '' ; // Keep it secret
initSlack() ;
}
} ) ;

initSlack() ;

return onProcCall ;
} ;

function onProcCall( method , path , args ){
switch(method){
case 'GET' :
if( path == '' ){
let re = {post:{text:'[TEXT TO SAY]'}} ;
if( args && args.option === 'true' ){
re.post.option = { doc:{ short:'Bot to say something' } } ;
}
return re ;
}
// break ; proceed to POST
case 'POST' :
if( path != 'post')
return {error: `path ${path} is not supported.`} ;
if( args.text == null || args.text == '' )
return {error: `No text to say.`} ;
if( !slackBot )
return {error: `Slack token is not properly set.`} ;

return new Promise((ac,rj)=>{
getChannelsList().then(channels=>{
channels.forEach(channel=>{
slackBot.say({
text : args.text
,channel: channel.id
}) ;
});
ac({success:'Successfully posted to channels ['+channels.map(ch=>ch.name).join(',')+']'}) ;
}).catch(rj) ;
}) ;

/*
return new Promise( (ac,rj)=>{
let API_URL ;
try {
let settings ;
try {
settings = JSON.parse( fs.readFileSync(pluginInterface.getpath()+'settings.json').toString() ) ;
if( typeof settings.APPID != 'string') throw {} ;
} catch(e){
rj({error:'No openweathermap API key is specified.\nYou can obtain your own api key by creating OpenWeatherMap account on https://home.openweathermap.org/users/sign_up'}) ;
return ;
} ;
settings = Object.assign(settings,args) ;
let settings_flat = '' ;
for( let key in settings ){
if( settings[key] == null || settings[key]=='' ){
delete settings[key] ;
continue ;
}
settings_flat += '&'+key+'='+settings[key] ;
}
API_URL = OPENWEATHERMAP_BASE_URL+path+'?'+settings_flat.slice(1) ; //remove first &
if( settings.q == null && (settings.lat == null || settings.lon == null ) )
rj({error:'No position data specified.',api:API_URL }) ;
http.get(API_URL, function(res) {
res.setEncoding('utf8');
let rep_body = '' ;
res.on('data', function(str) {
rep_body += str ;
}) ;
res.on('end', function() {
try {
ac(JSON.parse(rep_body)) ;
} catch(e){ rj({error:e.toString(),api:API_URL}); };
});
})
.setTimeout(REQ_TIMEOUT)
.on('timeout', function() {
rj({error:'Request time out',api:API_URL});
}).on('error', function(e) {
rj({error:e.message,api:API_URL});
});
} catch(e){ rj({error:e.toString(),api:API_URL}); };
}) ;*/
case 'POST' :
case 'PUT' :
case 'DELETE' :
default :
return {error:`The specified method ${method} is not implemented in admin plugin.`} ;
}
}

function getChannelsList(){
if( slackBot == null ) return Promise.reject({error:'Bot is not defined yet'}) ;

return new Promise((ac,rj)=>{
slackBot.api.channels.list({},function(err,response) {
if( err ){ rj({error:err});return; }
let channels = response.channels.filter(channel=>channel.is_member&&!channel.is_archived) ;
channels = channels.map(function(channel){
let ret = {} ;
['id','name','purpose'].forEach(elem=>ret[elem]=channel[elem]) ;
return ret ;
}) ;
ac(channels) ;
});
});
}
3 changes: 3 additions & 0 deletions v1/plugins/slack/settings.json
@@ -0,0 +1,3 @@
{
"bottoken": ""
}
11 changes: 11 additions & 0 deletions v1/plugins/slack/settings_schema.json
@@ -0,0 +1,11 @@
{
"title": "Slack Settings"
,"type": "object"
,"properties": {
"bottoken": {
"title": "Slack bot token",
"type": "string",
"description": "Create a new bot on https://[Your Team].slack.com/apps/A0F7YS25R-bots and input the generated API token above."
}
}
}

0 comments on commit 542d3b1

Please sign in to comment.