├── README.md ├── examples ├── basic.js ├── call.js └── config.js ├── lib ├── index.js └── xmlrpcServer.js ├── package.json └── src ├── examples ├── basic.coffee ├── call.coffee └── config.coffee └── lib ├── index.coffee └── xmlrpcServer.coffee /README.md: -------------------------------------------------------------------------------- 1 | node-ifttt 2 | ========== 3 | 4 | How it works 5 | ------------ 6 | 7 | This connect middleware exposes a fake WordPress compliant XMLRPC API, so you can use it as an endpoint for an IFTTT WordPress channel. 8 | 9 | This way you can create custom IFTTT actions using the Create Post action from a WordPress channel as a proxy. 10 | 11 | Why 12 | --- 13 | 14 | WordPress channel is the only one which allows you to use custom endpoints. It would be great if IFTTT implements a Custom channel natively. 15 | 16 | Gotchas 17 | ------- 18 | 19 | This middleware must be the first one the connect middleware stack. 20 | -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | 3 | /* 4 | 1) Run this application 5 | $ node examples/basic.js 6 | 7 | 2) Simulate ifttt XMLRPC calls 8 | $ node examples/call.js 9 | 10 | 3) Check the results 11 | $ curl http://127.0.0.1:3000/ 12 | */ 13 | 14 | 15 | (function() { 16 | var api, app, config, connect, ifttt, server, store; 17 | 18 | config = require('./config'); 19 | 20 | connect = require('connect'); 21 | 22 | ifttt = require('../index'); 23 | 24 | store = []; 25 | 26 | api = ifttt(config); 27 | 28 | api.on('save_post', function(data) { 29 | return store.push(data); 30 | }); 31 | 32 | app = function(req, res) { 33 | return res.end(JSON.stringify(store)); 34 | }; 35 | 36 | server = connect(); 37 | 38 | server.use(api); 39 | 40 | server.use(app); 41 | 42 | server.listen(config.port); 43 | 44 | console.log("Listening on http://" + config.host + ":" + config.port); 45 | 46 | }).call(this); 47 | -------------------------------------------------------------------------------- /examples/call.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | var client, config, crypto, eventName, i, makeRequest, serializedData, xmlrpc, _i; 4 | 5 | config = require('./config'); 6 | 7 | xmlrpc = require('xmlrpc'); 8 | 9 | crypto = require('crypto'); 10 | 11 | eventName = 'save_post'; 12 | 13 | serializedData = "[title]{{Title}}[/title] [excerpt]{{Excerpt}}[/excerpt] [imageUrl]{{ImageUrl}}[/imageUrl] [tags]{{Tags}},{{Tags2}},{{Tags3}}[/tags] [url]{{Url}}[/url] [created]{{AddedAt}}[/created]"; 14 | 15 | makeRequest = function(client, config) { 16 | var params, post; 17 | post = { 18 | title: eventName, 19 | description: serializedData 20 | }; 21 | params = [1, config.user, config.password, post, true]; 22 | return client.methodCall('metaWeblog.newPost', params, function(error, value) { 23 | return console.log("metaWeblog.newPost: " + (error != null ? error : value)); 24 | }); 25 | }; 26 | 27 | console.log(config); 28 | 29 | client = xmlrpc.createClient(config); 30 | 31 | for (i = _i = 0; _i <= 0; i = ++_i) { 32 | makeRequest(client, config); 33 | } 34 | 35 | }).call(this); 36 | -------------------------------------------------------------------------------- /examples/config.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | 4 | module.exports = { 5 | port: 3000, 6 | host: 'localhost', 7 | path: '/xmlrpc.php', 8 | user: 'admin', 9 | password: '1234' 10 | }; 11 | 12 | }).call(this); 13 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | (function() { 3 | var EventEmitter, XMLRPCServer, extend, parseText; 4 | 5 | EventEmitter = require('events').EventEmitter; 6 | 7 | XMLRPCServer = require('./xmlrpcServer'); 8 | 9 | extend = function(obj, mixin) { 10 | var method, name; 11 | for (name in mixin) { 12 | method = mixin[name]; 13 | obj[name] = method; 14 | } 15 | return obj; 16 | }; 17 | 18 | parseText = function(text) { 19 | var data, match, re; 20 | re = /\[(.*)\](.*)\[\/\1\]/g; 21 | data = {}; 22 | while (match = re.exec(text)) { 23 | data[match[1]] = match[2]; 24 | } 25 | return data; 26 | }; 27 | 28 | module.exports = function(options) { 29 | var credentialsAreWrong, middleware, path, server, _ref; 30 | if (options == null) { 31 | options = {}; 32 | } 33 | path = (_ref = options.path) != null ? _ref : '/xmlrpc.php'; 34 | server = new XMLRPCServer; 35 | middleware = function(req, res, next) { 36 | if (req.method === 'POST' && req.url === path) { 37 | return server.handleMethodCall(req, res, next); 38 | } else { 39 | return next(); 40 | } 41 | }; 42 | extend(middleware, EventEmitter.prototype); 43 | credentialsAreWrong = function(params) { 44 | return !(params[1] === options.user && params[2] === options.password); 45 | }; 46 | server.on('mt.supportedMethods', function(err, params, cb) { 47 | if (credentialsAreWrong(params)) { 48 | return cb("Unauthorized"); 49 | } 50 | return cb(null, 'metaWeblog.getRecentPosts'); 51 | }); 52 | server.on('metaWeblog.getRecentPosts', function(err, params, cb) { 53 | if (credentialsAreWrong(params)) { 54 | return cb("Unauthorized"); 55 | } 56 | return cb(null, []); 57 | }); 58 | server.on('metaWeblog.newPost', function(err, params, cb) { 59 | if (credentialsAreWrong(params)) { 60 | return cb("Unauthorized"); 61 | } 62 | middleware.emit(params[3].title, parseText(params[3].description)); 63 | return cb(null, new Date().getTime()); 64 | }); 65 | return middleware; 66 | }; 67 | 68 | }).call(this); 69 | -------------------------------------------------------------------------------- /lib/xmlrpcServer.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.4.0 2 | 3 | /* 4 | Inspired by https://github.com/baalexander/node-xmlrpc/blob/master/lib/server.js 5 | */ 6 | 7 | 8 | (function() { 9 | var Deserializer, EventEmitter, Serializer, Server, 10 | __hasProp = {}.hasOwnProperty, 11 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 12 | 13 | EventEmitter = require('events').EventEmitter; 14 | 15 | Deserializer = require('xmlrpc/lib/deserializer'); 16 | 17 | Serializer = require('xmlrpc/lib/serializer'); 18 | 19 | Server = (function(_super) { 20 | 21 | __extends(Server, _super); 22 | 23 | function Server() { 24 | return Server.__super__.constructor.apply(this, arguments); 25 | } 26 | 27 | Server.prototype.handleMethodCall = function(req, res, next) { 28 | var deserializer, 29 | _this = this; 30 | deserializer = new Deserializer; 31 | return deserializer.deserializeMethodCall(req, function(error, methodName, params) { 32 | if (_this._events.hasOwnProperty(methodName)) { 33 | return _this.emit(methodName, null, params, function(error, value) { 34 | var xml; 35 | if (error === null) { 36 | xml = Serializer.serializeMethodResponse(value); 37 | } else { 38 | xml = Serializer.serializeFault(error); 39 | } 40 | res.writeHead(200, { 41 | 'Connection': 'close', 42 | 'Content-Length': xml.length, 43 | 'Content-Type': 'text/xml', 44 | 'Date': Date.toString() 45 | }); 46 | return res.end(xml); 47 | }); 48 | } else { 49 | _this.emit('NotFound', methodName, params); 50 | res.writeHead(404); 51 | return res.end; 52 | } 53 | }); 54 | }; 55 | 56 | return Server; 57 | 58 | })(EventEmitter); 59 | 60 | module.exports = Server; 61 | 62 | }).call(this); 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ifttt", 3 | "version": "0.0.1", 4 | "description": "Create your own IFTTT Actions with node.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git@github.com:fcingolani/node-ifttt.git" 11 | }, 12 | "keywords": [ 13 | "connect", 14 | "express", 15 | "middleware", 16 | "json", 17 | "ifttt" 18 | ], 19 | "main": "./lib/index.js", 20 | "author": "Federico Cingolani ", 21 | "license": "BSD", 22 | "dependencies": { 23 | "xmlrpc" : "*" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/examples/basic.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | 1) Run this application 3 | $ node examples/basic.js 4 | 5 | 2) Simulate ifttt XMLRPC calls 6 | $ node examples/call.js 7 | 8 | 3) Check the results 9 | $ curl http://127.0.0.1:3000/ 10 | ### 11 | 12 | config = require './config' 13 | connect = require 'connect' 14 | ifttt = require '../index' 15 | 16 | store = [] 17 | 18 | api = ifttt(config) 19 | api.on 'do_something', (data) -> 20 | store.push data 21 | 22 | app = (req, res)-> 23 | res.end JSON.stringify store 24 | 25 | server = connect() 26 | server.use api 27 | server.use app 28 | server.listen config.port 29 | 30 | console.log("Listening on http://#{config.host}:#{config.port}"); 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/examples/call.coffee: -------------------------------------------------------------------------------- 1 | config = require './config' 2 | xmlrpc = require 'xmlrpc' 3 | crypto = require 'crypto' 4 | 5 | eventName = 'save_post' 6 | serializedData = """ 7 | [title]{{Title}}[/title] [excerpt]{{Excerpt}}[/excerpt] [imageUrl]{{ImageUrl}}[/imageUrl] [tags]{{Tags}},{{Tags2}},{{Tags3}}[/tags] [url]{{Url}}[/url] [created]{{AddedAt}}[/created] 8 | """ 9 | 10 | makeRequest = (client, config)-> 11 | post = 12 | title: eventName 13 | description: serializedData 14 | 15 | params = [ 1, config.user, config.password, post, true] 16 | 17 | client.methodCall 'metaWeblog.newPost', params, (error, value)-> 18 | console.log "metaWeblog.newPost: #{error ? value}" 19 | 20 | console.log config 21 | 22 | client = xmlrpc.createClient config 23 | 24 | makeRequest(client, config) for i in [0..0] 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/examples/config.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | port: 3000 3 | host: 'localhost' 4 | path: '/xmlrpc.php' 5 | user: 'admin' 6 | password: '1234' 7 | -------------------------------------------------------------------------------- /src/lib/index.coffee: -------------------------------------------------------------------------------- 1 | EventEmitter = require('events').EventEmitter 2 | XMLRPCServer = require './xmlrpcServer' 3 | 4 | extend = (obj, mixin) -> 5 | obj[name] = method for name, method of mixin 6 | obj 7 | 8 | parseText = (text) -> 9 | re = /\[(.*)\](.*)\[\/\1\]/g 10 | data = {} 11 | data[match[1]] = match[2] while match = re.exec(text) 12 | data 13 | 14 | module.exports = (options = {})-> 15 | path = options.path ? '/xmlrpc.php' 16 | server = new XMLRPCServer 17 | 18 | middleware = (req, res, next) -> 19 | if req.method is 'POST' and req.url is path 20 | server.handleMethodCall req, res, next 21 | else 22 | next() 23 | 24 | extend middleware, EventEmitter.prototype 25 | 26 | credentialsAreWrong = (params) -> 27 | not ( params[1] is options.user and params[2] is options.password ) 28 | 29 | server.on 'mt.supportedMethods', (err, params, cb)-> 30 | if credentialsAreWrong params 31 | return cb "Unauthorized" 32 | 33 | cb null, 'metaWeblog.getRecentPosts' 34 | 35 | server.on 'metaWeblog.getRecentPosts', (err, params, cb)-> 36 | if credentialsAreWrong params 37 | return cb "Unauthorized" 38 | 39 | cb null, [] 40 | 41 | server.on 'metaWeblog.newPost', (err, params, cb)-> 42 | if credentialsAreWrong params 43 | return cb "Unauthorized" 44 | 45 | middleware.emit params[3].title, parseText params[3].description 46 | cb null, new Date().getTime() 47 | 48 | middleware 49 | -------------------------------------------------------------------------------- /src/lib/xmlrpcServer.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Inspired by https://github.com/baalexander/node-xmlrpc/blob/master/lib/server.js 3 | ### 4 | 5 | EventEmitter = require('events').EventEmitter 6 | Deserializer = require 'xmlrpc/lib/deserializer' 7 | Serializer = require 'xmlrpc/lib/serializer' 8 | 9 | class Server extends EventEmitter 10 | 11 | handleMethodCall: (req, res, next)-> 12 | deserializer = new Deserializer 13 | deserializer.deserializeMethodCall req, (error, methodName, params)=> 14 | if @_events.hasOwnProperty methodName 15 | @emit methodName, null, params, (error, value)-> 16 | if error is null 17 | xml = Serializer.serializeMethodResponse value 18 | else 19 | xml = Serializer.serializeFault error 20 | 21 | res.writeHead 200, 22 | 'Connection': 'close' 23 | 'Content-Length': xml.length 24 | 'Content-Type': 'text/xml' 25 | 'Date': Date.toString() 26 | res.end xml 27 | 28 | else 29 | @emit 'NotFound', methodName, params 30 | res.writeHead 404 31 | res.end 32 | 33 | module.exports = Server 34 | --------------------------------------------------------------------------------