ruby-skype 0.1.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.md +60 -0
- data/VERSION +1 -0
- data/examples/call +14 -0
- data/examples/listen +14 -0
- data/ext/mkrf_conf.rb +26 -0
- data/lib/skype.rb +213 -0
- data/lib/skype/command_manager.rb +26 -0
- data/lib/skype/communication/dbus.rb +104 -0
- data/lib/skype/communication/protocol.rb +70 -0
- data/lib/skype/communication/windows.rb +200 -0
- data/lib/skype/communication/windows/win32.rb +365 -0
- data/lib/skype/data_maps.rb +7 -0
- data/lib/skype/data_maps/user_visibility.rb +18 -0
- data/lib/skype/errors/error_messages.rb +13 -0
- data/lib/skype/errors/exception_factory.rb +26 -0
- data/lib/skype/errors/general_error.rb +19 -0
- metadata +114 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Matthew Scharley
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
copy of this software and associated documentation files (the "Software"),
|
5
|
+
to deal in the Software without restriction, including without limitation
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included
|
11
|
+
in all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
Skype Public API for Ruby
|
2
|
+
=========================
|
3
|
+
|
4
|
+
**GitHub:** https://github.com/mscharley/ruby-skype
|
5
|
+
**Author:** Matthew Scharley
|
6
|
+
**Contributors:** [See contributors on GitHub][gh-contrib]
|
7
|
+
**Bugs/Support:** [Github Issues][gh-issues]
|
8
|
+
**Copyright:** 2012
|
9
|
+
**License:** [MIT license][license]
|
10
|
+
|
11
|
+
Synopsis
|
12
|
+
--------
|
13
|
+
|
14
|
+
This library is a binding for the [Skype Public API][skype-api]. Currently,
|
15
|
+
due to the Skype API being accessed in different ways on different platforms
|
16
|
+
this library will initially only be supported on Linux/DBus and Windows. The
|
17
|
+
Skype Public API is a way to hook into the Skype client released by Microsoft
|
18
|
+
and automate it. To use this library *you must have a copy of the Skype
|
19
|
+
client installed locally*. This is not a stand-alone library to access the
|
20
|
+
Skype network.
|
21
|
+
|
22
|
+
**This is very much still under development**
|
23
|
+
|
24
|
+
If you want to use this or help out, please feel free to clone/fork and play
|
25
|
+
with this, but it is prone to break or change at any time.
|
26
|
+
|
27
|
+
Installation
|
28
|
+
------------
|
29
|
+
|
30
|
+
For now, you will need to install from the git repository. Simply clone it
|
31
|
+
to wherever you like and then add it to your include path if needed.
|
32
|
+
|
33
|
+
Mac OS X Support
|
34
|
+
----------------
|
35
|
+
|
36
|
+
OS X integration should be possible, however I don't have a Mac to
|
37
|
+
test/develop with. If you want to help out, then look at the
|
38
|
+
`Skype::Communication::*` classes. `Skype::Communication::Protocol` is the
|
39
|
+
base interface you need to implement. Any help would be greatly appreciated!
|
40
|
+
|
41
|
+
Documentation
|
42
|
+
-------------
|
43
|
+
|
44
|
+
There is some documentation about the Skype API itself in the doc folder. The
|
45
|
+
[ruby-skype API documentation is available online][ruby-skype-rubydoc]. To
|
46
|
+
generate them locally, simply clone the library, then run `yard` in the repo
|
47
|
+
root. The API documentation will be available in `/doc/api` when complete.
|
48
|
+
|
49
|
+
Goals
|
50
|
+
-----
|
51
|
+
|
52
|
+
Generate a Ruby friendly front-end to access the Skype public API.
|
53
|
+
|
54
|
+
|
55
|
+
[skype-api]: http://developer.skype.com/public-api-reference
|
56
|
+
[license]: https://raw.github.com/mscharley/ruby-skype/master/LICENSE
|
57
|
+
[ruby-skype-rubydoc]: http://rubydoc.info/github/mscharley/ruby-skype/master/frames
|
58
|
+
[gh-contrib]: https://github.com/mscharley/ruby-skype/graphs/contributors
|
59
|
+
[gh-issues]: https://github.com/mscharley/ruby-skype/issues
|
60
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0.pre.2
|
data/examples/call
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.push('lib', '../lib')
|
4
|
+
|
5
|
+
require 'skype'
|
6
|
+
Skype.DEBUG = true
|
7
|
+
skype = Skype.new('ruby-skype-test')
|
8
|
+
puts "Using ruby-skype-#{Skype.VERSION}"
|
9
|
+
skype.connect
|
10
|
+
puts "Connected using protocol version #{skype.protocol_version}"
|
11
|
+
|
12
|
+
puts "Attempting to call the echo service..."
|
13
|
+
skype.send_raw_command('CALL echo123')
|
14
|
+
skype.run
|
data/examples/listen
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This program simply attaches to Skype and runs an event loop. Can be used as a starting point, or a way to
|
4
|
+
# listen for new update commands that we may not know about yet.
|
5
|
+
|
6
|
+
$:.push('lib', '../lib')
|
7
|
+
|
8
|
+
require 'skype'
|
9
|
+
Skype.DEBUG = true
|
10
|
+
puts "Using ruby-skype-#{Skype.VERSION}"
|
11
|
+
skype = Skype.new('ruby-skype-listen-in')
|
12
|
+
skype.connect
|
13
|
+
puts "Connected using protocol version #{skype.protocol_version}"
|
14
|
+
skype.run
|
data/ext/mkrf_conf.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/command.rb'
|
5
|
+
require 'rubygems/dependency_installer.rb'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Gem::Command.build_args = ARGV
|
9
|
+
rescue NoMethodError
|
10
|
+
end
|
11
|
+
|
12
|
+
inst = Gem::DependencyInstaller.new
|
13
|
+
begin
|
14
|
+
case RbConfig::CONFIG['host_os']
|
15
|
+
when /mingw|cygwin|mswin/
|
16
|
+
inst.install "ffi"
|
17
|
+
when /linux/
|
18
|
+
inst.install "ruby-dbus", '= 0.7.2'
|
19
|
+
end
|
20
|
+
rescue
|
21
|
+
exit(1)
|
22
|
+
end
|
23
|
+
|
24
|
+
f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
|
25
|
+
f.write("task :default\n")
|
26
|
+
f.close
|
data/lib/skype.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
|
2
|
+
require 'skype/command_manager'
|
3
|
+
require 'skype/errors/exception_factory'
|
4
|
+
require 'skype/data_maps/user_visibility'
|
5
|
+
|
6
|
+
# This class is the main interface between Ruby and Skype.
|
7
|
+
class Skype
|
8
|
+
private
|
9
|
+
|
10
|
+
def platform
|
11
|
+
@platform ||=
|
12
|
+
case RbConfig::CONFIG['host_os']
|
13
|
+
when /mingw|cygwin|mswin/
|
14
|
+
:windows
|
15
|
+
when /linux/
|
16
|
+
:linux
|
17
|
+
else
|
18
|
+
:unknown
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
public
|
23
|
+
|
24
|
+
# Initialises the Skype library and sets up a communication protocol, but
|
25
|
+
# doesn't connect yet.
|
26
|
+
#
|
27
|
+
# @param [String] application_name Name to use when identifying to Skype. Not
|
28
|
+
# all platforms use this value as Skype will automatically assign a name.
|
29
|
+
def initialize(application_name, communication_protocol = nil)
|
30
|
+
if communication_protocol.nil?
|
31
|
+
case platform
|
32
|
+
when :windows
|
33
|
+
require 'skype/communication/windows'
|
34
|
+
@skype = Skype::Communication::Windows.new(application_name)
|
35
|
+
when :linux
|
36
|
+
require 'skype/communication/dbus'
|
37
|
+
@skype = Skype::Communication::DBus.new(application_name)
|
38
|
+
else
|
39
|
+
puts "Unfortunately, we don't support your platform currently."
|
40
|
+
puts "Please file an issue if you think this is incorrect."
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@skype = communication_protocol
|
45
|
+
end
|
46
|
+
|
47
|
+
@command_manager = CommandManager.new(self)
|
48
|
+
@skype.add_observer(self, :received_command)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Controls whether the library should output extra debugging information or
|
52
|
+
# not. Currently controls whether we should output all network throughput.
|
53
|
+
#
|
54
|
+
# @return [Boolean]
|
55
|
+
def self.DEBUG
|
56
|
+
@debug_mode
|
57
|
+
end
|
58
|
+
|
59
|
+
# (see DEBUG)
|
60
|
+
def self.DEBUG=(value)
|
61
|
+
@debug_mode = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the currently in use version of the ruby-skype library.
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
def self.VERSION
|
68
|
+
@version ||=
|
69
|
+
IO.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp
|
70
|
+
end
|
71
|
+
|
72
|
+
# Connect to Skype and negotiate a communication channel. Blocks till the
|
73
|
+
# connection is fully established.
|
74
|
+
#
|
75
|
+
# @return [Symbol] Initial value for user_status.
|
76
|
+
def connect
|
77
|
+
@skype.connect
|
78
|
+
|
79
|
+
until @user_status
|
80
|
+
tick
|
81
|
+
sleep(0.1)
|
82
|
+
end
|
83
|
+
@user_status
|
84
|
+
end
|
85
|
+
|
86
|
+
# Are we connected to Skype?
|
87
|
+
#
|
88
|
+
# @return [Boolean]
|
89
|
+
def connected?
|
90
|
+
@skype.connected?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Execute a single run of the Skype event loop
|
94
|
+
#
|
95
|
+
# @return [void]
|
96
|
+
def tick
|
97
|
+
@skype.tick
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# Executes the Skype event loops. Doesn't return unless #quit is called.
|
102
|
+
#
|
103
|
+
# @return [void]
|
104
|
+
def run
|
105
|
+
@finished = false
|
106
|
+
until @finished
|
107
|
+
tick
|
108
|
+
sleep(0.1)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Stops the Skype event loop from running.
|
113
|
+
#
|
114
|
+
# @return [void]
|
115
|
+
def quit
|
116
|
+
@finished = true
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sends a raw command to Skype
|
120
|
+
#
|
121
|
+
# @param [String] command The command to send
|
122
|
+
# @return [String] The value returned by Skype
|
123
|
+
# @api private
|
124
|
+
def send_raw_command(command)
|
125
|
+
send_message(command)
|
126
|
+
end
|
127
|
+
|
128
|
+
#######################
|
129
|
+
### ###
|
130
|
+
### BEGIN SKYPE API ###
|
131
|
+
### ###
|
132
|
+
#######################
|
133
|
+
|
134
|
+
# Network connection status.
|
135
|
+
#
|
136
|
+
# Valid values:
|
137
|
+
#
|
138
|
+
# * `:offline`
|
139
|
+
# * `:connecting`
|
140
|
+
# * `:pausing`
|
141
|
+
# * `:online`
|
142
|
+
#
|
143
|
+
# @return [Symbol]
|
144
|
+
# @api skype
|
145
|
+
attr_reader :connection_status
|
146
|
+
|
147
|
+
# @!attribute [rw] user_status
|
148
|
+
# User visibility for the current user.
|
149
|
+
#
|
150
|
+
# Valid values:
|
151
|
+
#
|
152
|
+
# * `:unknown`
|
153
|
+
# * `:online`
|
154
|
+
# * `:offline`
|
155
|
+
# * `:skype_me`
|
156
|
+
# * `:away`
|
157
|
+
# * `:not_available`
|
158
|
+
# * `:do_not_disturb`
|
159
|
+
# * `:invisible`
|
160
|
+
#
|
161
|
+
# @return [Symbol]
|
162
|
+
# @api skype
|
163
|
+
def user_status
|
164
|
+
@user_status
|
165
|
+
end
|
166
|
+
|
167
|
+
# (see #user_status)
|
168
|
+
def user_status=(value)
|
169
|
+
send_message("SET USERSTATUS " + DataMaps::USER_VISIBILITY[value])
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
|
173
|
+
# The protocol version in use for the connection with Skype. This value is
|
174
|
+
# only reliable once connected.
|
175
|
+
#
|
176
|
+
# @return [Integer] The version number of the protocol in use.
|
177
|
+
def protocol_version
|
178
|
+
@skype.protocol_version
|
179
|
+
end
|
180
|
+
|
181
|
+
protected
|
182
|
+
|
183
|
+
# Callback for receiving updates from Skype.
|
184
|
+
#
|
185
|
+
# @param [String] command The command string to process.
|
186
|
+
# @return [void]
|
187
|
+
def received_command(command)
|
188
|
+
@command_manager.process_command(command)
|
189
|
+
puts "<= #{command}" if ::Skype.DEBUG
|
190
|
+
end
|
191
|
+
|
192
|
+
def update_user_status(status)
|
193
|
+
@user_status = status
|
194
|
+
end
|
195
|
+
|
196
|
+
def update_connection_status(status)
|
197
|
+
@connection_status = status
|
198
|
+
end
|
199
|
+
|
200
|
+
private
|
201
|
+
|
202
|
+
# Handles sending messages and handling possible errors returned by Skype
|
203
|
+
#
|
204
|
+
# @param [String] message The message to send to Skype
|
205
|
+
# @return [String] The reply from Skype or throws an exception on an error
|
206
|
+
def send_message(message)
|
207
|
+
ret = @skype.send(message)
|
208
|
+
if ret[0,6] == "ERROR "
|
209
|
+
Errors::ExceptionFactory.generate_exception(ret)
|
210
|
+
end
|
211
|
+
ret
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class Skype
|
3
|
+
# This class is used to manage updates coming back from Skype.
|
4
|
+
#
|
5
|
+
# @see #process_message
|
6
|
+
class CommandManager
|
7
|
+
def initialize(skype)
|
8
|
+
@skype = skype
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_command(command)
|
12
|
+
(command, args) = command.split(/\s+/, 2)
|
13
|
+
command = command.downcase.to_sym
|
14
|
+
|
15
|
+
self.send(command, args) if self.public_methods.include? command
|
16
|
+
end
|
17
|
+
|
18
|
+
def connstatus(args)
|
19
|
+
@skype.send :update_connection_status, args.downcase.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def userstatus(args)
|
23
|
+
@skype.send :update_user_status, DataMaps::USER_VISIBILITY.invert[args]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
|
2
|
+
require 'dbus'
|
3
|
+
require 'skype/communication/protocol'
|
4
|
+
|
5
|
+
# Monkey-patch dbus to fix an error till it makes it into a release
|
6
|
+
class DBus::Connection
|
7
|
+
def update_buffer
|
8
|
+
@buffer += @socket.read_nonblock(MSG_BUF_SIZE)
|
9
|
+
rescue EOFError
|
10
|
+
raise # the caller expects it
|
11
|
+
rescue Errno::EWOULDBLOCK
|
12
|
+
# simply fail the read if it would block
|
13
|
+
return
|
14
|
+
rescue Exception => e
|
15
|
+
puts "Oops:", e
|
16
|
+
raise if @is_tcp # why?
|
17
|
+
puts "WARNING: read_nonblock failed, falling back to .recv"
|
18
|
+
@buffer += @socket.recv(MSG_BUF_SIZE)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Skype
|
23
|
+
module Communication
|
24
|
+
# This class handles communication with Skype via DBus.
|
25
|
+
#
|
26
|
+
# This communication method is available under Linux.
|
27
|
+
class DBus
|
28
|
+
include Skype::Communication::Protocol
|
29
|
+
|
30
|
+
# DBus service name to connect to.
|
31
|
+
SKYPE_DBUS_SERVICE = 'com.Skype.API'
|
32
|
+
# DBus communication path for client -> Skype communication.
|
33
|
+
SKYPE_SERVER_PATH = '/com/Skype'
|
34
|
+
# Interface for the client -> Skype communication function.
|
35
|
+
SKYPE_SERVER_INTERFACE = 'com.Skype.API'
|
36
|
+
# DBus communication path for Skype -> client communication.
|
37
|
+
SKYPE_CLIENT_PATH = '/com/Skype/Client'
|
38
|
+
# Interface for the Skype -> client communication function.
|
39
|
+
SKYPE_CLIENT_INTERFACE = 'com.Skype.API.Client'
|
40
|
+
|
41
|
+
# Create a communication link to Skype via DBus. This initialises DBus,
|
42
|
+
# but doesn't attempt to connect to Skype yet.
|
43
|
+
#
|
44
|
+
# @see #connect.
|
45
|
+
def initialize(application_name)
|
46
|
+
@application_name = application_name
|
47
|
+
@dbus = ::DBus::SessionBus.instance
|
48
|
+
@dbus_service = @dbus.service(SKYPE_DBUS_SERVICE)
|
49
|
+
@skype = @dbus_service.object(SKYPE_SERVER_PATH)
|
50
|
+
@skype.introspect
|
51
|
+
@skype.default_iface = SKYPE_SERVER_INTERFACE
|
52
|
+
end
|
53
|
+
|
54
|
+
# Attempt to connect to Skype.
|
55
|
+
#
|
56
|
+
# For DBus, this includes exporting the client interface and then
|
57
|
+
# identifying ourselves and negotiating protocol version.
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
def connect
|
61
|
+
value = @skype.Invoke("NAME " + @application_name)
|
62
|
+
unless value == %w{OK}
|
63
|
+
Skype::Errors::ExceptionFactory.generate_exception *value
|
64
|
+
end
|
65
|
+
@protocol_version = @skype.Invoke("PROTOCOL 8")[0].
|
66
|
+
sub(/^PROTOCOL\s+/, '').to_i
|
67
|
+
@connected = true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Send a command to Skype.
|
71
|
+
#
|
72
|
+
# @param [string] message The message to send to Skype
|
73
|
+
# @return [string] The direct response from Skype
|
74
|
+
def send(message)
|
75
|
+
unless @connected
|
76
|
+
raise "You must be connected before sending data."
|
77
|
+
end
|
78
|
+
puts "-> #{message}" if ::Skype.DEBUG
|
79
|
+
ret = @skype.Invoke(message)[0]
|
80
|
+
puts "<- #{ret}" if ::Skype.DEBUG
|
81
|
+
ret
|
82
|
+
end
|
83
|
+
|
84
|
+
# Poll DBus for incoming messages. We use this method for watching for
|
85
|
+
# our messages as it is simpler, and an event loop is required no matter
|
86
|
+
# what.
|
87
|
+
#
|
88
|
+
# @return [void]
|
89
|
+
def tick
|
90
|
+
@dbus.update_buffer
|
91
|
+
@dbus.messages.each do |msg|
|
92
|
+
# Pass messages through DBus
|
93
|
+
@dbus.process(msg)
|
94
|
+
|
95
|
+
# Process messages to us.
|
96
|
+
if msg.interface == SKYPE_CLIENT_INTERFACE &&
|
97
|
+
msg.path == SKYPE_CLIENT_PATH
|
98
|
+
receive(msg.params[0])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
|
2
|
+
require 'observer'
|
3
|
+
|
4
|
+
class Skype
|
5
|
+
# You shouldn't try including this module, it is used purely for organisation.
|
6
|
+
# @private
|
7
|
+
module Communication
|
8
|
+
# Interface for the Skype::Communication::* classes. This provides basic
|
9
|
+
# input and output functionality with Skype.
|
10
|
+
#
|
11
|
+
# Includes Observable. Notifications sent will have a single string
|
12
|
+
# parameter which is the incoming command from Skype.
|
13
|
+
module Protocol
|
14
|
+
include Observable
|
15
|
+
|
16
|
+
# The protocol version supported by this communication protocol
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
attr_reader :protocol_version
|
20
|
+
|
21
|
+
# @!attribute [r] connected?
|
22
|
+
# Have we connected to Skype yet?
|
23
|
+
#
|
24
|
+
# @return [Boolean]
|
25
|
+
def connected?
|
26
|
+
@connected
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sends a message to Skype.
|
30
|
+
#
|
31
|
+
# Must be implemented by Protocol implementers.
|
32
|
+
#
|
33
|
+
# @param [string] message The message to send to Skype
|
34
|
+
# @return [string] The direct response from Skype
|
35
|
+
# @return [string] The direct response from Skype
|
36
|
+
def send(message)
|
37
|
+
raise "#send(message) must be implemented."
|
38
|
+
end
|
39
|
+
|
40
|
+
# Connects to Skype.
|
41
|
+
#
|
42
|
+
# Must be implemented by Protocol implementers.
|
43
|
+
#
|
44
|
+
# @return [void]
|
45
|
+
def connect
|
46
|
+
raise "#connect must be implemented"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Update processing. This is where you get a chance to check for input.
|
50
|
+
#
|
51
|
+
# Should be implemented by Protocol implementers, but no error is thrown
|
52
|
+
# if not.
|
53
|
+
#
|
54
|
+
# @return [void]
|
55
|
+
def tick
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Internal method to implement Observable.
|
61
|
+
#
|
62
|
+
# Protocol implementers may use this to send notifications of incoming
|
63
|
+
# commands.
|
64
|
+
def receive(message)
|
65
|
+
changed
|
66
|
+
notify_observers(message)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
|
2
|
+
require 'skype/communication/protocol'
|
3
|
+
require 'skype/communication/windows/win32'
|
4
|
+
|
5
|
+
class Skype
|
6
|
+
module Communication
|
7
|
+
# Utilises the Windows API to send and receive Window Messages to/from
|
8
|
+
# Skype.
|
9
|
+
#
|
10
|
+
# This protocol is only available on Windows and Cygwin.
|
11
|
+
class Windows
|
12
|
+
include Skype::Communication::Protocol
|
13
|
+
|
14
|
+
# Sets up access to Skype
|
15
|
+
#
|
16
|
+
# @see http://msdn.microsoft.com/en-us/library/bb384843.aspx Creating
|
17
|
+
# Win32-Based Applications
|
18
|
+
def initialize(application_name)
|
19
|
+
@application_name = application_name
|
20
|
+
|
21
|
+
# Get the message id's for the Skype Control messages
|
22
|
+
@api_discover_message_id =
|
23
|
+
Win32::RegisterWindowMessage('SkypeControlAPIDiscover')
|
24
|
+
@api_attach_message_id =
|
25
|
+
Win32::RegisterWindowMessage('SkypeControlAPIAttach')
|
26
|
+
|
27
|
+
instance = Win32::GetModuleHandle(nil)
|
28
|
+
|
29
|
+
@window_class = Win32::WNDCLASSEX.new
|
30
|
+
@window_class[:style] = Win32::CS_HREDRAW | Win32::CS_VREDRAW
|
31
|
+
@window_class[:lpfnWndProc] = method(:message_pump)
|
32
|
+
@window_class[:hInstance] = instance
|
33
|
+
@window_class[:hbrBackground] = Win32::COLOR_WINDOW
|
34
|
+
@window_class[:lpszClassName] =
|
35
|
+
FFI::MemoryPointer.from_string 'ruby-skype'
|
36
|
+
|
37
|
+
@window = Win32::CreateWindowEx(Win32::WS_EX_LEFT,
|
38
|
+
FFI::Pointer.new(@window_class.handle),
|
39
|
+
'ruby-skype',
|
40
|
+
Win32::WS_OVERLAPPEDWINDOW,
|
41
|
+
0, 0, 0, 0, Win32::NULL, Win32::NULL,
|
42
|
+
instance, nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Connects to Skype.
|
46
|
+
#
|
47
|
+
# @return [void]
|
48
|
+
def connect
|
49
|
+
# Do setup before sending message as Windows will process messages as
|
50
|
+
# well while in SendMessage()
|
51
|
+
@msg = Win32::MSG.new
|
52
|
+
@authorized = nil
|
53
|
+
@message_counter = 0
|
54
|
+
@replies = {}
|
55
|
+
|
56
|
+
Win32::PostMessage(Win32::HWND_BROADCAST, @api_discover_message_id,
|
57
|
+
@window, 0)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Update processing.
|
61
|
+
#
|
62
|
+
# This executes a Windows event loop while there are messages still
|
63
|
+
# pending, then dumps back out to let other things take over and do their
|
64
|
+
# thing.
|
65
|
+
#
|
66
|
+
# @return [void]
|
67
|
+
def tick
|
68
|
+
while Win32::PeekMessage(@msg, Win32::NULL, 0, 0, Win32::PM_REMOVE) > 0
|
69
|
+
Win32::TranslateMessage(@msg)
|
70
|
+
Win32::DispatchMessage(@msg)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Don't simplify this as we rely on false != nil for tribool values
|
74
|
+
#noinspection RubySimplifyBooleanInspection
|
75
|
+
if @authorized == false
|
76
|
+
Skype::Errors::ExceptionFactory.generate_exception("ERROR 68")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sends a message to Skype.
|
81
|
+
#
|
82
|
+
# @param [string] message The message to send to Skype
|
83
|
+
# @return [string] The direct response from Skype
|
84
|
+
def send(message)
|
85
|
+
puts "-> #{message}" if Skype.DEBUG
|
86
|
+
|
87
|
+
counter = next_message_counter
|
88
|
+
message = "##{counter} #{message}"
|
89
|
+
|
90
|
+
data = Win32::COPYDATASTRUCT.new
|
91
|
+
data[:dwData] = 0
|
92
|
+
data[:cbData] = message.length + 1
|
93
|
+
data[:lpData] = FFI::MemoryPointer.from_string(message + "\0")
|
94
|
+
|
95
|
+
Win32::SendMessage(@skype_window, Win32::WM_COPYDATA, @window,
|
96
|
+
pointer_to_long(data.to_ptr))
|
97
|
+
|
98
|
+
while @replies[counter].nil?
|
99
|
+
tick
|
100
|
+
sleep(0.1)
|
101
|
+
end
|
102
|
+
|
103
|
+
ret = @replies[counter]
|
104
|
+
@replies.delete(counter)
|
105
|
+
ret
|
106
|
+
end
|
107
|
+
|
108
|
+
# Attached to Skype successfully.
|
109
|
+
API_ATTACH_SUCCESS = 0
|
110
|
+
# Skype indicated that we should hold on.
|
111
|
+
API_ATTACH_PENDING = 1
|
112
|
+
# Attachment to Skype was refused.
|
113
|
+
API_ATTACH_REFUSED = 2
|
114
|
+
# Attachment to Skype isn't available currently. Typically there is no
|
115
|
+
# user logged in.
|
116
|
+
API_ATTACH_NOT_AVAILABLE = 3
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def next_message_counter
|
121
|
+
@message_counter += 1
|
122
|
+
end
|
123
|
+
|
124
|
+
LPARAM_BITS = Win32::LPARAM.size * 8
|
125
|
+
|
126
|
+
# Convert a ulong pointer value to a long int for use as a LPARAM because
|
127
|
+
# someone at Microsoft thought it'd be a good idea to pass around
|
128
|
+
# pointers as signed values.
|
129
|
+
def pointer_to_long(pointer)
|
130
|
+
pointer = pointer.to_i
|
131
|
+
pointer > (2 ** (LPARAM_BITS - 1)) ?
|
132
|
+
pointer - (2 ** LPARAM_BITS) : pointer
|
133
|
+
end
|
134
|
+
|
135
|
+
# Allows us to unwrap a pointer from a long. See #pointer_to_long
|
136
|
+
def long_to_pointer(long)
|
137
|
+
long < 0 ? long + (2 ** LPARAM_BITS) : long
|
138
|
+
end
|
139
|
+
|
140
|
+
# This is our message pump that receives messages from Windows.
|
141
|
+
#
|
142
|
+
# The return value from DefWindowProc is important and must be returned
|
143
|
+
# somehow.
|
144
|
+
#
|
145
|
+
# @see
|
146
|
+
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms633573.aspx
|
147
|
+
# MSDN
|
148
|
+
def message_pump(window_handle, message_id, wParam, lParam)
|
149
|
+
case message_id
|
150
|
+
when @api_discover_message_id
|
151
|
+
# Drop WM_API_DISCOVER messages on the floor
|
152
|
+
when @api_attach_message_id
|
153
|
+
case lParam
|
154
|
+
when API_ATTACH_SUCCESS
|
155
|
+
@skype_window = wParam
|
156
|
+
send("NAME " + @application_name)
|
157
|
+
@protocol_version = send("PROTOCOL 8").sub(/^PROTOCOL\s+/, '').
|
158
|
+
to_i
|
159
|
+
@authorized = true
|
160
|
+
|
161
|
+
when API_ATTACH_REFUSED
|
162
|
+
# Signal to the message pump that we were deauthorised
|
163
|
+
@authorized = false
|
164
|
+
|
165
|
+
else
|
166
|
+
# Ignore pending signal
|
167
|
+
puts "WM: Ignoring WM_API_ATTACH response: #{lParam}" if
|
168
|
+
Skype.DEBUG
|
169
|
+
|
170
|
+
end
|
171
|
+
when Win32::WM_COPYDATA
|
172
|
+
pointer = FFI::Pointer.new(long_to_pointer(lParam))
|
173
|
+
data = Win32::COPYDATASTRUCT.new pointer
|
174
|
+
|
175
|
+
input = data[:lpData].read_string(data[:cbData] - 1)
|
176
|
+
if input[0] == '#'
|
177
|
+
(counter, input) = input.split(/\s+/, 2)
|
178
|
+
counter = counter.gsub(/^#/, '').to_i
|
179
|
+
else
|
180
|
+
counter = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
if counter.nil?
|
184
|
+
receive(input)
|
185
|
+
else
|
186
|
+
@replies[counter] = input
|
187
|
+
puts "<- #{input}" if Skype.DEBUG
|
188
|
+
end
|
189
|
+
|
190
|
+
# Let Skype know we got it successfully
|
191
|
+
return 1
|
192
|
+
else
|
193
|
+
puts "Unhandled WM: #{sprintf("0x%04x", message_id)}" if Skype.DEBUG
|
194
|
+
return Win32::DefWindowProc(
|
195
|
+
window_handle, message_id, wParam, lParam)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
class Skype
|
5
|
+
module Communication
|
6
|
+
class Windows
|
7
|
+
# This module is used to provide access to the Win32 API to Ruby. There
|
8
|
+
# is lots of stuff here that is poorly named but tries stick closely to
|
9
|
+
# the original Win32 API for ease of reference.
|
10
|
+
#
|
11
|
+
# BEWARE: Here there be dragons aplenty.
|
12
|
+
module Win32
|
13
|
+
extend FFI::Library
|
14
|
+
ffi_lib('user32', 'kernel32')
|
15
|
+
ffi_convention(:stdcall)
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self._func(*args)
|
20
|
+
attach_function *args
|
21
|
+
case args.size
|
22
|
+
when 3
|
23
|
+
module_function args[0]
|
24
|
+
when 4
|
25
|
+
module_function args[0]
|
26
|
+
alias_method(args[1], args[0])
|
27
|
+
module_function args[1]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!group Windows Data Types
|
32
|
+
# @see http://msdn.microsoft.com/en-us/library/aa383751%28VS.85%29.aspx
|
33
|
+
|
34
|
+
# Check for 64b operating systems.
|
35
|
+
if [nil].pack('p').bytesize == 8
|
36
|
+
ULONG_PTR = FFI::TypeDefs[:uint64]
|
37
|
+
LONG_PTR = FFI::TypeDefs[:int64]
|
38
|
+
else
|
39
|
+
ULONG_PTR = FFI::TypeDefs[:ulong]
|
40
|
+
LONG_PTR = FFI::TypeDefs[:long]
|
41
|
+
end
|
42
|
+
|
43
|
+
ULONG = FFI::TypeDefs[:ulong]
|
44
|
+
LONG = FFI::TypeDefs[:long]
|
45
|
+
PVOID = FFI::TypeDefs[:pointer]
|
46
|
+
LPVOID = FFI::TypeDefs[:pointer]
|
47
|
+
INT = FFI::TypeDefs[:int]
|
48
|
+
BYTE = FFI::TypeDefs[:uint16]
|
49
|
+
DWORD = FFI::TypeDefs[:ulong]
|
50
|
+
BOOL = FFI::TypeDefs[:int]
|
51
|
+
UINT = FFI::TypeDefs[:uint]
|
52
|
+
POINTER = FFI::TypeDefs[:pointer]
|
53
|
+
VOID = FFI::TypeDefs[:void]
|
54
|
+
|
55
|
+
HANDLE = ULONG_PTR
|
56
|
+
HWND = HANDLE
|
57
|
+
HICON = HANDLE
|
58
|
+
HCURSOR = HANDLE
|
59
|
+
HBRUSH = HANDLE
|
60
|
+
HINSTANCE = HANDLE
|
61
|
+
HGDIOBJ = HANDLE
|
62
|
+
HMENU = HANDLE
|
63
|
+
HMODULE = HANDLE
|
64
|
+
|
65
|
+
LPARAM = LONG_PTR
|
66
|
+
WPARAM = ULONG_PTR
|
67
|
+
LPMSG = LPVOID
|
68
|
+
LPCTSTR = LPVOID
|
69
|
+
LRESULT = LONG_PTR
|
70
|
+
ATOM = BYTE
|
71
|
+
|
72
|
+
# @!endgroup
|
73
|
+
|
74
|
+
public
|
75
|
+
|
76
|
+
# Provide a NULL constant so we can be a little more explicit.
|
77
|
+
NULL = 0
|
78
|
+
|
79
|
+
# This is the callback function used to process window messages.
|
80
|
+
WNDPROC = callback(:WindowProc, [HWND, UINT, WPARAM, LPARAM], LRESULT)
|
81
|
+
|
82
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms633577.aspx MSDN
|
83
|
+
class WNDCLASSEX < FFI::Struct
|
84
|
+
layout :cbSize, UINT,
|
85
|
+
:style, UINT,
|
86
|
+
:lpfnWndProc, WNDPROC,
|
87
|
+
:cbClsExtra, INT,
|
88
|
+
:cbWndExtra, INT,
|
89
|
+
:hInstance, HANDLE,
|
90
|
+
:hIcon, HICON,
|
91
|
+
:hCursor, HCURSOR,
|
92
|
+
:hbrBackground, HBRUSH,
|
93
|
+
:lpszMenuName, LPCTSTR,
|
94
|
+
:lpszClassName, LPCTSTR,
|
95
|
+
:hIconSm, HICON
|
96
|
+
|
97
|
+
def initialize(*args)
|
98
|
+
super
|
99
|
+
self[:cbSize] = self.size
|
100
|
+
@atom = 0
|
101
|
+
end
|
102
|
+
|
103
|
+
# Register class with Windows.
|
104
|
+
def register_class_ex
|
105
|
+
# According to MSDN, you must add 1 to this value before
|
106
|
+
# registering. We shouldn't expect client code to remember to
|
107
|
+
# always do this.
|
108
|
+
if self[:hbrBackground] > 0
|
109
|
+
self[:hbrBackground] = self[:hbrBackground] + 1
|
110
|
+
end
|
111
|
+
|
112
|
+
(@atom = Win32::RegisterClassEx(self)) != 0 ?
|
113
|
+
@atom : raise("RegisterClassEx Error")
|
114
|
+
end
|
115
|
+
|
116
|
+
# @!attribute [r] handle
|
117
|
+
#
|
118
|
+
# Returns a handle to use the windo class with CreateWindowEx()
|
119
|
+
def handle
|
120
|
+
@atom != 0 ? @atom : register_class_ex
|
121
|
+
end
|
122
|
+
end # WNDCLASSEX
|
123
|
+
|
124
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805.aspx
|
125
|
+
class POINT < FFI::Struct
|
126
|
+
layout :x, LONG,
|
127
|
+
:y, LONG
|
128
|
+
end
|
129
|
+
|
130
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx
|
131
|
+
class MSG < FFI::Struct
|
132
|
+
layout :hwnd, HWND,
|
133
|
+
:message, UINT,
|
134
|
+
:wParam, WPARAM,
|
135
|
+
:lParam, LPARAM,
|
136
|
+
:time, DWORD,
|
137
|
+
:pt, POINT
|
138
|
+
end
|
139
|
+
|
140
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010.aspx
|
141
|
+
class COPYDATASTRUCT < FFI::Struct
|
142
|
+
layout :dwData, ULONG_PTR,
|
143
|
+
:cbData, DWORD,
|
144
|
+
:lpData, PVOID
|
145
|
+
end
|
146
|
+
|
147
|
+
# @!method RegisterWindowMessage(message_name)
|
148
|
+
#
|
149
|
+
# Registers a Window Message with Windows or returns a handle to an
|
150
|
+
# existing message.
|
151
|
+
#
|
152
|
+
# @param [String] message_name The name of the message to register
|
153
|
+
# @return [Handle]
|
154
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644947.aspx MSDN
|
155
|
+
_func(:RegisterWindowMessage, :RegisterWindowMessageA, [LPCTSTR], UINT)
|
156
|
+
|
157
|
+
# @!method GetModuleHandle(module_name)
|
158
|
+
#
|
159
|
+
# Used to obtain a handle to a module loaded by the application. If
|
160
|
+
# passed DL::NULL then returns a handle to the current module.
|
161
|
+
#
|
162
|
+
# @param [String|DL::NULL] module_name The name of the module to return
|
163
|
+
# a handle to.
|
164
|
+
# @return [ModuleHandle]
|
165
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683199.aspx MSDN
|
166
|
+
_func(:GetModuleHandle, :GetModuleHandleA, [LPCTSTR], HMODULE)
|
167
|
+
|
168
|
+
# @!method RegisterClassEx(class_definition)
|
169
|
+
#
|
170
|
+
# Registers a Window Class for use with CreateWindowEx.
|
171
|
+
#
|
172
|
+
# @param [WindowClass] class_definition
|
173
|
+
# @return [Handle]
|
174
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms633587.aspx MSDN
|
175
|
+
_func(:RegisterClassEx, :RegisterClassExA, [LPVOID], ATOM)
|
176
|
+
|
177
|
+
# @!method CreateWindowEx(extended_style, window_class, window_name, style, x, y, width, height, parent, menu, instance)
|
178
|
+
#
|
179
|
+
# Creates a new window.
|
180
|
+
#
|
181
|
+
# @param [Integer] extended_style A combination of WS_EX_* constant
|
182
|
+
# values defining the extended style for this window.
|
183
|
+
# @param [String] window_class This matches up with a registered
|
184
|
+
# WindowClass's lpszClassName parameter.
|
185
|
+
# @param [String] window_name This is the title of the newly created
|
186
|
+
# window.
|
187
|
+
# @param [Integer] style A combination of the WS_* constant values
|
188
|
+
# defining the style for this window.
|
189
|
+
# @param [Integer] x The horizontal position of the window on the
|
190
|
+
# screen.
|
191
|
+
# @param [Integer] y The vertical position of the window on the screen.
|
192
|
+
# @param [Integer] width The width of the window to create.
|
193
|
+
# @param [Integer] height The height of the window to create.
|
194
|
+
# @param [WindowHandle] parent A parent window for this one.
|
195
|
+
# @param [MenuHandle] menu The menu for this window.
|
196
|
+
# @param [InstanceHandle] instance
|
197
|
+
# @return [WindowHandle]
|
198
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms632680.aspx MSDN
|
199
|
+
_func(:CreateWindowEx, :CreateWindowExA, [DWORD, LPCTSTR, LPCTSTR,
|
200
|
+
DWORD, INT, INT, INT, INT,
|
201
|
+
HWND, HMENU, HINSTANCE,
|
202
|
+
LPVOID],
|
203
|
+
HWND)
|
204
|
+
|
205
|
+
# @!method GetMessage(message, window, filter_min, filter_max)
|
206
|
+
#
|
207
|
+
# Get a message from the message queue. Blocks until there is one to
|
208
|
+
# return. Compare with PeekMessage().
|
209
|
+
#
|
210
|
+
# @param [MSG] message **[out]** A message structure to output the
|
211
|
+
# incoming message to.
|
212
|
+
# @param [WindowHandle] window Which window to get messages for.
|
213
|
+
# @param [Integer] filter_min The first message to return, numerically.
|
214
|
+
# For suggestions, see MSDN.
|
215
|
+
# @param [Integer] filter_max The last message to return, numerically.
|
216
|
+
# If min and max are both 0 then all messages are returned. For
|
217
|
+
# suggestions, see MSDN.
|
218
|
+
# @return [Integer] -1 on error, otherwise 0 or 1 indicating whether to
|
219
|
+
# keep processing.
|
220
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644936.aspx MSDN
|
221
|
+
_func(:GetMessage, :GetMessageA, [LPMSG, HWND, UINT, UINT], BOOL)
|
222
|
+
|
223
|
+
# @!method PeekMessage(message, window, filter_min, filter_max, remove_message)
|
224
|
+
#
|
225
|
+
# Peek at a message from the message queue and optionally remove it.
|
226
|
+
# Never blocks. Compare with GetMessage().
|
227
|
+
#
|
228
|
+
# @param [MSG] message **[out]** A message structure to output the
|
229
|
+
# incoming message to.
|
230
|
+
# @param [WindowHandle] window Which window to get messages for.
|
231
|
+
# @param [Integer] filter_min The first message to return, numerically.
|
232
|
+
# For suggestions, see MSDN.
|
233
|
+
# @param [Integer] filter_max The last message to return, numerically.
|
234
|
+
# If min and max are both 0 then all messages are returned. For
|
235
|
+
# suggestions, see MSDN.
|
236
|
+
# @param [Integer] remove_message One of the PM_* values.
|
237
|
+
# @return [Integer] 0 or 1 indicating whether to keep processing.
|
238
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943.aspx MSDN
|
239
|
+
_func(:PeekMessage, :PeekMessageA, [LPMSG, HWND, UINT, UINT, UINT], BOOL)
|
240
|
+
|
241
|
+
# @!method SendMessage(window, message, wParam, lParam)
|
242
|
+
#
|
243
|
+
# Send a message to another window.
|
244
|
+
#
|
245
|
+
# @param [WindowHandle] window Which window to send the message to.
|
246
|
+
# @param [Integer] message WM_* message to send.
|
247
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950.aspx MSDN
|
248
|
+
_func(:SendMessage, :SendMessageA, [HWND, UINT, WPARAM, LPARAM], LRESULT)
|
249
|
+
|
250
|
+
# @!method PostMessage(window, message, wParam, lParam)
|
251
|
+
#
|
252
|
+
# Send a message to another window and return without waiting for a reply.
|
253
|
+
#
|
254
|
+
# @param [WindowHandle] window Which window to send the message to.
|
255
|
+
# @param [Integer] message WM_* message to send.
|
256
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950.aspx MSDN
|
257
|
+
_func(:PostMessage, :PostMessageA, [HWND, UINT, WPARAM, LPARAM], LRESULT)
|
258
|
+
|
259
|
+
# @!method TranslateMessage(message)
|
260
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644955.aspx MSDN
|
261
|
+
_func(:TranslateMessage, [LPVOID], BOOL)
|
262
|
+
|
263
|
+
# @!method DispatchMessage(message)
|
264
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644934.aspx MSDN
|
265
|
+
_func(:DispatchMessage, :DispatchMessageA, [LPVOID], BOOL)
|
266
|
+
|
267
|
+
# @!method DefWindowProc(message, window, filter_min, filter_max)
|
268
|
+
# @return [Object] Return value is dependant on message type
|
269
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms633572.aspx MSDN
|
270
|
+
_func(:DefWindowProc, :DefWindowProcA, [HWND, UINT, WPARAM, LPARAM], LRESULT)
|
271
|
+
|
272
|
+
# @!group Predefined WindowHandle's
|
273
|
+
#
|
274
|
+
# These are WindowHandle's provided by the Win32 API for special
|
275
|
+
# purposes.
|
276
|
+
|
277
|
+
# Target for SendMessage(). Broadcast to all windows.
|
278
|
+
HWND_BROADCAST = 0xffff
|
279
|
+
# Used as a parent in CreateWindow(). Signifies that this should be a
|
280
|
+
# message-only window.
|
281
|
+
HWND_MESSAGE = -3
|
282
|
+
|
283
|
+
# @!endgroup
|
284
|
+
|
285
|
+
# CreateWindow Use Default Value
|
286
|
+
CW_USEDEFAULT = 0x80000000
|
287
|
+
|
288
|
+
#@!group HBRUSH colours
|
289
|
+
|
290
|
+
# System window colour
|
291
|
+
COLOR_WINDOW = 5
|
292
|
+
|
293
|
+
# @!group Class Style contants.
|
294
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ff729176.aspx MSDN
|
295
|
+
|
296
|
+
# Redraws the entire window if a movement or size adjustment changes
|
297
|
+
# the height of the client area.
|
298
|
+
CS_VREDRAW = 0x0001
|
299
|
+
# Redraws the entire window if a movement or size adjustment changes
|
300
|
+
# the width of the client area.
|
301
|
+
CS_HREDRAW = 0x0002
|
302
|
+
|
303
|
+
# @!group Window Message constants
|
304
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644927.aspx#system_defined MSDN
|
305
|
+
|
306
|
+
# An application sends the WM_COPYDATA message to pass data to another
|
307
|
+
# application.
|
308
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms649011.aspx MSDN
|
309
|
+
WM_COPYDATA = 0x004A
|
310
|
+
|
311
|
+
# @!group PeekMessage constants
|
312
|
+
|
313
|
+
# Messages are not removed from the queue after processing by
|
314
|
+
# #PeekMessage().
|
315
|
+
PM_NOREMOVE = 0
|
316
|
+
# Messages are removed from the queue after processing by
|
317
|
+
# #PeekMessage().
|
318
|
+
PM_REMOVE = 1
|
319
|
+
|
320
|
+
# @!group Window Style constants
|
321
|
+
#
|
322
|
+
# This is only a subset.
|
323
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600.aspx
|
324
|
+
|
325
|
+
# The window has a thin-line border.
|
326
|
+
WS_BORDER = 0x00800000
|
327
|
+
# The window has a title bar
|
328
|
+
WS_CAPTION = 0x00C00000
|
329
|
+
# The window is initially disabled. A disabled window cannot receive
|
330
|
+
# input from the user.
|
331
|
+
WS_DISABLED = 0x08000000
|
332
|
+
# The window is an overlapped window. An overlapped window has a title
|
333
|
+
# bar and a border.
|
334
|
+
WS_OVERLAPPED = 0x00000000
|
335
|
+
# The windows is a pop-up window.
|
336
|
+
WS_POPUP = 0x80000000
|
337
|
+
# The window has a sizing border.
|
338
|
+
WS_SIZEBOX = 0x00040000
|
339
|
+
# The window has a window menu on its title bar.
|
340
|
+
WS_SYSMENU = 0x00080000
|
341
|
+
# The window has a sizing border.
|
342
|
+
WS_THICKFRAME = 0x00040000
|
343
|
+
# The window has a maximize button.
|
344
|
+
WS_MAXIMIZEBOX = 0x00010000
|
345
|
+
# The window has a minimize button.
|
346
|
+
WS_MINIMIZEBOX = 0x00020000
|
347
|
+
# The window is an overlapped window.
|
348
|
+
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
|
349
|
+
WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
|
350
|
+
# The window is a pop-up window.
|
351
|
+
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU
|
352
|
+
|
353
|
+
# @!group Window Extended Style constants
|
354
|
+
#
|
355
|
+
# This is only a subset.
|
356
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543.aspx
|
357
|
+
|
358
|
+
# The window has generic left-aligned properties. This is the default.
|
359
|
+
WS_EX_LEFT = 0
|
360
|
+
|
361
|
+
# @!endgroup
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
class Skype
|
3
|
+
module DataMaps
|
4
|
+
# Data mapping between user visibilities as returned by Skype and the
|
5
|
+
# symbols that this API cares about.
|
6
|
+
USER_VISIBILITY = {
|
7
|
+
:unknown => 'UNKNOWN',
|
8
|
+
:online => 'ONLINE',
|
9
|
+
:offline => 'OFFLINE',
|
10
|
+
:skype_me => 'SKYPEME',
|
11
|
+
:away => 'AWAY',
|
12
|
+
:not_available => 'NA',
|
13
|
+
:do_not_disturb => 'DND',
|
14
|
+
:invisible => 'INVISIBLE',
|
15
|
+
:logged_out => 'LOGGEDOUT',
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
class Skype
|
3
|
+
module Errors
|
4
|
+
# This is a map of error codes to error messages used by ExceptionFactory
|
5
|
+
# to generate it's messages.
|
6
|
+
#
|
7
|
+
# @see http://developer.skype.com/public-api-reference#ERRORS Skype Public
|
8
|
+
# API error codes
|
9
|
+
ERROR_MESSAGES = {
|
10
|
+
68 => 'Access denied',
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
require 'skype/errors/error_messages'
|
3
|
+
require 'skype/errors/general_error'
|
4
|
+
|
5
|
+
class Skype
|
6
|
+
# You shouldn't try including this module, it is used purely for organisation.
|
7
|
+
# @private
|
8
|
+
module Errors
|
9
|
+
# This class allows the library to generate exceptions based on error codes
|
10
|
+
# from Skype easily.
|
11
|
+
class ExceptionFactory
|
12
|
+
# This function will raise an exception. It never returns.
|
13
|
+
#
|
14
|
+
# @param [String] error_message The ERROR #error_id response from Skype.
|
15
|
+
def self.generate_exception(error_message)
|
16
|
+
data = error_message.match(/^ERROR\s+(\d+)(?:\s+(.+))?$/)
|
17
|
+
error_code = data[1].to_i
|
18
|
+
exception_message = ERROR_MESSAGES[error_code] || data[2] ||
|
19
|
+
"Unknown error: #{error_code}"
|
20
|
+
|
21
|
+
raise ::Skype::Errors::GeneralError.new(error_code), exception_message,
|
22
|
+
caller
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
class Skype
|
3
|
+
module Errors
|
4
|
+
# A generalised Exception. This adds an error code field which holds the
|
5
|
+
# numeric error code as returned by Skype. The Exception's message is a
|
6
|
+
# plain text version of the error code, usually suitable for display to
|
7
|
+
# users.
|
8
|
+
class GeneralError < Exception
|
9
|
+
# This is the error code reported by Skype.
|
10
|
+
attr_reader :code
|
11
|
+
|
12
|
+
# Create a new GeneralError with the specified code. The error message
|
13
|
+
# should be provided to raise from ERROR_MESSAGES
|
14
|
+
def initialize(error_code)
|
15
|
+
@code = error_code
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-skype
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.pre.2
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matthew Scharley
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: cane
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: ! " ruby-skype is a binding to the Skype Public API. The Public API
|
63
|
+
is a method\n to talk to the official Skype client running on the local computer
|
64
|
+
and\n allows for automation of the client.\n"
|
65
|
+
email: matt.scharley@gmail.com
|
66
|
+
executables: []
|
67
|
+
extensions:
|
68
|
+
- ext/mkrf_conf.rb
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- lib/skype/errors/exception_factory.rb
|
72
|
+
- lib/skype/errors/error_messages.rb
|
73
|
+
- lib/skype/errors/general_error.rb
|
74
|
+
- lib/skype/data_maps.rb
|
75
|
+
- lib/skype/data_maps/user_visibility.rb
|
76
|
+
- lib/skype/command_manager.rb
|
77
|
+
- lib/skype/communication/protocol.rb
|
78
|
+
- lib/skype/communication/windows/win32.rb
|
79
|
+
- lib/skype/communication/dbus.rb
|
80
|
+
- lib/skype/communication/windows.rb
|
81
|
+
- lib/skype.rb
|
82
|
+
- VERSION
|
83
|
+
- README.md
|
84
|
+
- LICENSE
|
85
|
+
- examples/call
|
86
|
+
- examples/listen
|
87
|
+
- ext/mkrf_conf.rb
|
88
|
+
homepage: https://github.com/mscharley/ruby-skype
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ~>
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 1.9.0
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>'
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 1.3.1
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.8.24
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Ruby binding to the Skype Public API.
|
113
|
+
test_files: []
|
114
|
+
has_rdoc:
|