ausca 0.1.1 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZTNlNDA0ZmU4NzdhOWI2MmNiODgwMDgzOGQ0MTU4YzkzYjBhYWRjMQ==
5
- data.tar.gz: !binary |-
6
- NjZlOGZlNmU3MDllYzkxOGVmNmM4ZGI5MTdhOTQ2OWQ3MzFhZDJiNA==
2
+ SHA256:
3
+ metadata.gz: 71a86a7f1a4c25736fe49ec7ae8cbb3ffea87441bcbe2b68deb98525f6a1d260
4
+ data.tar.gz: e72666ca1fd885a5be41c4645d400283851a66c16ba7daa2cbcfb8847db20acc
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- MDU3NzdhMTE1Y2U4Y2EwNWM4ZjEzMDAxZDQ2NTcwMzdiMzM4Nzk4MmJhMDk2
10
- OThlMjIxOGVjNGQzYjdlNzBhNjU1Yjk2ZTE3NGY1MDFiMWNjMzg0YzE4NGJl
11
- OTU0OGFjOGY0YzQzYmExZWU2ZjI4YjU0YTdiNTQ4ZDhjYWM3YTQ=
12
- data.tar.gz: !binary |-
13
- NDNiNmFjZTgxY2Y2OWJlY2ZiMzI2NzIyOWRiNjQzZTFmNGI2NjhhYzU3YjRi
14
- ZWEwNDNmYmM5NDkyMjAwNzFiNWVlMzI1ZDU3M2Y0MWQ3MWY2YWQ4ZTg5ZDg4
15
- N2Y2MzBjZDkzZjVlZDc5MmE1ZDFlOTZhNDRmOWUwMDgzZmIzYWM=
6
+ metadata.gz: 1c71ae3f163e4fb3f4a57e374988df8b50c51f77f2a15fc8854a6a418daeff315e390fa31ebb6dde35f15060d8a92dbcc5224d07612d01f990347873265a4732
7
+ data.tar.gz: 5ab83ec201d4bf0520a1d44324f310222ce45006b60391220450e021fcb647558509579648756dc30436b8340d96465bc01d51e1f098195bc0c912bfacd0e4b0
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ausca
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,29 +1,38 @@
1
- # Ausca
2
-
3
- Ausca is a collection of automation utilities and bots written in Ruby.
4
-
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
8
-
9
- gem 'ausca'
10
-
11
- And then execute:
12
-
13
- $ bundle
14
-
15
- Or install it yourself as:
16
-
17
- $ gem install ausca
18
-
19
- ## Usage
20
-
21
- Check out http://ausca.com for documentation, and the `examples` directory for functional examples.
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
1
+ # Ausca for Ruby
2
+
3
+ This gem resolves the live [Ausca catalog](https://ausca.com/catalog.json),
4
+ binds an exact offer revision, and preserves a caller-owned purchase identity
5
+ across uncertain HTTP responses. It has no runtime gem dependencies and no
6
+ embedded wallet or rail. The paid transport must implement
7
+ `call(uri, net_http_request) -> net_http_response`, including its own payment
8
+ spend policy and x402 challenge/retry handling.
9
+
10
+ ```ruby
11
+ require "ausca"
12
+
13
+ client = Ausca::Client.new(payment: my_payment_authority)
14
+ offer = client.offer("browser.session") # free catalog read
15
+ puts offer.fetch("price")
16
+
17
+ result = client.invoke("browser.session", { duration_seconds: 600 },
18
+ idempotency_key: "my-browser-purchase-0001") do |identity|
19
+ persist_identity(identity) # must succeed before payment begins
20
+ end
21
+ puts result.fetch(:body)
22
+ ```
23
+
24
+ Use `probe` without payment to inspect a challenge. `Ausca::UncertainError`
25
+ contains the purchase identity: recover with the same input and key, not a new
26
+ purchase. `Ausca::RefusalError` contains the status, decoded body, and identity.
27
+ `invocation` reads durable state without payment. `commit` stores immutable
28
+ input bytes through keyless artifact ingress; `access` returns a short-lived
29
+ download URL with no HTTP request body. Verify downloaded bytes against the
30
+ returned digest.
31
+
32
+ The live catalog controls price, route, revision, schemas, and offer-specific
33
+ limits. This package is only a buyer client; it never owns product contracts.
34
+
35
+ ```sh
36
+ ruby -Ilib test/client_test.rb
37
+ gem build ausca.gemspec
38
+ ```
data/lib/ausca.rb CHANGED
@@ -1,9 +1,248 @@
1
- require_relative "ausca/version"
2
- require_relative "ausca/util"
3
- require_relative "ausca/rss/util"
4
- require_relative "ausca/rss/joiner"
5
- require_relative "ausca/twitter/bot"
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+ require "digest"
5
+ require "json"
6
+ require "net/http"
7
+ require "securerandom"
8
+ require "uri"
6
9
 
7
10
  module Ausca
8
- # :)
11
+ ORIGIN = "https://ausca.com"
12
+ MAX_ARTIFACT_BYTES = 25 * 1024 * 1024
13
+ MAX_RESPONSE_BYTES = 8 * 1024 * 1024
14
+
15
+ class Error < StandardError; end
16
+
17
+ class RefusalError < Error
18
+ attr_reader :status, :body, :identity
19
+
20
+ def initialize(status, body, identity)
21
+ @status, @body, @identity = status, body, identity
22
+ super("Ausca answered #{status}; inspect the body and retain purchase key #{identity[:idempotency_key]}")
23
+ end
24
+ end
25
+
26
+ class UncertainError < Error
27
+ attr_reader :identity
28
+
29
+ def initialize(identity)
30
+ @identity = identity
31
+ super("Ausca outcome uncertain; recover #{identity[:offer_id]} with the same input and key #{identity[:idempotency_key]}")
32
+ end
33
+ end
34
+
35
+ # The default transport performs ordinary HTTP. A paid authority implements
36
+ # the same #call(uri, request) method and handles 402 challenges under its own
37
+ # spend policy. Wallets, signing, and provider SDKs never enter this package.
38
+ class HTTPTransport
39
+ def call(uri, request)
40
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: 10, read_timeout: 60) do |http|
41
+ http.request(request)
42
+ end
43
+ end
44
+ end
45
+
46
+ class Client
47
+ def initialize(payment: nil, origin: ORIGIN, http: HTTPTransport.new)
48
+ @origin = origin.delete_suffix("/")
49
+ @http = http
50
+ @payment = payment
51
+ @catalog_json = nil
52
+ end
53
+
54
+ def catalog(refresh: false)
55
+ if @catalog_json.nil? || refresh
56
+ response = request(@http, :get, "/catalog.json")
57
+ raise Error, "catalog answered #{response.code}" unless response.code.to_i == 200
58
+
59
+ document = decode_json(response.body)
60
+ offers = document.fetch("offers")
61
+ raise Error, "catalog has no offers" unless offers.is_a?(Array)
62
+
63
+ offers.each { |offer| validate_offer(offer) }
64
+ @catalog_json = response.body.dup.freeze
65
+ end
66
+ JSON.parse(@catalog_json)
67
+ end
68
+
69
+ def offer(offer_id)
70
+ catalog.fetch("offers").find { |entry| entry.fetch("offer_id") == offer_id } ||
71
+ raise(Error, "offer #{offer_id.inspect} is not active in the catalog")
72
+ end
73
+
74
+ def price(offer_id)
75
+ offer(offer_id).fetch("price")
76
+ end
77
+
78
+ def envelope(bound_offer, input, idempotency_key: nil, attribution: nil)
79
+ validate_offer(bound_offer)
80
+ key = idempotency_key || new_key("ausca-")
81
+ validate_key(key)
82
+ validate_attribution(attribution) unless attribution.nil?
83
+ {
84
+ "offer_id" => bound_offer.fetch("offer_id"),
85
+ "offer_revision" => bound_offer.fetch("revision"),
86
+ "offer_revision_digest" => bound_offer.fetch("revision_digest"),
87
+ "input_schema_digest" => bound_offer.fetch("input_schema").fetch("digest"),
88
+ "output_schema_digest" => bound_offer.fetch("output_schema").fetch("digest"),
89
+ "input" => input,
90
+ "idempotency_key" => key
91
+ }.tap { |body| body["attribution"] = attribution unless attribution.nil? }
92
+ end
93
+
94
+ # Read-only challenge inspection; never calls the payment authority.
95
+ def probe(offer_id, input, idempotency_key: nil, attribution: nil)
96
+ bound = offer(offer_id)
97
+ body = envelope(bound, input, idempotency_key: idempotency_key, attribution: attribution)
98
+ [request(@http, :post, bound.fetch("route").fetch("path"), body: JSON.generate(body)), identity(body)]
99
+ end
100
+
101
+ # The caller may persist the identity in the block before payment begins.
102
+ # On uncertainty, retry with identical input and the same key.
103
+ def invoke(offer_id, input, idempotency_key: nil, attribution: nil)
104
+ raise Error, "payment authority is required for invoke" if @payment.nil?
105
+
106
+ bound = offer(offer_id)
107
+ body = envelope(bound, input, idempotency_key: idempotency_key, attribution: attribution)
108
+ purchase = identity(body)
109
+ yield(purchase) if block_given?
110
+ response = begin
111
+ request(@payment, :post, bound.fetch("route").fetch("path"), body: JSON.generate(body))
112
+ rescue StandardError
113
+ raise UncertainError, purchase
114
+ end
115
+ decoded = begin
116
+ decode_json(response.body)
117
+ rescue StandardError
118
+ raise UncertainError, purchase
119
+ end
120
+ raise RefusalError.new(response.code.to_i, decoded, purchase) if response.code.to_i >= 400
121
+
122
+ { status: response.code.to_i, body: decoded, identity: purchase }
123
+ end
124
+
125
+ def invocation(invocation_id)
126
+ raise Error, "invocation ID is required" if invocation_id.to_s.empty?
127
+
128
+ response = request(@http, :get, "/v1/invocations/#{URI.encode_www_form_component(invocation_id)}")
129
+ raise Error, "invocation read answered #{response.code}" unless response.code.to_i == 200
130
+
131
+ decode_json(response.body)
132
+ end
133
+
134
+ # Keyless artifact ingress. Reuse a key only for the same bytes and media
135
+ # type after an uncertain response. The offer may impose a lower limit.
136
+ def commit(bytes, media_type, idempotency_key: nil)
137
+ raise Error, "artifact must contain 1 to #{MAX_ARTIFACT_BYTES} bytes" unless bytes.bytesize.between?(1, MAX_ARTIFACT_BYTES)
138
+ raise Error, "invalid media type" unless media_type.is_a?(String) && !media_type.empty? && media_type.bytesize <= 200 && media_type.strip == media_type
139
+
140
+ key = idempotency_key || new_key("ausca-artifact-")
141
+ validate_key(key)
142
+ digest = "sha256:#{Digest::SHA256.hexdigest(bytes)}"
143
+ body = JSON.generate(data_base64: Base64.strict_encode64(bytes), content_digest: digest,
144
+ media_type: media_type, idempotency_key: key)
145
+ response = begin
146
+ request(@http, :post, "/v1/artifacts", body: body)
147
+ rescue StandardError
148
+ raise Error, "artifact commit uncertain; reuse key #{key}"
149
+ end
150
+ raise Error, "artifact ingress answered #{response.code}; reuse key #{key}" unless response.code.to_i == 200
151
+
152
+ result = decode_json(response.body)
153
+ artifact = result.fetch("artifact")
154
+ unless result.fetch("status") == "stored" && artifact.fetch("artifact_ref").is_a?(String) &&
155
+ artifact.fetch("artifact_ref").bytesize.between?(1, 512) &&
156
+ artifact.fetch("content_digest") == digest && artifact.fetch("media_type") == media_type &&
157
+ artifact.fetch("size_bytes") == bytes.bytesize && artifact.fetch("created_at").is_a?(String)
158
+ raise Error, "artifact ingress returned mismatched evidence; reuse key #{key}"
159
+ end
160
+ artifact.slice("artifact_ref", "content_digest", "media_type")
161
+ rescue KeyError, JSON::ParserError
162
+ raise Error, "artifact ingress returned malformed evidence; reuse key #{key}"
163
+ end
164
+
165
+ # The HTTP access route requires a header and no request body.
166
+ def access(artifact_ref, idempotency_key: nil)
167
+ raise Error, "invalid artifact reference" unless artifact_ref.is_a?(String) && artifact_ref.bytesize.between?(1, 512)
168
+
169
+ key = idempotency_key || new_key("ausca-")
170
+ validate_key(key)
171
+ response = request(@http, :post,
172
+ "/v1/artifacts/#{URI.encode_www_form_component(artifact_ref)}/access",
173
+ headers: { "Idempotency-Key" => key })
174
+ raise Error, "artifact access answered #{response.code}" unless response.code.to_i == 200
175
+
176
+ result = decode_json(response.body)
177
+ artifact = result.fetch("artifact")
178
+ unless result.fetch("status") == "ready" && artifact.fetch("artifact_ref") == artifact_ref &&
179
+ artifact.fetch("content_digest").is_a?(String) && artifact.fetch("download_url").is_a?(String) &&
180
+ artifact.fetch("expires_at").is_a?(String)
181
+ raise Error, "artifact access returned invalid evidence"
182
+ end
183
+ artifact
184
+ rescue KeyError, JSON::ParserError
185
+ raise Error, "artifact access returned malformed evidence"
186
+ end
187
+
188
+ private
189
+
190
+ def request(transport, method, path, body: nil, headers: {})
191
+ raise Error, "invalid resource path" unless path.start_with?("/") && !path.start_with?("//")
192
+
193
+ uri = URI.parse("#{@origin}#{path}")
194
+ request = method == :post ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
195
+ request["Content-Type"] = "application/json" unless body.nil?
196
+ headers.each { |name, value| request[name] = value }
197
+ request.body = body unless body.nil?
198
+ response = transport.call(uri, request)
199
+ raise Error, "response exceeds #{MAX_RESPONSE_BYTES} bytes" if response.body.to_s.bytesize > MAX_RESPONSE_BYTES
200
+
201
+ response
202
+ end
203
+
204
+ def decode_json(body)
205
+ JSON.parse(body.to_s)
206
+ rescue JSON::ParserError => error
207
+ raise Error, "invalid JSON response: #{error.message}"
208
+ end
209
+
210
+ def identity(body)
211
+ { offer_id: body.fetch("offer_id"), idempotency_key: body.fetch("idempotency_key") }
212
+ end
213
+
214
+ def validate_offer(bound)
215
+ route = bound.fetch("route")
216
+ path = route.fetch("path")
217
+ valid = bound.fetch("offer_id").is_a?(String) && !bound.fetch("offer_id").empty? &&
218
+ %w[revision revision_digest].all? { |field| bound.fetch(field).is_a?(String) && !bound.fetch(field).empty? } &&
219
+ %w[input_schema output_schema].all? { |field| !bound.fetch(field).fetch("digest").to_s.empty? } &&
220
+ route.fetch("method") == "POST" && path.is_a?(String) && path.start_with?("/v1/") &&
221
+ !path.include?("..") && !path.match?(/[?#]/)
222
+ raise Error, "invalid catalog binding for #{bound['offer_id'].inspect}" unless valid
223
+ rescue KeyError, NoMethodError
224
+ raise Error, "malformed catalog offer binding"
225
+ end
226
+
227
+ def validate_key(key)
228
+ valid = key.is_a?(String) && key.valid_encoding? && key.bytesize.between?(16, 128) &&
229
+ key.strip == key && !key.match?(/[\x00-\x1f\x7f]/)
230
+ raise Error, "idempotency key must be 16 to 128 clean UTF-8 bytes" unless valid
231
+ end
232
+
233
+ def validate_attribution(value)
234
+ valid = value.is_a?(Hash) && value.keys.all? { |key| %w[source campaign].include?(key.to_s) } &&
235
+ label?(value[:source] || value["source"], 64) &&
236
+ (value[:campaign] || value["campaign"]).then { |campaign| campaign.nil? || label?(campaign, 128) }
237
+ raise Error, "attribution source and campaign must be bounded lowercase labels" unless valid
238
+ end
239
+
240
+ def label?(value, max)
241
+ value.is_a?(String) && value.bytesize <= max && value.match?(/\A[a-z0-9][a-z0-9._-]*\z/)
242
+ end
243
+
244
+ def new_key(prefix)
245
+ "#{prefix}#{SecureRandom.hex(16)}"
246
+ end
247
+ end
9
248
  end
metadata CHANGED
@@ -1,88 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ausca
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Kam Low
8
- autorequire:
7
+ - Ausca
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2014-04-06 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ~>
18
- - !ruby/object:Gem::Version
19
- version: '1.3'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ~>
25
- - !ruby/object:Gem::Version
26
- version: '1.3'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ! '>='
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- description: Ausca is a collection of automation utilities that you can use to fast-track
42
- the development of your online empire.
43
- email:
44
- - hello@sourcey.com
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Resolve active offers, bind paid invocations, recover uncertain calls,
13
+ and handle artifacts without embedding payment credentials.
45
14
  executables: []
46
15
  extensions: []
47
16
  extra_rdoc_files: []
48
17
  files:
49
- - .gitignore
50
- - Gemfile
51
- - LICENSE.txt
18
+ - LICENSE
52
19
  - README.md
53
- - Rakefile
54
- - ausca.gemspec
55
- - examples/feed.rss
56
- - examples/feed_joiner.rb
57
- - examples/twitter_bot.rb
58
20
  - lib/ausca.rb
59
- - lib/ausca/rss/joiner.rb
60
- - lib/ausca/rss/util.rb
61
- - lib/ausca/twitter/bot.rb
62
- - lib/ausca/util.rb
63
- - lib/ausca/version.rb
64
- homepage: http://ausca.com
21
+ homepage: https://ausca.com
65
22
  licenses:
66
23
  - MIT
67
- metadata: {}
68
- post_install_message:
24
+ metadata:
25
+ homepage_uri: https://ausca.com
26
+ source_uri: https://github.com/auscahq/ausca/tree/main/ruby/ausca
27
+ bug_tracker_uri: https://github.com/auscahq/ausca/issues
69
28
  rdoc_options: []
70
29
  require_paths:
71
30
  - lib
72
31
  required_ruby_version: !ruby/object:Gem::Requirement
73
32
  requirements:
74
- - - ! '>='
33
+ - - ">="
75
34
  - !ruby/object:Gem::Version
76
- version: '0'
35
+ version: '3.1'
77
36
  required_rubygems_version: !ruby/object:Gem::Requirement
78
37
  requirements:
79
- - - ! '>='
38
+ - - ">="
80
39
  - !ruby/object:Gem::Version
81
40
  version: '0'
82
41
  requirements: []
83
- rubyforge_project:
84
- rubygems_version: 2.1.9
85
- signing_key:
42
+ rubygems_version: 3.6.9
86
43
  specification_version: 4
87
- summary: Automation utilities and bots.
44
+ summary: Rail-neutral client for Ausca's live pay-per-call catalog
88
45
  test_files: []
data/.gitignore DELETED
@@ -1,17 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in ausca.gemspec
4
- gemspec
data/LICENSE.txt DELETED
@@ -1,22 +0,0 @@
1
- Copyright (c) 2014 Kam
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile DELETED
@@ -1 +0,0 @@
1
- require "bundler/gem_tasks"
data/ausca.gemspec DELETED
@@ -1,23 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'ausca/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "ausca"
8
- spec.version = Ausca::VERSION
9
- spec.authors = ["Kam Low"]
10
- spec.email = ["hello@sourcey.com"]
11
- spec.description = %q{Ausca is a collection of automation utilities that you can use to fast-track the development of your online empire.}
12
- spec.summary = %q{Automation utilities and bots.}
13
- spec.homepage = "http://ausca.com"
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
data/examples/feed.rss DELETED
@@ -1,309 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <rss version="2.0">
3
- <channel>
4
- <title>test title</title>
5
- <description>test description</description>
6
- <link>test link</link>
7
- <author>test author</author>
8
- <item>
9
- <title>Active mums 'have active children'</title>
10
- <description><![CDATA[There is a direct link between how physically active mothers are and how active their young children are, suggests a UK study.]]></description>
11
- <link>http://www.bbc.co.uk/news/health-26679906#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
12
- <pubDate>Mon, 24 Mar 2014 06:02:39 GMT</pubDate>
13
- </item>
14
- <item>
15
- <title>Police incompetent, says acid victim</title>
16
- <description><![CDATA[A woman left scarred when he friend attacked her with acid accuses Scotland Yard of incompetence, saying they initially claimed she had done it to herself.]]></description>
17
- <link>http://www.bbc.co.uk/news/uk-26710956#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
18
- <pubDate>Mon, 24 Mar 2014 06:01:06 GMT</pubDate>
19
- </item>
20
- <item>
21
- <title>Rain could increase river pollution</title>
22
- <description><![CDATA[Wetter winters in the future could increase agricultural pollution in Britain's rivers, say scientists.]]></description>
23
- <link>http://www.bbc.co.uk/news/science-environment-26682750#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
24
- <pubDate>Mon, 24 Mar 2014 06:00:20 GMT</pubDate>
25
- </item>
26
- <item>
27
- <title>Russian troops 'overrun Crimea base'</title>
28
- <description><![CDATA[Russian troops seize control of a Crimean naval base at Feodosia, the third such attack in 48 hours, Ukrainian officials tell the BBC.]]></description>
29
- <link>http://www.bbc.co.uk/news/world-europe-26710884#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
30
- <pubDate>Mon, 24 Mar 2014 05:49:47 GMT</pubDate>
31
- </item>
32
- <item>
33
- <title>Missing plane: China spots 'objects'</title>
34
- <description><![CDATA[A Chinese plane spots possible debris hours after it joined the search for the missing Malaysia Airlines plane in the southern Indian Ocean.]]></description>
35
- <link>http://www.bbc.co.uk/news/world-asia-26678492#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
36
- <pubDate>Mon, 24 Mar 2014 05:46:09 GMT</pubDate>
37
- </item>
38
- <item>
39
- <title>Taiwan students storm government HQ</title>
40
- <description><![CDATA[Dozens hurt as Taiwan police disperse students who had occupied government headquarters to protest at a trade deal with China.]]></description>
41
- <link>http://www.bbc.co.uk/news/world-asia-26705779#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
42
- <pubDate>Mon, 24 Mar 2014 05:06:07 GMT</pubDate>
43
- </item>
44
- <item>
45
- <title>Ex-Army chief calls for more troops</title>
46
- <description><![CDATA[The UK needs 3,000 extra troops to show it "takes its defence responsibilities seriously" in light of the Ukraine crisis, says a former head of the Army.]]></description>
47
- <link>http://www.bbc.co.uk/news/uk-26710855#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
48
- <pubDate>Mon, 24 Mar 2014 04:22:30 GMT</pubDate>
49
- </item>
50
- <item>
51
- <title>Rising death toll from US landslide</title>
52
- <description><![CDATA[Rescuers searching the area of Saturday's massive mudslide in the US state of Washington find eight bodies, with a number of people still missing.]]></description>
53
- <link>http://www.bbc.co.uk/news/world-us-canada-26703994#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
54
- <pubDate>Mon, 24 Mar 2014 03:58:55 GMT</pubDate>
55
- </item>
56
- <item>
57
- <title>Banners to line Crow funeral route</title>
58
- <description><![CDATA[Hundreds of trade unionists are expected to join the funeral procession of Rail, Maritime and Transport union leader Bob Crow.]]></description>
59
- <link>http://www.bbc.co.uk/news/uk-26707611#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
60
- <pubDate>Mon, 24 Mar 2014 03:45:35 GMT</pubDate>
61
- </item>
62
- <item>
63
- <title>Fraud 'costing NHS £5bn a year'</title>
64
- <description><![CDATA[Fraud is costing the NHS £5bn a year, with a further £2bn lost to financial errors, the former head of its anti-fraud section tells BBC Panorama.]]></description>
65
- <link>http://www.bbc.co.uk/news/health-26654001#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
66
- <pubDate>Mon, 24 Mar 2014 03:40:22 GMT</pubDate>
67
- </item>
68
- <item>
69
- <title>VIDEO: Deadly landslide in Washington state</title>
70
- <description><![CDATA[At least eight people have been killed and several badly injured in a huge landslide in a rural area of the north-western American state of Washington, officials say.]]></description>
71
- <link>http://www.bbc.co.uk/news/world-us-canada-26710342#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
72
- <pubDate>Mon, 24 Mar 2014 03:37:09 GMT</pubDate>
73
- </item>
74
- <item>
75
- <title>Japan PM Abe visits Ann Frank museum</title>
76
- <description><![CDATA[Japan's Shinzo Abe visits Amsterdam's Anne Frank House museum, weeks after 300 copies of her famous diary were vandalised in Tokyo.]]></description>
77
- <link>http://www.bbc.co.uk/news/world-asia-26710575#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
78
- <pubDate>Mon, 24 Mar 2014 02:58:01 GMT</pubDate>
79
- </item>
80
- <item>
81
- <title>Ceremony honours 'The Great Escape'</title>
82
- <description><![CDATA[A ceremony to commemorate the Great Escape, the famous breakout from German prisoner of war camp Stalag Luft III in 1944, is due to take place later.]]></description>
83
- <link>http://www.bbc.co.uk/news/uk-26706141#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
84
- <pubDate>Mon, 24 Mar 2014 02:13:37 GMT</pubDate>
85
- </item>
86
- <item>
87
- <title>VIDEO: Mexico's mariachi academy run by American</title>
88
- <description><![CDATA[Mexico's first mariachi academy plans global domination]]></description>
89
- <link>http://www.bbc.co.uk/news/magazine-26686725#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
90
- <pubDate>Mon, 24 Mar 2014 01:18:35 GMT</pubDate>
91
- </item>
92
- <item>
93
- <title>Elton John: On the Yellow Brick Road</title>
94
- <description><![CDATA[The story of Goodbye Yellow Brick Road by the people who made it]]></description>
95
- <link>http://www.bbc.co.uk/news/entertainment-arts-26648532#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
96
- <pubDate>Mon, 24 Mar 2014 01:17:41 GMT</pubDate>
97
- </item>
98
- <item>
99
- <title>Did Hyman Minsky find the secret behind financial crashes?</title>
100
- <description><![CDATA[Did this man find the secret behind financial crashes?]]></description>
101
- <link>http://www.bbc.co.uk/news/magazine-26680993#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
102
- <pubDate>Mon, 24 Mar 2014 01:14:46 GMT</pubDate>
103
- </item>
104
- <item>
105
- <title>Cows connected to web to boost milk</title>
106
- <description><![CDATA[Scottish dairy famers are connecting their cows to the internet to help track the animal's health and improve their milk yield.]]></description>
107
- <link>http://www.bbc.co.uk/news/uk-scotland-scotland-business-26705812#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
108
- <pubDate>Mon, 24 Mar 2014 01:09:34 GMT</pubDate>
109
- </item>
110
- <item>
111
- <title>Scots constitution to be published</title>
112
- <description><![CDATA[An interim written Scottish constitution will be published this summer, the country's deputy first minister announces.]]></description>
113
- <link>http://www.bbc.co.uk/news/uk-scotland-scotland-politics-26707495#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
114
- <pubDate>Mon, 24 Mar 2014 00:53:24 GMT</pubDate>
115
- </item>
116
- <item>
117
- <title>New tool to help catch child abusers</title>
118
- <description><![CDATA[Victims of child sexual abuse could soon be identified more quickly, thanks to law enforcement agency work on a cloud-based archive of abuse material.]]></description>
119
- <link>http://www.bbc.co.uk/news/technology-26612059#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
120
- <pubDate>Mon, 24 Mar 2014 00:43:44 GMT</pubDate>
121
- </item>
122
- <item>
123
- <title>Apollo collapse due to 'old' materials</title>
124
- <description><![CDATA[A ceiling collapse at London's Apollo Theatre has been put down to weak and old materials, the BBC has learned.]]></description>
125
- <link>http://www.bbc.co.uk/news/entertainment-arts-26689705#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
126
- <pubDate>Mon, 24 Mar 2014 00:41:05 GMT</pubDate>
127
- </item>
128
- <item>
129
- <title>Boy, 15, charged with girl's murder</title>
130
- <description><![CDATA[A teenage boy is charged with murdering 15-year-old Shereka Fab-Ann Marsh, who was shot in the neck in Hackney, east London.]]></description>
131
- <link>http://www.bbc.co.uk/news/uk-england-london-26710714#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
132
- <pubDate>Mon, 24 Mar 2014 00:38:30 GMT</pubDate>
133
- </item>
134
- <item>
135
- <title>Stamps honour 10 greats born in 1914</title>
136
- <description><![CDATA[Actor Sir Alec Guinness and poet Dylan Thomas are among 10 "remarkable" people - all born in 1914- who feature on a new set of stamps.]]></description>
137
- <link>http://www.bbc.co.uk/news/uk-26709537#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
138
- <pubDate>Mon, 24 Mar 2014 00:31:20 GMT</pubDate>
139
- </item>
140
- <item>
141
- <title>VIDEO: Five ways Crimea is becoming Russian</title>
142
- <description><![CDATA[Five ways Crimea will change as it becomes part of Russia]]></description>
143
- <link>http://www.bbc.co.uk/news/world-europe-26679240#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
144
- <pubDate>Mon, 24 Mar 2014 00:10:32 GMT</pubDate>
145
- </item>
146
- <item>
147
- <title>Johnnie Boden's passion for fashion</title>
148
- <description><![CDATA[How Johnnie Boden came to dress the UK's yummy mummies]]></description>
149
- <link>http://www.bbc.co.uk/news/business-26334330#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
150
- <pubDate>Mon, 24 Mar 2014 00:07:45 GMT</pubDate>
151
- </item>
152
- <item>
153
- <title>Fathers 'not taking paternity leave'</title>
154
- <description><![CDATA["Ingrained" attitudes among employers and fears about the loss of salary is preventing many men from taking paternity leave, a report has suggested.]]></description>
155
- <link>http://www.bbc.co.uk/news/business-26710507#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
156
- <pubDate>Mon, 24 Mar 2014 00:04:22 GMT</pubDate>
157
- </item>
158
- <item>
159
- <title>Russian UK tourist spending dives</title>
160
- <description><![CDATA[The UK's hotels and shops sees income from Russians fall 17% last month as visitor numbers fell back amid political unrest in Ukraine, says a report.]]></description>
161
- <link>http://www.bbc.co.uk/news/business-26708117#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
162
- <pubDate>Mon, 24 Mar 2014 00:02:47 GMT</pubDate>
163
- </item>
164
- <item>
165
- <title>New push for energy firm probe</title>
166
- <description><![CDATA[The consumer group Which? and the Federation of Small Businesses are asking regulators to investigate the "broken" energy market.]]></description>
167
- <link>http://www.bbc.co.uk/news/business-26704915#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
168
- <pubDate>Mon, 24 Mar 2014 00:00:12 GMT</pubDate>
169
- </item>
170
- <item>
171
- <title>Russia invasion fears and NHS fraud - front pages</title>
172
- <description><![CDATA[Tensions between Russia and the international community, NHS fraud claims and Samantha Cameron's onesie make headlines.]]></description>
173
- <link>http://www.bbc.co.uk/news/blogs-the-papers-26710333#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
174
- <pubDate>Sun, 23 Mar 2014 23:47:28 GMT</pubDate>
175
- </item>
176
- <item>
177
- <title>US actor James Rebhorn dies aged 65</title>
178
- <description><![CDATA[US character actor James Rebhorn, whose most recent role was in TV series Homeland, dies at the age of 65 after a long battle with skin cancer.]]></description>
179
- <link>http://www.bbc.co.uk/news/entertainment-arts-26710547#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
180
- <pubDate>Sun, 23 Mar 2014 23:30:12 GMT</pubDate>
181
- </item>
182
- <item>
183
- <title>Poll gains for French National Front</title>
184
- <description><![CDATA[France's far-right anti-immigration National Front party has made significant gains in local elections, according to early results and projections.]]></description>
185
- <link>http://www.bbc.co.uk/news/world-europe-26707588#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
186
- <pubDate>Sun, 23 Mar 2014 22:47:14 GMT</pubDate>
187
- </item>
188
- <item>
189
- <title>Real Madrid 3-4 Barcelona</title>
190
- <description><![CDATA[Lionel Messi's hat-trick helps Barcelona beat 10-man Real Madrid in a thrilling match to boost their La Liga title hopes.]]></description>
191
- <link>http://www.bbc.co.uk/sport/0/football/26690210</link>
192
- <pubDate>Sun, 23 Mar 2014 22:43:58 GMT</pubDate>
193
- </item>
194
- <item>
195
- <title>Afghanistan's first spaceman returns home</title>
196
- <description><![CDATA[From heroic spaceman to humble accountant]]></description>
197
- <link>http://www.bbc.co.uk/news/magazine-26648270#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
198
- <pubDate>Sun, 23 Mar 2014 21:47:21 GMT</pubDate>
199
- </item>
200
- <item>
201
- <title>Venezuela probes protest abuses</title>
202
- <description><![CDATA[Venezuela's attorney general, Luiza Ortega Diaz, says 60 cases of alleged human rights abuses during recent street protests are being investigated.]]></description>
203
- <link>http://www.bbc.co.uk/news/world-latin-america-26709707#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
204
- <pubDate>Sun, 23 Mar 2014 20:33:13 GMT</pubDate>
205
- </item>
206
- <item>
207
- <title>Turkey downs Syrian military jet</title>
208
- <description><![CDATA[Turkey shoots down a Syrian military jet it says violated its airspace, but Damascus condemns what it calls "blatant aggression".]]></description>
209
- <link>http://www.bbc.co.uk/news/world-middle-east-26706417#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
210
- <pubDate>Sun, 23 Mar 2014 20:07:21 GMT</pubDate>
211
- </item>
212
- <item>
213
- <title>Player arrested over taxi damage</title>
214
- <description><![CDATA[Former Newcastle United footballer Nile Ranger is arrested on suspicion of causing criminal damage to a taxi in Liverpool.]]></description>
215
- <link>http://www.bbc.co.uk/news/uk-england-merseyside-26706371#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
216
- <pubDate>Sun, 23 Mar 2014 19:26:31 GMT</pubDate>
217
- </item>
218
- <item>
219
- <title>Murray eases past Lopez in Miami</title>
220
- <description><![CDATA[Britain's Andy Murray beats Spaniard Feliciano Lopez in straight sets to reach the last 16 at the Sony Open in Miami.]]></description>
221
- <link>http://www.bbc.co.uk/sport/0/tennis/26705843</link>
222
- <pubDate>Sun, 23 Mar 2014 19:00:09 GMT</pubDate>
223
- </item>
224
- <item>
225
- <title>Deadly Ebola reaches Guinea capital</title>
226
- <description><![CDATA[An outbreak of the Ebola virus - which has already killed 59 people in Guinea - has reached the capital Conakry, the UN's children agency warns.]]></description>
227
- <link>http://www.bbc.co.uk/news/world-africa-26701733#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
228
- <pubDate>Sun, 23 Mar 2014 18:09:23 GMT</pubDate>
229
- </item>
230
- <item>
231
- <title>Where now for Wenger and Arsenal?</title>
232
- <description><![CDATA[Where does Arsenal manager Arsene Wenger go now after his 1,000th game in charge of the Gunners turned into a nightmare?]]></description>
233
- <link>http://www.bbc.co.uk/sport/0/football/26705642</link>
234
- <pubDate>Sun, 23 Mar 2014 17:39:50 GMT</pubDate>
235
- </item>
236
- <item>
237
- <title>Three arrested over city sex attack</title>
238
- <description><![CDATA[Three men are arrested following a serious sexual assault on a woman in north Belfast.]]></description>
239
- <link>http://www.bbc.co.uk/news/uk-northern-ireland-26708351#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
240
- <pubDate>Sun, 23 Mar 2014 17:12:00 GMT</pubDate>
241
- </item>
242
- <item>
243
- <title>Broad fined for umpire criticism</title>
244
- <description><![CDATA[England captain Stuart Broad is fined for questioning the umpires' decision to play on in a storm at the World Twenty20.]]></description>
245
- <link>http://www.bbc.co.uk/sport/0/cricket/26708635</link>
246
- <pubDate>Sun, 23 Mar 2014 17:04:19 GMT</pubDate>
247
- </item>
248
- <item>
249
- <title>Dewani 'to be extradited on 7 April'</title>
250
- <description><![CDATA[A man accused of arranging the murder of his wife on their honeymoon will be extradited to South Africa on 7 April, the government there says.]]></description>
251
- <link>http://www.bbc.co.uk/news/uk-england-bristol-26705011#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
252
- <pubDate>Sun, 23 Mar 2014 16:47:20 GMT</pubDate>
253
- </item>
254
- <item>
255
- <title>Tottenham Hotspur 3-2 Southampton</title>
256
- <description><![CDATA[Gylfi Sigurdsson comes off the bench to score a late winner for Tottenham against Southampton at White Hart Lane.]]></description>
257
- <link>http://www.bbc.co.uk/sport/0/football/26599215</link>
258
- <pubDate>Sun, 23 Mar 2014 16:12:52 GMT</pubDate>
259
- </item>
260
- <item>
261
- <title>VIDEO: Kinnock name 'not always an advantage'</title>
262
- <description><![CDATA[Stephen Kinnock, son of the former Labour leader Neil Kinnock, has been selected as the Labour candidate for Aberavon at the 2015 general election but says his family name does not necessarily work to his advantage.]]></description>
263
- <link>http://www.bbc.co.uk/news/uk-wales-26706905#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
264
- <pubDate>Sun, 23 Mar 2014 15:53:40 GMT</pubDate>
265
- </item>
266
- <item>
267
- <title>Police probe 'tragic' toddler death</title>
268
- <description><![CDATA[Police are investigating the "unexplained" death of a two-year-old boy after being called to what was described as a "serious incident" at a house.]]></description>
269
- <link>http://www.bbc.co.uk/news/uk-scotland-edinburgh-east-fife-26705113#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
270
- <pubDate>Sun, 23 Mar 2014 15:43:35 GMT</pubDate>
271
- </item>
272
- <item>
273
- <title>Four killed in Kenya church attack</title>
274
- <description><![CDATA[Four people are killed and a number are injured when gunmen open fire in a church near the Kenyan city of Mombasa, officials say.]]></description>
275
- <link>http://www.bbc.co.uk/news/world-africa-26707242#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
276
- <pubDate>Sun, 23 Mar 2014 14:55:49 GMT</pubDate>
277
- </item>
278
- <item>
279
- <title>NHS criticisms 'worry for patients'</title>
280
- <description><![CDATA[A nurses' leader says constant criticism of care in the NHS is "worrying for patients" and unfair to staff if claims are not investigated and substantiated.]]></description>
281
- <link>http://www.bbc.co.uk/news/uk-wales-26705033#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
282
- <pubDate>Sun, 23 Mar 2014 13:49:47 GMT</pubDate>
283
- </item>
284
- <item>
285
- <title>VIDEO: Bez on his plan to stand as MP</title>
286
- <description><![CDATA[Mark Berry, better known as Bez from Manchester band Happy Mondays, is planning to stand for parliament.]]></description>
287
- <link>http://www.bbc.co.uk/news/uk-politics-26697322#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
288
- <pubDate>Sun, 23 Mar 2014 13:24:06 GMT</pubDate>
289
- </item>
290
- <item>
291
- <title>Blackrock outlines Yes vote 'risks'</title>
292
- <description><![CDATA[The world's biggest investment fund manager says Scottish independence would bring "major uncertainties, costs and risks," for Scotland and the rest of the UK.]]></description>
293
- <link>http://www.bbc.co.uk/news/uk-scotland-scotland-politics-26705466#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
294
- <pubDate>Sun, 23 Mar 2014 13:15:31 GMT</pubDate>
295
- </item>
296
- <item>
297
- <title>VIDEO: Obamas visit Great Wall of China</title>
298
- <description><![CDATA[US First Lady Michelle Obama and her daughters visit the Great Wall of China on the fourth day of their week-long visit to the country.]]></description>
299
- <link>http://www.bbc.co.uk/news/world-us-canada-26705887#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
300
- <pubDate>Sun, 23 Mar 2014 12:35:17 GMT</pubDate>
301
- </item>
302
- <item>
303
- <title>Man charged in river murder inquiry</title>
304
- <description><![CDATA[A 28-year-old man is charged with murder after the body of a 45-year-old man was discovered partially submerged in a Cardiff river.]]></description>
305
- <link>http://www.bbc.co.uk/news/uk-wales-south-east-wales-26706019#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
306
- <pubDate>Sun, 23 Mar 2014 12:11:41 GMT</pubDate>
307
- </item>
308
- </channel>
309
- </rss>
@@ -1,19 +0,0 @@
1
- require_relative "../lib/ausca"
2
-
3
- rss = Ausca::RSS::Joiner.new({
4
- :feeds =>
5
- # http://www.topix.com/rss/popular/topstories
6
- # http://feeds.bbci.co.uk/news/rss.xml
7
- # http://www.nasa.gov/rss/lg_image_of_the_day.rss
8
- [ "http://www.topix.com/rss/popular/topstories", "http://feeds.bbci.co.uk/news/rss.xml" ],
9
- :max_items => 50,
10
- :output_path => "feed.rss",
11
-
12
- # RSS variables
13
- :version => "2.0",
14
- :title => "test title",
15
- :description => "test description",
16
- :link => "test link",
17
- :author => "test author"
18
- })
19
- rss.generate
@@ -1,15 +0,0 @@
1
- require_relative "../lib/ausca"
2
-
3
- # Instantiate the bot
4
- bot = Ausca::Twitter::Bot.new({
5
- :consumer_key => "TWITTER_API_KEY",
6
- :consumer_secret => "TWITTER_API_SECRET",
7
- :access_token => "TWITTER_ACCESS_TOKEN",
8
- :access_token_secret => "TWITTER_ACCESS_SECRET",
9
- :search => "#awesome -rt",
10
- :want_num_retweets => 1,
11
- :want_num_favorites => 3
12
- })
13
-
14
- # Run the bot once
15
- bot.run
@@ -1,104 +0,0 @@
1
- require 'nokogiri'
2
- require 'open-uri'
3
-
4
- module Ausca
5
- module RSS
6
- class Joiner
7
- def options
8
- @options ||= {
9
- # Array of source feed URLs to join
10
- feeds: [],
11
-
12
- # Max output feed items
13
- max_items: 20,
14
-
15
- # Output RSS file path
16
- output_path: "feed.rss",
17
-
18
- # RSS variables
19
- version: "2.0",
20
- title: "my title",
21
- description: "my description",
22
- link: "my link",
23
- author: "my author",
24
- }
25
- end
26
-
27
- def initialize(opts)
28
- options.merge!(opts)
29
- options[:feeds].to_a unless options[:feeds].is_a? Array
30
- end
31
-
32
- # Generates the output RSS feed
33
- def generate
34
- items = self.fetch
35
- items = self.filter items
36
-
37
- rss = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
38
- xml.rss(version: options[:version]) do
39
- xml.channel do
40
- xml.title options[:title]
41
- xml.description options[:description]
42
- xml.link options[:link]
43
- xml.author options[:author]
44
-
45
- items.each do |source_item|
46
- xml.item do
47
- xml.title source_item[:title]
48
- xml.description do
49
- xml.cdata source_item[:description] # CGI::unescape_html
50
- end
51
- xml.link source_item[:link]
52
- xml.category source_item[:category] unless source_item[:category].empty?
53
- xml.pubDate source_item[:pubDate].httpdate
54
-
55
- # optional image
56
- if source_item[:image]
57
- xml.image do
58
- xml.url source_item[:image]
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
66
-
67
- self.save rss.to_xml
68
- end
69
-
70
- # Fetch and combine the remote feeds
71
- def fetch
72
- items = []
73
- # File.open(options[:feeds], 'r').each_line { |f| }
74
- options[:feeds].each { |url|
75
- p "Fetching: #{url}"
76
- open(url) do |rss|
77
- doc = Nokogiri::XML(rss.read) # Nokogiri::XML::ParseOptions::NOCDATA
78
- doc.xpath('//item').map do |i|
79
- items << {
80
- title: i.xpath('title').text,
81
- description: i.xpath('description').text,
82
- link: RSS::Util.item_source_url(i),
83
- image: RSS::Util.item_image_url(i),
84
- category: i.xpath('category').text,
85
- pubDate: Time.parse(i.xpath('pubDate').text)
86
- }
87
- end
88
- end
89
- }
90
- items.sort_by { |k| k[:pubDate] }.reverse
91
- end
92
-
93
- # Filter the output feed items based on optional constraints
94
- def filter items
95
- items = items.first(options[:max_items]) if options[:max_items] > 0
96
- end
97
-
98
- def save rss
99
- File.write(options[:output_path], rss)
100
- #File.open(options[:output_path], 'w') { |f| rss }
101
- end
102
- end
103
- end
104
- end
@@ -1,36 +0,0 @@
1
- module Ausca
2
- module RSS
3
- class Util
4
- class << self
5
-
6
- # Get the source URL for an RSS item
7
- def item_source_url node
8
- url = node.at_xpath('source').attr('url') if node.at_xpath('source')
9
- url = (node.xpath('link').text if node.at_xpath('link')) unless url
10
- url
11
- end
12
-
13
- # Get an image from the image or content element
14
- def item_image_url item
15
- url = i.at_xpath('media|thumbnail').attr('url') if i.at_xpath('media|thumbnail') rescue nil
16
- url = i.at_xpath('enclosure').attr('url') unless url rescue nil
17
- #url = item.content.to_s[/img.*?src=\\\"(.*?)\\\"/i,1] unless url rescue nil
18
- url
19
- end
20
-
21
- def default_text item
22
- s = "#{item[:title]} #{item[:link]}"
23
- s += " ##{item[:category].gsub(/\s+/, "").downcase}" if item[:category]
24
- CGI::unescape_html s #.html_safe
25
- end
26
-
27
- def default_text_with_description item
28
- s = "#{item[:title]}: #{item[:description]} #{item[:link]}"
29
- s += " ##{item[:category].gsub(/\s+/, "").downcase}" if item[:category]
30
- CGI::unescape_html s #.html_safe
31
- end
32
-
33
- end
34
- end
35
- end
36
- end
@@ -1,180 +0,0 @@
1
- require 'twitter'
2
- require 'json'
3
-
4
- module Ausca
5
- module Twitter
6
-
7
- # Twitter bot for gaining followers by following, favouriting and retweeting
8
- # targeted users and content.
9
- #
10
- # TODO: Unfollow users after a few days if no followback
11
- class Bot
12
- def options
13
- @options ||= {
14
- :consumer_key => nil,
15
- :consumer_secret => nil,
16
- :access_token => nil,
17
- :access_token_secret => nil,
18
- :config => nil,
19
- :config_filename => "config.json",
20
- :search => "#popular -rt",
21
- :freind_follower_ratio => 0.75,
22
- :retweet_min_follower_count => 100,
23
- :want_num_retweets => 1,
24
- :want_num_favorites => 5,
25
- :max_loops => 5
26
- }
27
- end
28
-
29
- def initialize(opts)
30
- options.merge!(opts)
31
- load_config
32
- end
33
-
34
- # Create the Twitter REST client instance
35
- def client
36
- @client ||= ::Twitter::REST::Client.new do |config|
37
- config.consumer_key = options[:consumer_key]
38
- config.consumer_secret = options[:consumer_secret]
39
- config.access_token = options[:access_token]
40
- config.access_token_secret = options[:access_token_secret]
41
- end
42
- end
43
-
44
- # Load the config file if a filename option was provided
45
- def load_config
46
- # Load from options or file
47
- @config = options[:config]
48
- @config ||= (JSON.parse(IO.read(
49
- File.expand_path(options[:config_filename],
50
- File.dirname(__FILE__)))) rescue {}) if options[:config_filename]
51
- @config['following'] ||= {}
52
- end
53
-
54
- # Save the config file if a filename option was provided
55
- def save_config
56
- p @config.inspect
57
- File.open(options[:config_filename], 'w'){ |f| JSON.dump(@config, f) } if options[:config_filename]
58
- end
59
-
60
- # Run the bot action
61
- def run
62
- num_retweets = 0
63
- num_favorites = 0
64
- num_loops = 0
65
- filters = { max_id: nil }
66
-
67
- # Loop until we have reached our target number of follows, retweets and favs,
68
- # or hit the max number of loops
69
- while num_loops < options[:max_loops] &&
70
- (num_retweets < options[:want_num_retweets] ||
71
- num_favorites < options[:want_num_favorites])
72
- num_loops += 1
73
- p "*** Looping #{num_loops}"
74
-
75
- # @option options [String] :geocode Returns tweets by users located within a given radius of the given latitude/longitude. The location is preferentially taking from the Geotagging API, but will fall back to their Twitter profile. The parameter value is specified by "latitude,longitude,radius", where radius units must be specified as either "mi" (miles) or "km" (kilometers). Note that you cannot use the near operator via the API to geocode arbitrary locations; however you can use this geocode parameter to search near geocodes directly.
76
- # @option options [String] :lang Restricts tweets to the given language, given by an ISO 639-1 code.
77
- # @option options [String] :locale Specify the language of the query you are sending (only ja is currently effective). This is intended for language-specific clients and the default should work in the majority of cases.
78
- # @option options [String] :result_type Specifies what type of search results you would prefer to receive. Options are "mixed", "recent", and "popular". The current default is "mixed."
79
- # @option options [Integer] :count The number of tweets to return per page, up to a maximum of 100.
80
- # @option options [String] :until Optional. Returns tweets generated before the given date. Date should be formatted as YYYY-MM-DD.
81
- # @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID. There are limits to the number of Tweets which can be accessed through the API. If the limit of Tweets has occured since the since_id, the since_id will be forced to the oldest ID available.
82
- # @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
83
- # @option options [Boolean, String, Integer] :include_entities The tweet entities node will be disincluded when set to false.
84
- client.search(options[:search], filters).each do |tweet|
85
- filters[:max_id] = tweet.id
86
-
87
- #p "*** Looping #{tweet.text}"
88
-
89
- # Skip dead tweets
90
- next if tweet.lang != 'en'
91
-
92
- # Skip dead tweets
93
- next if tweet.retweet_count == 0 || tweet.favorite_count == 0
94
-
95
- # Skip unpopular or overpopular tweets
96
- next if tweet.retweet_count < 3 || tweet.retweet_count > 100#0
97
-
98
- # Skip tweets we have interacted with
99
- #next if tweet.retweeted || tweet.favorited
100
-
101
- # Skip users that aren't easy marks
102
- next if tweet.user.friends_count < (tweet.user.followers_count * options[:freind_follower_ratio])
103
-
104
- # Skip if already following
105
- next if @config['following'].has_key? tweet.user.id
106
-
107
- # Retweet the first most suitable tweet, and follow that user
108
- if num_retweets < options[:want_num_retweets] &&
109
- tweet.retweet_count > tweet.favorite_count &&
110
-
111
- # Ensure min followers count is met before we retweet
112
- tweet.user.followers_count > options[:retweet_min_follower_count]
113
-
114
- p "*** DO RT"
115
- print_tweet tweet
116
-
117
- begin
118
- @config['following'][tweet.user.id] = Time.now.utc
119
- client.follow!(tweet.user)
120
- client.retweet!(tweet)
121
- num_retweets += 1
122
- rescue => e
123
- p "*** RT ERROR: #{e}"
124
- ensure
125
- end
126
- next
127
- end
128
-
129
- # Favourite the first most suitable tweet, and follow that user
130
- if num_favorites < options[:want_num_favorites] #&& tweet.favorite_count > tweet.retweet_count
131
- p "*** DO FAV"
132
- print_tweet tweet
133
-
134
- begin
135
- @config['following'][tweet.user.id] = Time.now.utc
136
- client.follow!(tweet.user)
137
- client.favorite!(tweet)
138
- num_favorites += 1
139
- rescue => e
140
- p "*** FAV ERROR: #{e}"
141
- ensure
142
- end
143
- next
144
- end
145
- end
146
- end
147
-
148
- # # Loop followed users and unfollow if no followback after 5 days
149
- # now = Time.now
150
- # days = 5
151
- # p @config.inspect
152
- # @config['following'].each do |id,time|
153
- # expired = (Time.parse(time) + (60 * 60 * 24 * days)) < now
154
- # if expired
155
- # # TODO
156
- # # check if following us
157
- # # => - remove entry if true
158
- # # => - remove firend and entry if false
159
- # end
160
- # end
161
-
162
- save_config
163
- end
164
-
165
- # Print useful information about a tweet
166
- def print_tweet tweet
167
- puts "---------------------------------------"
168
- puts tweet.text
169
- puts "favorite_count: #{tweet.favorite_count}"
170
- puts "retweet_count: #{tweet.retweet_count}"
171
- puts "retweeted #{tweet.retweeted}"
172
- puts "favorited #{tweet.favorited}"
173
- puts "source #{tweet.source}"
174
- puts "lang #{tweet.lang}"
175
- puts "user friends #{tweet.user.friends_count}"
176
- puts "user followers #{tweet.user.followers_count}"
177
- end
178
- end
179
- end
180
- end
data/lib/ausca/util.rb DELETED
@@ -1,14 +0,0 @@
1
- module Ausca
2
- class Util
3
-
4
- # Load a JSON config file
5
- #def load_json_config path
6
- # JSON.parse(IO.read(File.expand_path(path, File.dirname(__FILE__)))) rescue {}
7
- #end
8
-
9
- # Save a JSON config file if a filename option was provided
10
- #def save_json_config path, object
11
- # File.open(path, 'w') { |f| JSON.dump(object, f) }
12
- #end
13
- end
14
- end
data/lib/ausca/version.rb DELETED
@@ -1,3 +0,0 @@
1
- module Ausca
2
- VERSION = "0.1.1"
3
- end