├── .gitignore ├── LICENSE ├── README.md ├── bin ├── skype-cli └── skype-zerorpc ├── lib └── skype-api.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2014 Vyacheslav Slinko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # skype-api 2 | 3 | > Node.js API to local Skype instance. 4 | 5 | ## Usage Example 6 | 7 | ```js 8 | var skypeAPI = require('skype-api'); 9 | 10 | var skype = skypeAPI(); 11 | 12 | skype.getUser('echo123', function (err, user) { 13 | console.log(user); 14 | }); 15 | ``` 16 | 17 | ## Installation 18 | 19 | ```bash 20 | npm install --save skype-api@^0.2 21 | pip install Skype4Py zerorpc gevent 22 | ``` 23 | 24 | ## API 25 | 26 | ### getChats(callback) 27 | 28 | * `callback` — `function (err, chats) {}` 29 | 30 | ```json 31 | [ 32 | { 33 | "friendlyName": "Вася Хобот", 34 | "topic": "", 35 | "name": "#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed" 36 | } 37 | ] 38 | ``` 39 | 40 | ### getChat(chatName, callback) 41 | 42 | * `chatName` — `'#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed'` 43 | * `callback` — `function (err, chat) {}` 44 | 45 | ```json 46 | { 47 | "status": "DIALOG", 48 | "bookmarked": false, 49 | "myRole": "USER", 50 | "dialogPartner": "live:vasya.hobot", 51 | "description": "", 52 | "timestamp": 1387175594, 53 | "guideLines": "", 54 | "blob": "mTuwsNpnv9Sh2ynShSr4UaAGUJH_c-3UhvY5wphXoGBORSLDZ8ZNvaqRmh2Uy3uoS4FzjGUEMU2X9fjcaJFe", 55 | "name": "#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed", 56 | "passwordHint": "", 57 | "friendlyName": "Вася Хобот", 58 | "topic": "", 59 | "myStatus": "SUBSCRIBED", 60 | "adder": "", 61 | "members": ["live:vasya.hobot", "vyacheslav.slinko"], 62 | "activityTimestamp": 1387175597, 63 | "posters": ["vyacheslav.slinko"], 64 | "activeMembers": ["live:vasya.hobot", "vyacheslav.slinko"], 65 | "type": "DIALOG", 66 | "applicants": [] 67 | } 68 | ``` 69 | 70 | ### getUser(userName, callback) 71 | 72 | * `userName` — `'live:vasya.hobot'` 73 | * `callback` — `function (err, user) {}` 74 | 75 | ```json 76 | { 77 | "province": "", 78 | "languageCode": "ru", 79 | "canLeaveVoicemail": false, 80 | "handle": "live:vasya.hobot", 81 | "countryCode": "ru", 82 | "isVoicemailCapable": false, 83 | "isAuthorized": true, 84 | "buddyStatus": 3, 85 | "phoneOffice": "", 86 | "hasCallEquipment": true, 87 | "birthday": 664232400, 88 | "moodText": "Вася. Управляй мечтой.", 89 | "timezone": 86400, 90 | "fullName": "Вася Хобот", 91 | "sex": "MALE", 92 | "aliases": [], 93 | "city": "", 94 | "about": "", 95 | "speedDial": "", 96 | "displayName": "", 97 | "language": "Russian", 98 | "isCallForwardActive": false, 99 | "country": "Russia", 100 | "richMoodText": "Вася. Управляй мечтой.", 101 | "isSkypeOutContact": false, 102 | "phoneHome": "", 103 | "numberOfAuthBuddies": 0, 104 | "phoneMobile": "", 105 | "isVideoCapable": false, 106 | "isBlocked": false, 107 | "lastOnline": 1387175597, 108 | "homepage": "вася-хобот.рф", 109 | "onlineStatus": "NA", 110 | "receivedAuthRequest": "" 111 | } 112 | ``` 113 | 114 | ### sendMessage(chatName, messageBody, callback) 115 | 116 | 117 | * `chatName` — `'#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed'` 118 | * `messageBody` — `'Hello World!'` 119 | * `callback` — `function (err, message) {}` 120 | 121 | ```json 122 | { 123 | "body": "Hello World!", 124 | "fromHandle": "vyacheslav.slinko", 125 | "id": 1464329, 126 | "chatName": "#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed" 127 | } 128 | ``` 129 | 130 | ### on('message', callback) 131 | 132 | * `callback` — `function (message) {}` 133 | 134 | ```json 135 | { 136 | "message": "Hello dude!", 137 | "user": "live:vasya.hobot", 138 | "room": "#vyacheslav.slinko/$live:vasya.hobot;9f62de6c85e3daed" 139 | } 140 | ``` 141 | -------------------------------------------------------------------------------- /bin/skype-cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * Skype API 4 | * 5 | * Copyright (c) 2013-2014 Vyacheslav Slinko 6 | * Licensed under the MIT License 7 | */ 8 | 9 | 10 | var skypeAPI = require('..'); 11 | var util = require('util'); 12 | var repl = require('repl'); 13 | 14 | 15 | var messages = []; 16 | var skype = skypeAPI(); 17 | 18 | var mapping = { 19 | 'help': function(args, callback) { 20 | callback(null, [ 21 | 'shift_messages', 22 | 'get_chats', 23 | 'get_chat CHAT_NAME', 24 | 'get_user USER_NAME', 25 | 'send_message CHAT_NAME MESSAGE' 26 | ]); 27 | }, 28 | 'shift_messages': function(args, callback) { 29 | callback(null, messages); 30 | messages = []; 31 | }, 32 | 'get_chats': function(args, callback) { 33 | skype.getChats(callback); 34 | }, 35 | 'get_chat': function(args, callback) { 36 | skype.getChat(args[0], callback); 37 | }, 38 | 'get_user': function(args, callback) { 39 | skype.getUser(args[0], callback); 40 | }, 41 | 'send_message': function(args, callback) { 42 | var chatName = args.shift(); 43 | skype.sendMessage(chatName, args.join(' '), callback); 44 | } 45 | }; 46 | 47 | skype.on('message', function(message) { 48 | messages.push(message); 49 | }); 50 | 51 | skype.on('error', function(err) { 52 | console.error(err); 53 | process.exit(1); 54 | }); 55 | 56 | skype.connect(); 57 | 58 | repl.start({ 59 | prompt: 'skype> ', 60 | 61 | eval: function(cmd, context, filename, callback) { 62 | var args = cmd.slice(1, -2).split(' '); 63 | var method = args.shift(); 64 | 65 | if (method.length === 0) { 66 | return callback(); 67 | } 68 | 69 | if (mapping[method]) { 70 | mapping[method](args, function(err, res) { 71 | if (err) { 72 | callback(new Error(err.message)); 73 | } else { 74 | callback(null, res); 75 | } 76 | }); 77 | } else { 78 | callback(new Error('Unknown function ' + method)); 79 | } 80 | } 81 | }); 82 | -------------------------------------------------------------------------------- /bin/skype-zerorpc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # Skype API 4 | # 5 | # Copyright (c) 2013-2014 Vyacheslav Slinko 6 | # Licensed under the MIT License 7 | # 8 | 9 | 10 | import os 11 | import sys 12 | import time 13 | import Skype4Py 14 | import zerorpc 15 | import gevent 16 | 17 | 18 | def user_handle_mapper(user): 19 | return user.Handle 20 | 21 | 22 | def user_mapper(user): 23 | return { 24 | 'about': user.About, 25 | 'aliases': user.Aliases, 26 | 'birthday': time.mktime(user.Birthday.timetuple()) if user.Birthday else None, 27 | 'buddyStatus': user.BuddyStatus, 28 | 'canLeaveVoicemail': user.CanLeaveVoicemail, 29 | 'city': user.City, 30 | 'country': user.Country, 31 | 'countryCode': user.CountryCode, 32 | 'displayName': user.DisplayName, 33 | 'fullName': user.FullName, 34 | 'handle': user.Handle, 35 | 'hasCallEquipment': user.HasCallEquipment, 36 | 'homepage': user.Homepage, 37 | 'isAuthorized': user.IsAuthorized, 38 | 'isBlocked': user.IsBlocked, 39 | 'isCallForwardActive': user.IsCallForwardActive, 40 | 'isSkypeOutContact': user.IsSkypeOutContact, 41 | 'isVideoCapable': user.IsVideoCapable, 42 | 'isVoicemailCapable': user.IsVoicemailCapable, 43 | 'language': user.Language, 44 | 'languageCode': user.LanguageCode, 45 | 'lastOnline': user.LastOnline, 46 | 'moodText': user.MoodText, 47 | 'numberOfAuthBuddies': user.NumberOfAuthBuddies, 48 | 'onlineStatus': user.OnlineStatus, 49 | 'phoneHome': user.PhoneHome, 50 | 'phoneMobile': user.PhoneMobile, 51 | 'phoneOffice': user.PhoneOffice, 52 | 'province': user.Province, 53 | 'receivedAuthRequest': user.ReceivedAuthRequest, 54 | 'richMoodText': user.RichMoodText, 55 | 'sex': user.Sex, 56 | 'speedDial': user.SpeedDial, 57 | 'timezone': user.Timezone 58 | } 59 | 60 | 61 | def short_chat_mapper(chat): 62 | return { 63 | 'friendlyName': chat.FriendlyName, 64 | 'name': chat.Name, 65 | 'topic': chat.Topic 66 | } 67 | 68 | 69 | def chat_mapper(chat): 70 | return { 71 | 'activeMembers': map(user_handle_mapper, chat.ActiveMembers), 72 | 'activityTimestamp': chat.ActivityTimestamp, 73 | 'adder': user_handle_mapper(chat.Adder), 74 | 'applicants': map(user_handle_mapper, chat.Applicants), 75 | 'blob': chat.Blob, 76 | 'bookmarked': chat.Bookmarked, 77 | 'description': chat.Description, 78 | 'dialogPartner': chat.DialogPartner, 79 | 'friendlyName': chat.FriendlyName, 80 | 'guideLines': chat.GuideLines, 81 | 'members': map(user_handle_mapper, chat.Members), 82 | 'myRole': chat.MyRole, 83 | 'myStatus': chat.MyStatus, 84 | 'name': chat.Name, 85 | 'passwordHint': chat.PasswordHint, 86 | 'posters': map(user_handle_mapper, chat.Posters), 87 | 'status': chat.Status, 88 | 'timestamp': chat.Timestamp, 89 | 'topic': chat.Topic, 90 | 'type': chat.Type 91 | } 92 | 93 | 94 | def short_message_mapper(message): 95 | return { 96 | 'id': message.Id, 97 | 'body': message.Body, 98 | 'fromHandle': message.FromHandle, 99 | 'chatName': message.ChatName 100 | } 101 | 102 | 103 | class SkypeRPC(object): 104 | def __init__(self, skype): 105 | self.skype = skype 106 | self.skype.OnMessageStatus = self.on_message 107 | self.message_subscribers = set() 108 | 109 | @zerorpc.stream 110 | def stream_messages(self): 111 | try: 112 | queue = gevent.queue.Queue() 113 | self.message_subscribers.add(queue) 114 | for message in queue: 115 | yield message 116 | finally: 117 | self.message_subscribers.remove(queue) 118 | 119 | def on_message(self, message, status): 120 | if status == Skype4Py.cmsReceived: 121 | for queue in self.message_subscribers: 122 | queue.put({ 123 | 'user': message.Sender.Handle, 124 | 'message': message.Body, 125 | 'room': message.Chat.Name 126 | }) 127 | 128 | def get_chats(self): 129 | return map(short_chat_mapper, self.skype.Chats) 130 | 131 | def get_chat(self, chat_name): 132 | return chat_mapper(self.skype.Chat(chat_name)) 133 | 134 | def get_user(self, user_name): 135 | return user_mapper(self.skype.User(user_name)) 136 | 137 | def send_message(self, chat_name, message_text): 138 | chat = self.skype.Chat(chat_name) 139 | return short_message_mapper(chat.SendMessage(message_text)) 140 | 141 | 142 | def main(): 143 | if sys.platform.startswith('linux'): 144 | skype = Skype4Py.Skype(Transport=os.environ.get('SKYPE_API_TRANSPORT', 'x11')) 145 | else: 146 | skype = Skype4Py.Skype() 147 | 148 | skype.Attach() 149 | 150 | rpc_server = zerorpc.Server(SkypeRPC(skype)) 151 | rpc_server.bind('tcp://127.0.0.1:4243') 152 | rpc_server.run() 153 | 154 | 155 | if __name__ == '__main__': 156 | main() 157 | -------------------------------------------------------------------------------- /lib/skype-api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Skype API 3 | * 4 | * Copyright (c) 2013-2014 Vyacheslav Slinko 5 | * Licensed under the MIT License 6 | */ 7 | 8 | 9 | var EventEmitter = require('events').EventEmitter; 10 | var zerorpc = require('zerorpc'); 11 | var spawn = require('child_process').spawn; 12 | var path = require('path'); 13 | var util = require('util'); 14 | 15 | 16 | function SkypeClient(pythonPath) { 17 | EventEmitter.apply(this, arguments); 18 | this.connected = false; 19 | this._exitListener = function() { 20 | this.close(); 21 | process.exit(); 22 | }.bind(this); 23 | } 24 | 25 | 26 | util.inherits(SkypeClient, EventEmitter); 27 | 28 | 29 | SkypeClient.prototype.getChats = function(callback) { 30 | this.client.invoke('get_chats', callback); 31 | }; 32 | 33 | 34 | SkypeClient.prototype.getChat = function(chatName, callback) { 35 | this.client.invoke('get_chat', chatName, callback); 36 | }; 37 | 38 | 39 | SkypeClient.prototype.getUser = function(userName, callback) { 40 | this.client.invoke('get_user', userName, callback); 41 | }; 42 | 43 | 44 | SkypeClient.prototype.sendMessage = function(chatName, message, callback) { 45 | this.client.invoke('send_message', chatName, message, callback); 46 | }; 47 | 48 | 49 | SkypeClient.prototype.connect = function() { 50 | if (!this.connected) { 51 | this.connected = true; 52 | this._runBackend(); 53 | this._connectToBacked(); 54 | this._subscribeMessages(); 55 | process.on('exit', this._exitListener); 56 | process.on('SIGINT', this._exitListener); 57 | } 58 | }; 59 | 60 | 61 | SkypeClient.prototype.close = function() { 62 | if (this.connected) { 63 | this.connected = false; 64 | this.backend.kill(); 65 | this.client.close(); 66 | process.removeListener('exit', this._exitListener); 67 | process.removeListener('SIGINT', this._exitListener); 68 | } 69 | }; 70 | 71 | 72 | SkypeClient.prototype._runBackend = function() { 73 | var command = 'python', 74 | args = [path.join(__dirname, '..', 'bin', 'skype-zerorpc')], 75 | options = {}; 76 | 77 | if (process.platform === 'darwin') { 78 | command = 'arch'; 79 | args = ['-i386', 'python'].concat(args); 80 | options.env = {VERSIONER_PYTHON_PREFER_32_BIT: 'yes'}; 81 | } 82 | 83 | this.backend = spawn(command, args, options); 84 | this.backend.on('error', this.emit.bind(this, 'error')); 85 | }; 86 | 87 | 88 | SkypeClient.prototype._connectToBacked = function() { 89 | this.client = new zerorpc.Client(); 90 | this.client.connect('tcp://127.0.0.1:4243'); 91 | this.client.on('error', this.emit.bind(this, 'error')); 92 | }; 93 | 94 | 95 | SkypeClient.prototype._subscribeMessages = function() { 96 | this.client.invoke('stream_messages', function(err, message) { 97 | if (err) { 98 | this._subscribeMessages(); 99 | return; 100 | } 101 | this.emit('message', message); 102 | }.bind(this)); 103 | }; 104 | 105 | 106 | function createClient() { 107 | return new SkypeClient(); 108 | } 109 | 110 | 111 | module.exports = createClient; 112 | module.exports.SkypeClient = SkypeClient; 113 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skype-api", 3 | "version": "0.2.2", 4 | "description": "Skype API", 5 | "keywords": ["skype"], 6 | "homepage": "https://github.com/vslinko/skype-api", 7 | "bugs": { 8 | "url": "https://github.com/vslinko/skype-api/issues", 9 | "email": "vyacheslav.slinko@gmail.com" 10 | }, 11 | "license": "MIT", 12 | "author": { 13 | "name": "Vyacheslav Slinko", 14 | "email": "vyacheslav.slinko@gmail.com" 15 | }, 16 | "main": "lib/skype-api", 17 | "bin": { 18 | "skype-zerorpc": "bin/skype-zerorpc", 19 | "skype-cli": "bin/skype-cli" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com/vslinko/skype-api.git" 24 | }, 25 | "dependencies": { 26 | "zerorpc": "^0.9" 27 | } 28 | } 29 | --------------------------------------------------------------------------------