rosumi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +20 -0
- data/Gemfile +5 -0
- data/README.md +29 -0
- data/lib/rosumi.rb +53 -0
- data/lib/rosumi/locator.rb +50 -0
- data/lib/rosumi/messenger.rb +42 -0
- data/lib/rosumi/post_helper.rb +116 -0
- data/rosumi.gemspec +23 -0
- data/spec/integration/locator_test.rb +29 -0
- data/spec/integration/messenger_test.rb +12 -0
- data/spec/spec_helper.rb +19 -0
- metadata +93 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Rosumi
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rosumi'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rosumi
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/lib/rosumi.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
class Rosumi
|
2
|
+
|
3
|
+
def initialize(user, pass)
|
4
|
+
@user = user.strip
|
5
|
+
@pass = pass.strip
|
6
|
+
@locator = Rosumi::Locator.new(user, pass)
|
7
|
+
@messenger = Rosumi::Messenger.new(user, pass)
|
8
|
+
end
|
9
|
+
|
10
|
+
def devices
|
11
|
+
devices = @locator.update_devices
|
12
|
+
|
13
|
+
result = {}
|
14
|
+
devices.each_with_index do |device, i|
|
15
|
+
result[i] = {:type => device['deviceClass'], :name => device['name']}
|
16
|
+
end
|
17
|
+
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
# Gets location information for a device.
|
22
|
+
#
|
23
|
+
# ==== Attributes
|
24
|
+
#
|
25
|
+
# * +id+ - ID to locate (0,1,2,3, et cetera).
|
26
|
+
def locate_device(id)
|
27
|
+
unless id
|
28
|
+
raise "An id must be specified."
|
29
|
+
end
|
30
|
+
|
31
|
+
@locator.locate(id)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sends a message to the specified device.
|
35
|
+
#
|
36
|
+
# ==== Attributes
|
37
|
+
#
|
38
|
+
# * +id+ - id of the device (0,1,2,3 et cetera).
|
39
|
+
# * +subject+ - Subject of the message.
|
40
|
+
# * +message+ - The message to display on the device.
|
41
|
+
# * +sound+ - If true, plays a sound on the device.
|
42
|
+
def send_message(id, subject="", message, sound)
|
43
|
+
unless id
|
44
|
+
raise "An id must be specified."
|
45
|
+
end
|
46
|
+
|
47
|
+
@messenger.send_message(id, subject, message, sound)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
require "rosumi/locator"
|
53
|
+
require "rosumi/messenger"
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "uri"
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'base64'
|
6
|
+
require 'pry'
|
7
|
+
require_relative "post_helper"
|
8
|
+
|
9
|
+
class Rosumi::Locator
|
10
|
+
include PostHelper
|
11
|
+
|
12
|
+
def initialize(user, pass)
|
13
|
+
@user = user
|
14
|
+
@pass = pass
|
15
|
+
@devices = []
|
16
|
+
super()
|
17
|
+
end
|
18
|
+
|
19
|
+
# Find a device by it's number.
|
20
|
+
#
|
21
|
+
# ==== Attributes
|
22
|
+
#
|
23
|
+
# * +device_num+ - Device number as returned from the update_devices method.
|
24
|
+
# * +max_wait+ - Maximum wait in seconds to wait for call to complete.
|
25
|
+
def locate(device_num = 0, max_wait = 300)
|
26
|
+
|
27
|
+
start = Time.now
|
28
|
+
|
29
|
+
begin
|
30
|
+
raise "Unable to find location within '#{max_wait}' seconds" if ((Time.now - start) > max_wait)
|
31
|
+
|
32
|
+
sleep(5)
|
33
|
+
update_devices
|
34
|
+
raise "Invalid device number!" if @devices[device_num].nil?
|
35
|
+
raise "There is no location data for this device (#{@devices[device_num]['name']})" if @devices[device_num]['location'].nil?
|
36
|
+
end while (@devices[device_num]['location']['locationFinished'] == 'false')
|
37
|
+
|
38
|
+
loc = {
|
39
|
+
:name => @devices[device_num]['name'],
|
40
|
+
:latitude => @devices[device_num]['location']['latitude'],
|
41
|
+
:longitude => @devices[device_num]['location']['longitude'],
|
42
|
+
:accuracy => @devices[device_num]['location']['horizontalAccuracy'],
|
43
|
+
:timestamp => @devices[device_num]['location']['timeStamp'],
|
44
|
+
:position_type => @devices[device_num]['location']['positionType']
|
45
|
+
};
|
46
|
+
|
47
|
+
return loc;
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "uri"
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'base64'
|
6
|
+
require_relative "post_helper"
|
7
|
+
|
8
|
+
class Rosumi::Messenger
|
9
|
+
include PostHelper
|
10
|
+
|
11
|
+
def initialize(user, pass)
|
12
|
+
@user = user
|
13
|
+
@pass = pass
|
14
|
+
super()
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sends a message to the specified device.
|
18
|
+
#
|
19
|
+
# ==== Attributes
|
20
|
+
#
|
21
|
+
# * +id+ - id of the device (0,1,2,3 et cetera).
|
22
|
+
# * +subject+ - Subject of the message.
|
23
|
+
# * +message+ - The message to display on the device.
|
24
|
+
# * +sound+ - If true, plays a sound on the device.
|
25
|
+
def send_message(id, subject, message, sound)
|
26
|
+
|
27
|
+
update_devices
|
28
|
+
device_id = @devices[id]['id']
|
29
|
+
|
30
|
+
data = {'clientContext' => client_context(device_id),
|
31
|
+
'device' => device_id,
|
32
|
+
'sound' => sound,
|
33
|
+
'subject' => subject,
|
34
|
+
'text' => message,
|
35
|
+
'userText' => true
|
36
|
+
};
|
37
|
+
|
38
|
+
self.send(:post,"/fmipservice/device/#{@user}/sendMessage", data)
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module PostHelper
|
2
|
+
|
3
|
+
URL ="fmipmobile.icloud.com"
|
4
|
+
PORT=443
|
5
|
+
|
6
|
+
attr_accessor :http, :partition, :devices
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@http = Net::HTTP.new(URL, PORT)
|
10
|
+
@http.use_ssl = true
|
11
|
+
@partition = nil
|
12
|
+
@devices = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Updates the devices array with the latest information from icloud.
|
16
|
+
def update_devices
|
17
|
+
data = {'clientContext' => client_context(nil)};
|
18
|
+
|
19
|
+
json_devices = self.send(:post,"/fmipservice/device/#{@user}/initClient", data)
|
20
|
+
json_devices['content'].each { |json_device| @devices << json_device }
|
21
|
+
|
22
|
+
@devices
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Posts some data to the URL and the path specified.
|
28
|
+
#
|
29
|
+
# ==== Attributes
|
30
|
+
#
|
31
|
+
# * +path+ - The path to post to (relative to the URL constant).
|
32
|
+
# * +data+ - Payload to send.
|
33
|
+
def post(path, data)
|
34
|
+
auth = Base64.encode64(@user+':'+@pass)
|
35
|
+
headers = {
|
36
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
37
|
+
'X-Apple-Find-Api-Ver' => '2.0',
|
38
|
+
'X-Apple-Authscheme' => 'UserIdGuest',
|
39
|
+
'X-Apple-Realm-Support' => '1.2',
|
40
|
+
'User-Agent' => 'Find iPhone/1.1 MeKit (iPad: iPhone OS/4.2.1)',
|
41
|
+
'X-Client-Name' => 'iPad',
|
42
|
+
'X-Client-Uuid' => '0cf3dc501ff812adb0b202baed4f37274b210853',
|
43
|
+
'Accept-Language' => 'en-us',
|
44
|
+
'Authorization' => "Basic #{auth}"
|
45
|
+
}
|
46
|
+
|
47
|
+
unless @partition
|
48
|
+
@partition = self.send(:fetch_partition, path, JSON.generate(data), headers)
|
49
|
+
@http = Net::HTTP.new(partition, PORT)
|
50
|
+
@http.use_ssl=true
|
51
|
+
end
|
52
|
+
|
53
|
+
resp = fetch(path, JSON.generate(data), headers)
|
54
|
+
|
55
|
+
return JSON.parse(resp.body);
|
56
|
+
end
|
57
|
+
|
58
|
+
# Posts some data to the URL and the path specified.
|
59
|
+
#
|
60
|
+
# ==== Attributes
|
61
|
+
#
|
62
|
+
# * +path+ - Path to do an http post to the URL constant.
|
63
|
+
# * +data+ - Payload to send.
|
64
|
+
# * +headers+ - HTTP headers.
|
65
|
+
# * +limit+ - Number of redirects allowed.
|
66
|
+
def fetch(path, data, headers, limit = 10)
|
67
|
+
|
68
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
69
|
+
|
70
|
+
response = http.post(path, data, headers)
|
71
|
+
|
72
|
+
case response
|
73
|
+
when Net::HTTPSuccess then response
|
74
|
+
when Net::HTTPRedirection then fetch(response['location'], data, headers, limit - 1)
|
75
|
+
else
|
76
|
+
response.error!
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Posts some data to the path and returns the 'X-Apple-MMe-Host' portion
|
81
|
+
# of the response.
|
82
|
+
#
|
83
|
+
# ==== Attributes
|
84
|
+
#
|
85
|
+
# * +path+ - Path to do an http post to the URL constant.
|
86
|
+
# * +data+ - Payload to send.
|
87
|
+
# * +headers+ - HTTP headers.
|
88
|
+
def fetch_partition(path, data, headers)
|
89
|
+
|
90
|
+
response = http.post(path, data, headers)
|
91
|
+
response['X-Apple-MMe-Host']
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the 'clientContext' attribute for requests.
|
96
|
+
#
|
97
|
+
# ==== Attributes
|
98
|
+
#
|
99
|
+
# * +id+ - The device id.
|
100
|
+
def client_context(id)
|
101
|
+
|
102
|
+
{
|
103
|
+
'appName' => 'FindMyiPhone',
|
104
|
+
'appVersion' => '1.4',
|
105
|
+
'buildVersion' => '145',
|
106
|
+
'deviceUDID' => '0000000000000000000000000000000000000000',
|
107
|
+
'inactiveTime' => 5911,
|
108
|
+
'osVersion' => '4.2.1',
|
109
|
+
'productType' => 'iPad1,1',
|
110
|
+
'selectedDevice' => id,
|
111
|
+
'shouldLocate'=>false
|
112
|
+
}
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/rosumi.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rosumi/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rosumi"
|
8
|
+
spec.version = Rosumi::VERSION
|
9
|
+
spec.authors = ["Kevin Eder"]
|
10
|
+
spec.email = ["kevin.eder@gmail.com"]
|
11
|
+
spec.description = %q{Provides an API for locating and messaging Apple devices.}
|
12
|
+
spec.summary = "Provides and API for locating and messaging Apple devices."
|
13
|
+
spec.homepage = "https://github.com/kevineder/rosumi"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
DEVICE_TO_CHECK = 0
|
3
|
+
|
4
|
+
describe Rosumi do
|
5
|
+
|
6
|
+
describe "#devices" do
|
7
|
+
it "should return a devices hash mapping id => {:type, :name}" do
|
8
|
+
devices = @@rosumi.devices
|
9
|
+
devices.should_not be_nil
|
10
|
+
devices[0].should_not be_nil
|
11
|
+
devices[0][:type].should_not be_nil
|
12
|
+
devices[0][:name].should_not be_nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#locate_device" do
|
17
|
+
it "should return location information for device #{DEVICE_TO_CHECK}" do
|
18
|
+
location = @@rosumi.locate_device(DEVICE_TO_CHECK)
|
19
|
+
location.should_not be_nil
|
20
|
+
location.should_not be_nil
|
21
|
+
location[:name].should_not be_nil
|
22
|
+
location[:latitude].should_not be_nil
|
23
|
+
location[:longitude].should_not be_nil
|
24
|
+
location[:accuracy].should_not be_nil
|
25
|
+
location[:timestamp].should_not be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rosumi'
|
4
|
+
require 'yaml'
|
5
|
+
require "rspec"
|
6
|
+
require "pry"
|
7
|
+
|
8
|
+
CREDENTIALS_FILE = File.join(File.dirname(__FILE__), 'credentials.yml')
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
unless File.exist?(CREDENTIALS_FILE) and File.file?(CREDENTIALS_FILE)
|
12
|
+
raise "Please create a credentials.yml file containing your Apple email / password."
|
13
|
+
end
|
14
|
+
|
15
|
+
data = YAML.load_file(CREDENTIALS_FILE)
|
16
|
+
@@username = data['email']
|
17
|
+
@@password = data['password']
|
18
|
+
@@rosumi = Rosumi.new @@username, @@password
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rosumi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kevin Eder
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
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: '1.3'
|
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
|
+
description: Provides an API for locating and messaging Apple devices.
|
47
|
+
email:
|
48
|
+
- kevin.eder@gmail.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- Gemfile
|
55
|
+
- README.md
|
56
|
+
- lib/rosumi.rb
|
57
|
+
- lib/rosumi/locator.rb
|
58
|
+
- lib/rosumi/messenger.rb
|
59
|
+
- lib/rosumi/post_helper.rb
|
60
|
+
- rosumi.gemspec
|
61
|
+
- spec/integration/locator_test.rb
|
62
|
+
- spec/integration/messenger_test.rb
|
63
|
+
- spec/spec_helper.rb
|
64
|
+
homepage: https://github.com/kevineder/rosumi
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.23
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Provides and API for locating and messaging Apple devices.
|
89
|
+
test_files:
|
90
|
+
- spec/integration/locator_test.rb
|
91
|
+
- spec/integration/messenger_test.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
has_rdoc:
|