turbovax 0.0.2pre

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.
@@ -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: []