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 ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ *.swp
7
+ *.orig
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ coverage
12
+ doc/
13
+ lib/bundler/man
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
20
+ spec/credentials.yml
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ # Specify your gem's dependencies in rosumi.gemspec
3
+ gemspec
4
+ gem 'rspec'
5
+ gem 'pry'
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
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ DEVICE_TO_CHECK = 1
3
+
4
+ describe Rosumi do
5
+
6
+ describe "#send_message" do
7
+ it "Should send message to device" do
8
+ @@rosumi.send_message(DEVICE_TO_CHECK, "Subject!", "Hello, word...!", false)
9
+ end
10
+ end
11
+
12
+ end
@@ -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: