├── .gitignore ├── Dockerfile ├── README.md ├── index.js ├── package.json └── samples └── hello /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY package.json ./package.json 6 | RUN npm install && rm -rf /tmp/npm-* 7 | COPY . ./ 8 | 9 | ENV PATH=/opt/bin:$PATH 10 | ENV NODE_ENV=production 11 | ENV BIND_HOST=:: BIND_PORT=3001 12 | 13 | ENTRYPOINT [ "npm", "start" ] 14 | EXPOSE 3001 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TTTFI 2 | 3 | [![npm](https://img.shields.io/npm/v/tttfi.svg?maxAge=86400)](https://www.npmjs.com/package/tttfi) 4 | 5 | Middleware for IFTTT. 6 | 7 | ## Usage 8 | 9 | TTTFI picks up data from IFTTT webhook, pass data through scripts and send output of script back to IFTTT. 10 | 11 | **STEP1** Prepare scripts: 12 | 13 | Let's write a simple hello world in shell (you can find that in samples folder). 14 | 15 | ```sh 16 | #!/bin/sh 17 | 18 | # Read body from stdin 19 | read NAME 20 | 21 | # Print response to IFTTT 22 | cat << EOF 23 | {"value1": "Hello, $NAME!"} 24 | EOF 25 | ``` 26 | 27 | **STEP2** Run docker image: 28 | 29 | ``` 30 | docker run -e API_KEY= \ 31 | -e SECRET_TOKEN= \ 32 | -v `pwd`/samples:/opt/bin \ 33 | -p 3001:3001 \ 34 | --name tttfi \ 35 | kamikat/tttfi 36 | ``` 37 | 38 | NOTE: Pick up a random string as your `SECRET_TOKEN`. 39 | 40 | **STEP3** Setup trigger: 41 | 42 | 1. Create a webhook trigger 43 | 2. Set **Event Name** to `hello` (name of the script) 44 | 3. Choose any action service 45 | 4. Click **Add ingredient** and select `Value1` on any field of the service that supports it 46 | 47 | Test your configuration by sending some data using curl 48 | 49 | ``` 50 | echo 'world' | curl -XPOST -d- https://your-domain.com/hello/secret/ 51 | ``` 52 | 53 | **STEP4** Setup action: 54 | 55 | 1. Create trigger with any service (Telegram for example) 56 | 2. Choose **Webhooks** service to create an action 57 | 3. Set the url (for example `https://your-domain.com/hello/secret/`) 58 | 4. Set **Method** to `POST` and **Content Type** to `text/plain` (the `hello` event requires simple text) 59 | 5. Add any ingredient to **body** 60 | 61 | ## Scripting with Python/Perl/Go... 62 | 63 | Scripts written in python or other platform can failed to start because they're not installed in container. 64 | 65 | Add required package after start the container: 66 | 67 | ``` 68 | docker exec tttfi apk --no-cache add 69 | ``` 70 | 71 | Or run tttfi natively (see next section) 72 | 73 | ## Native server 74 | 75 | TTTFI can run natively on *nix with ES6 compatible Node.js. 76 | 77 | ``` 78 | npm install -g tttfi 79 | ``` 80 | 81 | Run TTTFI: 82 | 83 | ``` 84 | PATH=:$PATH API_KEY= SECRET_TOKEN= tttfi 85 | ``` 86 | 87 | Be careful that it executes ANYTHING in your `$PATH` with correct secret token. 88 | 89 | ## License 90 | 91 | (The MIT License) 92 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var { spawn } = require('child_process'); 4 | var express = require('express'); 5 | var parser = require('body-parser'); 6 | var request = require('request'); 7 | var debug = require('debug'); 8 | 9 | var createApp = (function({ apiKey, secret }) { 10 | 11 | var app = express(); 12 | 13 | app.use(parser.raw({ type: '*/*' })); 14 | 15 | app.post('/:event_name/secret/:secret', (req, res) => { 16 | 17 | // Ensure client is trust worthy (use HTTPS please) 18 | if (req.params.secret != secret) { 19 | return res.status(403).send(); 20 | } 21 | 22 | var output = []; 23 | var cmd = req.params.event_name; 24 | var child = spawn('/bin/sh', ['-c', cmd], { stdio: 'pipe' }); 25 | var DEBUG = debug('ttt'); 26 | 27 | DEBUG(`Process (PID ${child.pid}) created for "${cmd}".`) 28 | 29 | child.on('error', (err) => { 30 | DEBUG(`Child process failed with error`); 31 | console.error(err); 32 | }); 33 | 34 | child.on('close', (code) => { 35 | DEBUG(`Process (PID ${child.pid}) exited with code ${code}.`); 36 | if (code == 0) { 37 | var data = output.join(''); 38 | DEBUG(`Received ${data.length} bytes from process.`); 39 | try { 40 | var json = JSON.parse(data); 41 | } catch (e) { 42 | DEBUG(`Invalid JSON data. Transmission aborted.`); 43 | return; 44 | } 45 | var jsonString = JSON.stringify(json); 46 | DEBUG(`Sending response body (${jsonString.length} bytes) as event data of "${cmd}".`) 47 | request.post({ 48 | url: `https://maker.ifttt.com/trigger/${cmd}/with/key/${apiKey}`, 49 | json: true, 50 | body: json 51 | }) 52 | .on('response', (res0) => { 53 | if (res0.statusCode == 200) { 54 | DEBUG(`Response body sent successfully.`) 55 | } else { 56 | DEBUG(`Failed to send response (code ${res0.statusCode}).`) 57 | } 58 | }); 59 | } 60 | }); 61 | 62 | child.stdout.on('data', (data) => { 63 | output.push(data); 64 | }); 65 | 66 | DEBUG(`Sending request body (${req.body.length} bytes)...`); 67 | child.stdin.write(req.body); 68 | child.stdin.end(); 69 | DEBUG(`Sent.`); 70 | 71 | return res.status(200).send(); 72 | }); 73 | 74 | return app; 75 | }); 76 | 77 | (function ({ host, port, app }) { 78 | var server = app.listen(port, host, function () { 79 | console.log('Server listening at http://%s:%s', server.address().address, server.address().port); 80 | }); 81 | })({ 82 | host: process.env['BIND_HOST'] || 'localhost', 83 | port: process.env['BIND_PORT'] || '3001', 84 | app : createApp({ 85 | apiKey: process.env['API_KEY'], 86 | secret: process.env['SECRET_TOKEN'] 87 | }) 88 | }); 89 | 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tttfi", 3 | "version": "1.0.0", 4 | "description": "Middleware for IFTTT", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "DEBUG=ttt supervisor index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "bin": { 11 | "tttfi": "index.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/kamikat/tttfi.git" 16 | }, 17 | "keywords": [ 18 | "ifttt", 19 | "automation", 20 | "middleware" 21 | ], 22 | "author": "kamikat", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/kamikat/tttfi/issues" 26 | }, 27 | "homepage": "https://github.com/kamikat/tttfi#readme", 28 | "dependencies": { 29 | "body-parser": "^1.17.2", 30 | "debug": "^2.6.8", 31 | "express": "^4.15.3", 32 | "request": "^2.81.0", 33 | "supervisor": "^0.12.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /samples/hello: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Read body from stdin 4 | read NAME 5 | 6 | cat << EOF 7 | {"value1": "Hello, $NAME!"} 8 | EOF 9 | --------------------------------------------------------------------------------