turbovax 0.0.2pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Turbovax
6
+ # Captures configuration required to fetch and process data from a specific vaccine website
7
+ class Portal
8
+ class << self
9
+ # @!macro [attach] definte_parameter
10
+ # @method $1
11
+ # $3
12
+ # @return [$2]
13
+ # @example $4
14
+ # $5
15
+ def self.definte_parameter(
16
+ attribute, _doc_return_type, _explanation = nil, _explanation = nil,
17
+ _example = nil
18
+ )
19
+ # metaprogramming magic so that
20
+ # 1) attributes can be defined via DSL
21
+ # 2) attributes can be fetched when method is called without any parameters
22
+ # 3) attributes can saved static variables or blocks that can be called (for dynamic)
23
+ # might be better to refactor in the future
24
+ define_method attribute do |argument = nil, &block|
25
+ variable = nil
26
+ block_exists =
27
+ begin
28
+ variable = instance_variable_get("@#{attribute}")
29
+ variable.is_a?(Proc)
30
+ rescue StandardError
31
+ false
32
+ end
33
+
34
+ if !variable.nil?
35
+ block_exists ? variable.call(argument) : variable
36
+ else
37
+ instance_variable_set("@#{attribute}", argument || block)
38
+ end
39
+ end
40
+ end
41
+
42
+ definte_parameter :name, String, "Full name of portal", "Name",
43
+ "'New York City Vaccine Website'"
44
+ definte_parameter :key, String, "Unique identifier for portal", "Key", "'nyc_vax'"
45
+ definte_parameter :public_url,
46
+ String,
47
+ "Link to public facing website", "Full URL",
48
+ "'https://www.turbovax.info/'"
49
+
50
+ definte_parameter :request_headers, Hash, "Key:value mapping of HTTP request headers",
51
+ "Specify user agent and cookies", "{ 'user-agent': 'Mozilla/5.0', " \
52
+ "'cookies': 'ABC' }"
53
+ definte_parameter :request_http_method, Symbol,
54
+ "Turbovax::Constants::GET_REQUEST_METHOD or " \
55
+ "Turbovax::Constants::POST_REQUEST_METHOD"
56
+ definte_parameter :api_url, String, "Full API URL", "Example Turbovax endpoint",
57
+ "'https://api.turbovax.info/v1/dashboard'"
58
+ definte_parameter :api_url_variables, Hash,
59
+ "Hash or block that is interpolated ",
60
+ '
61
+ api_url_variables do |extra_params|
62
+ {
63
+ site_id: NAME_TO_ID_MAPPING[extra_params[:name]],
64
+ date: extra_params.strftime("%F"),
65
+ }
66
+ end
67
+
68
+ # before api_url_variables interpolation
69
+ api_url "https://api.turbovax.info/v1/sites/%{site_id}/%{date}"
70
+
71
+ # after api_url_variables interpolation
72
+ api_url "https://api.turbovax.info/v1/sites/8888/2021-08-08"
73
+ '
74
+ # definte_parameter :request_body, Hash,
75
+ # "Hash (or block evaluates to a hash) that is used to in a POST request",
76
+ # '
77
+ # request_body do |extra_params|
78
+ # {
79
+ # site_id: NAME_TO_ID_MAPPING[extra_params[:name]],
80
+ # date: extra_params.strftime("%F"),
81
+ # }
82
+ # end
83
+ # '
84
+
85
+ # Block that will called after raw data is fetched from API. Must return list of Location
86
+ # instances
87
+ # @param [Array] args passed from [Turbovax::DataFetcher]
88
+ # @param [Block] block stored as a class instance variable
89
+ # @return [Array<Turbovax::Location>]
90
+ # @example Parse API responses from turbovax.info
91
+ # parse_response do |response|
92
+ # response_json = JSON.parse(response)
93
+ # response_json["locations"].map do |location|
94
+ # Turbovax::Location.new(
95
+ # name: location["name"],
96
+ # time_zone: "America/New_York",
97
+ # data: {
98
+ # is_available: location["is_available"],
99
+ # appointment_count: location["appointments"]["count"],
100
+ # appointments: [{
101
+ # time: "2021-04-19T17:21:15-04:00",
102
+ # }]
103
+ # }
104
+ # )
105
+ # end
106
+ # end
107
+ def parse_response(*args, &block)
108
+ if args.size.positive? && !@parse_response.nil?
109
+ @parse_response.call(*args)
110
+ elsif !block.nil?
111
+ @parse_response = block
112
+ else
113
+ {}
114
+ end
115
+ end
116
+
117
+ # Block that will be executed and then appended to API url path. The extra_params variable is
118
+ # provided by Turbovax::DataFetcher.
119
+ # When specified, this will overwrite any query string that is already present in api_url
120
+ #
121
+ # @param [Array] args passed from [Turbovax::DataFetcher]
122
+ # @param [Block] block stored as a class instance variable
123
+ # @return [Hash]
124
+ # @example Append date and noCache to URL
125
+ # # result: /path?date=2021-08-08&noCache=0.123
126
+ # api_query_params do |extra_params|
127
+ # {
128
+ # date: extra_params[:date].strftime("%F"),
129
+ # noCache: rand,
130
+ # }
131
+ # end
132
+ def api_query_params(*args, &block)
133
+ if args.size.positive? && !@api_query_params.nil?
134
+ @api_query_params.call(*args)
135
+ elsif !block.nil?
136
+ @api_query_params = block
137
+ else
138
+ {}
139
+ end
140
+ end
141
+
142
+ def request_body(*args, &block)
143
+ if args.size.positive? && !@request_body.nil?
144
+ @request_body.call(*args)
145
+ elsif !block.nil?
146
+ @request_body = block
147
+ else
148
+ {}.to_json
149
+ end
150
+ end
151
+
152
+ # Returns base API URL (used when creating Faraday connection)
153
+ def api_base_url
154
+ "#{api_uri_object.scheme}://#{api_uri_object.hostname}"
155
+ end
156
+
157
+ # Returns API URL path (used when making Faraday requests)
158
+ def api_path
159
+ "#{api_uri_object.path}?#{api_uri_object.query}"
160
+ end
161
+
162
+ # Calls parse_response and assigns portal to each location so user doesn't need to do
163
+ # this by themselves
164
+ def parse_response_with_portal(response, extra_params)
165
+ parse_response(response, extra_params).map do |location|
166
+ location.portal ||= self
167
+ location
168
+ end
169
+ end
170
+
171
+ private
172
+
173
+ def api_uri_object
174
+ @api_uri_object ||= URI(api_url % api_url_variables)
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "twitter"
4
+
5
+ module Turbovax
6
+ # Helper class that wraps around Twitter gem
7
+ class TwitterClient
8
+ def self.client
9
+ @client ||= Twitter::REST::Client.new do |config|
10
+ config.consumer_key = Turbovax.twitter_credentials[:consumer_key]
11
+ config.consumer_secret = Turbovax.twitter_credentials[:consumer_secret]
12
+ config.access_token = Turbovax.twitter_credentials[:access_token]
13
+ config.access_token_secret = Turbovax.twitter_credentials[:access_token_secret]
14
+ end
15
+ end
16
+
17
+ def self.send_tweet(message, reply_to_id: nil)
18
+ response = client.update(message, in_reply_to_status_id: reply_to_id)
19
+ Turbovax.logger.info("[Turbovax::Twitter::Client] send_tweet (#{response.id})")
20
+ response
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Turbovax
4
+ VERSION = "0.0.2pre"
5
+ end
data/lib/turbovax.rb ADDED
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ require_relative "turbovax/constants"
6
+ require_relative "turbovax/version"
7
+ require_relative "turbovax/portal"
8
+ require_relative "turbovax/location"
9
+ require_relative "turbovax/appointment"
10
+ require_relative "turbovax/data_fetcher"
11
+
12
+ require_relative "turbovax/twitter_client"
13
+ require_relative "turbovax/handlers/location_handler"
14
+
15
+ # Turbovax gem
16
+ module Turbovax
17
+ class InvalidRequestTypeError < StandardError; end
18
+
19
+ def self.configure
20
+ yield self
21
+ end
22
+
23
+ def self.logger
24
+ @logger ||= Logger.new($stdout, level: Logger::INFO)
25
+ end
26
+
27
+ def self.logger=(logger)
28
+ if logger.nil?
29
+ self.logger.level = Logger::FATAL
30
+ return self.logger
31
+ end
32
+
33
+ @logger = logger
34
+ end
35
+
36
+ def self.twitter_enabled
37
+ # enable twitter by default
38
+ @twitter_enabled = true if @twitter_enabled.nil?
39
+ @twitter_enabled
40
+ end
41
+
42
+ def self.twitter_enabled=(twitter_enabled)
43
+ @twitter_enabled = twitter_enabled
44
+ end
45
+
46
+ def self.twitter_credentials
47
+ raise NotImplementedError, "no twitter credentials provided" if @twitter_credentials.nil?
48
+
49
+ @twitter_credentials
50
+ end
51
+
52
+ def self.twitter_credentials=(twitter_credentials)
53
+ @twitter_credentials = twitter_credentials
54
+ end
55
+
56
+ def self.faraday_logging_config
57
+ @faraday_logging_config ||= { headers: false, bodies: false, log_level: :info }
58
+ end
59
+
60
+ def self.faraday_logging_config=(faraday_logging_config)
61
+ @faraday_logging_config = faraday_logging_config
62
+ end
63
+ end
data/turbovax.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/turbovax/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "turbovax"
7
+ spec.version = Turbovax::VERSION
8
+ spec.authors = ["Huge Ma"]
9
+ spec.email = ["huge@turbovax.info"]
10
+ spec.license = "AGPLv3"
11
+
12
+ spec.summary = "Quickly build vaccine twitter bots"
13
+ spec.description = spec.summary
14
+ spec.homepage = "https://github.com/hugem/turbovax-gem"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/hugem/turbovax-gem"
19
+ spec.metadata["changelog_uri"] = "https://github.com/hugem/turbovax-gem/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "activesupport", "~> 6.0", ">= 6.0.0.1"
31
+ spec.add_dependency "faraday", "~> 0.17"
32
+ spec.add_dependency "twitter", "~> 7.0"
33
+
34
+ # For more information and examples about making a new gem, checkout our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turbovax
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2pre
5
+ platform: ruby
6
+ authors:
7
+ - Huge Ma
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 6.0.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '6.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 6.0.0.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: faraday
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.17'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.17'
47
+ - !ruby/object:Gem::Dependency
48
+ name: twitter
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '7.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '7.0'
61
+ description: Quickly build vaccine twitter bots
62
+ email:
63
+ - huge@turbovax.info
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - ".github/workflows/main.yml"
69
+ - ".gitignore"
70
+ - ".rspec"
71
+ - ".rubocop.yml"
72
+ - CHANGELOG.md
73
+ - CODE_OF_CONDUCT.md
74
+ - Gemfile
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - bin/console
79
+ - bin/setup
80
+ - lib/turbovax.rb
81
+ - lib/turbovax/appointment.rb
82
+ - lib/turbovax/constants.rb
83
+ - lib/turbovax/data_fetcher.rb
84
+ - lib/turbovax/handlers/location_handler.rb
85
+ - lib/turbovax/location.rb
86
+ - lib/turbovax/portal.rb
87
+ - lib/turbovax/twitter_client.rb
88
+ - lib/turbovax/version.rb
89
+ - turbovax.gemspec
90
+ homepage: https://github.com/hugem/turbovax-gem
91
+ licenses:
92
+ - AGPLv3
93
+ metadata:
94
+ homepage_uri: https://github.com/hugem/turbovax-gem
95
+ source_code_uri: https://github.com/hugem/turbovax-gem
96
+ changelog_uri: https://github.com/hugem/turbovax-gem/CHANGELOG.md
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 2.5.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.3.1
111
+ requirements: []
112
+ rubygems_version: 3.1.4
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Quickly build vaccine twitter bots
116
+ test_files: []