bianchi 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6fe9c6aabbf8e704a9ea9403896cc71c9aedf9a5f8bf5a5433469dba9e80d40b
4
- data.tar.gz: 4caa2699c795f348136640d2300e636c8ce2848daf7605a0596749507034286a
3
+ metadata.gz: 3a8d03b012d92b96f021d80a4ce43c668d75f7003978feb81b189cdcb71952be
4
+ data.tar.gz: b1c13d1ead41b898177e6915f06d134a0a762fb367b95cadbbb688d031c4767a
5
5
  SHA512:
6
- metadata.gz: 3f56b2bf18384a755a4e6022b457e450c54efeb088926e112fca3b0ba6a7732a3762d83243a30572fd704c0131c816154e7cf4bbb3548fb489af5c0f11bdbec9
7
- data.tar.gz: f763c36ef9616506fc87223383bb73487f0fc37901e8e14e99078970ad86c618b97f6b4602ef8140f95d182d23f62c23878fe72c68db9b84f3075ed3e5db8419
6
+ metadata.gz: 5178fa0c0e7a8b65829b004d348b73dc74a0305789e03edbdb0af2fe9ac1236281dd31835052281791289b7a48912b918ec536969aa27de484d16a383a8edcf3
7
+ data.tar.gz: 7b961616077c63d17997d26837b1207bfd3718f5abc27e34a4fdb15ebbea3ec0b7754c846d0cbc1e77ba2984f974bff259e7100716dcf50b1af12f8e2d34346c
@@ -0,0 +1,45 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '*'
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ rubocop:
13
+ runs-on: ubuntu-latest
14
+
15
+ services:
16
+ redis:
17
+ image: redis
18
+ ports:
19
+ - 6379:6379
20
+ options: --entrypoint redis-server
21
+
22
+ steps:
23
+ - name: Checkout code
24
+ uses: actions/checkout@v4.1.1
25
+
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1.171.0
28
+ with:
29
+ ruby-version: 3.0
30
+
31
+ - name: Install dependencies
32
+ run: |
33
+ gem install bundler
34
+ bundle install
35
+
36
+ - name: Run RuboCop
37
+ run: bundle exec rubocop
38
+
39
+ - name: Start Redis
40
+ run: docker ps -a
41
+
42
+ - name: Run RSpec tests
43
+ env:
44
+ REDIS_URL: redis://localhost:6379
45
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+ .byebug_history
data/.rubocop.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
3
  TargetRubyVersion: 3.0
4
+ SuggestExtensions: false
4
5
 
5
6
  Style/StringLiterals:
6
7
  Enabled: true
@@ -18,3 +19,4 @@ Metrics/MethodLength:
18
19
 
19
20
 
20
21
 
22
+
@@ -0,0 +1,5 @@
1
+ {
2
+ "cSpell.words": [
3
+ "appsnmobile"
4
+ ]
5
+ }
data/Gemfile CHANGED
@@ -9,6 +9,6 @@ gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
11
 
12
- gem "rubocop", "~> 0.80"
12
+ gem "rubocop", "~> 1.60", ">= 1.60.2"
13
13
 
14
14
  gem "byebug"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bianchi (0.1.0)
4
+ bianchi (0.1.2)
5
5
  activesupport
6
6
  json
7
7
  redis
@@ -32,10 +32,11 @@ GEM
32
32
  i18n (1.14.1)
33
33
  concurrent-ruby (~> 1.0)
34
34
  json (2.7.1)
35
+ language_server-protocol (3.17.0.3)
35
36
  minitest (5.21.2)
36
37
  mutex_m (0.2.0)
37
38
  parallel (1.24.0)
38
- parser (3.3.0.2)
39
+ parser (3.3.0.5)
39
40
  ast (~> 2.4.1)
40
41
  racc
41
42
  racc (1.7.3)
@@ -60,15 +61,17 @@ GEM
60
61
  diff-lcs (>= 1.2.0, < 2.0)
61
62
  rspec-support (~> 3.12.0)
62
63
  rspec-support (3.12.1)
63
- rubocop (0.93.1)
64
+ rubocop (1.60.2)
65
+ json (~> 2.3)
66
+ language_server-protocol (>= 3.17.0)
64
67
  parallel (~> 1.10)
65
- parser (>= 2.7.1.5)
68
+ parser (>= 3.3.0.2)
66
69
  rainbow (>= 2.2.2, < 4.0)
67
- regexp_parser (>= 1.8)
68
- rexml
69
- rubocop-ast (>= 0.6.0)
70
+ regexp_parser (>= 1.8, < 3.0)
71
+ rexml (>= 3.2.5, < 4.0)
72
+ rubocop-ast (>= 1.30.0, < 2.0)
70
73
  ruby-progressbar (~> 1.7)
71
- unicode-display_width (>= 1.4.0, < 2.0)
74
+ unicode-display_width (>= 2.4.0, < 3.0)
72
75
  rubocop-ast (1.30.0)
73
76
  parser (>= 3.2.1.0)
74
77
  ruby-progressbar (1.13.0)
@@ -76,9 +79,10 @@ GEM
76
79
  thor (1.3.0)
77
80
  tzinfo (2.0.6)
78
81
  concurrent-ruby (~> 1.0)
79
- unicode-display_width (1.8.0)
82
+ unicode-display_width (2.5.0)
80
83
 
81
84
  PLATFORMS
85
+ arm64-darwin-22
82
86
  x86_64-darwin-23
83
87
 
84
88
  DEPENDENCIES
@@ -86,7 +90,7 @@ DEPENDENCIES
86
90
  byebug
87
91
  rake (~> 13.0)
88
92
  rspec (~> 3.0)
89
- rubocop (~> 0.80)
93
+ rubocop (~> 1.60, >= 1.60.2)
90
94
 
91
95
  BUNDLED WITH
92
96
  2.2.3
data/README.md CHANGED
@@ -1,43 +1,196 @@
1
- # bianchi
1
+ # Bianchi
2
+ A DSL (Domain-Specific Language) and a minimalist framework in Ruby, tailored for USSD development. Structured around a menu page approach, Bianchi offers a comprehensive suite of methods and generators, streamlining the process of building USSD applications efficiently and easily.
3
+ A basic test run with bianchi can be found here https://github.com/SydDaps/sinatra_bianchi_example
2
4
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/bianchi`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ ## Installation
6
+ Add `gem 'bianchi', '~> 0.1.1'` to your gem file and run `bundle install`
4
7
 
5
- TODO: Delete this and the text above, and describe your gem
8
+ Bianchi relies on Redis and requires the `REDIS_URL` environment variable to be set, pointing to your Redis instance. Ensure Redis is installed, and running, and that REDIS_URL is correctly configured before using Bianchi.
6
9
 
7
- ## Installation
10
+ ## Getting Started
11
+
12
+ To initialize a new USSD project, generate a project directory using Bianchi's command-line interface. Use the following Ruby command:
13
+
14
+ ```ruby
15
+ bundle exec bianchi setup -p provider_name
16
+ ```
17
+
18
+ Replace `provider_name` with your desired provider. Currently supported providers include: africastalking.
19
+
20
+ This command creates a `ussd/engine.rb` file in the project root directory. Here's a sample content of `ussd/engine.rb`:
21
+
22
+ ```ruby
23
+ module USSD
24
+ class Engine
25
+ def self.start(params)
26
+ Bianchi::USSD::Engine.start(params, provider: :africastalking) do
27
+ # e.g menu :main, options
28
+ end
29
+ end
30
+ end
31
+ end
32
+ ```
33
+
34
+ To define menus for your USSD applications' engine instance, use the following command:
35
+
36
+ ```ruby
37
+ menu :menu_name, options
38
+ ```
39
+
40
+ For example, let's define our first menu, which serves as the initial menu of the application, and let's call this the main menu:
41
+
42
+ ```ruby
43
+ menu :main, initial: true
44
+ ```
45
+ now that we have the initial menu up let's generate our pages for that menu using Bianchi's command-line interface. Use the following Ruby command:
46
+ ```ruby
47
+ command: bundle exec bianchi g menu menu_name page page_number
48
+ example: bianchi g menu main page 1
49
+ ```
50
+ This creates a `ussd/main_menu/page_1` file in the project root directory. Here's a sample content of the file:
51
+
52
+ ```ruby
53
+ module USSD
54
+ module MainMenu
55
+ class Page1 < Bianchi::USSD::Page
56
+ def request; end
57
+
58
+ def response; end
59
+ end
60
+ end
61
+ end
62
+ ```
8
63
 
9
- Add this line to your application's Gemfile:
64
+ In the `ussd/main_menu/page_1` file, the main application code goes into the `request` and `response` methods. Here's a sample code to illustrate the usage:
10
65
 
11
66
  ```ruby
12
- gem 'bianchi'
67
+ module USSD
68
+ module MainMenu
69
+ class Page1 < Bianchi::USSD::Page
70
+ def request
71
+ render_and_await(request_body)
72
+ end
73
+
74
+ def response
75
+ case session_input_body
76
+ when "1"
77
+ redirect_to_greetings_menu_page_1
78
+ when "2"
79
+ redirect_to_repeat_menu_page_1
80
+ else
81
+ render_and_await("invalid input \n" + request_body)
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def request_body
88
+ <<~MSG
89
+ Welcome
90
+ 1. Greetings
91
+ 2. Repeat my name
92
+ MSG
93
+ end
94
+ end
95
+ end
96
+ end
13
97
  ```
14
98
 
15
- And then execute:
99
+ In this example, when a page is requested, you send some information, and the end user submits data for that request. The `response` method processes the response data and can move to a new page request, end the session, or send a response from there.
100
+ ### Page Renders
16
101
 
17
- $ bundle install
102
+ In Bianchi USSD applications, any method that starts with `render` sends data back to the user. Typically, we either send data and expect a response or send data and terminate the session. Here are the two main methods for rendering pages:
18
103
 
19
- Or install it yourself as:
104
+ - `render_and_await(string)`: Responds to the user and expects the user to respond.
20
105
 
21
- $ gem install bianchi
106
+ - `render_and_end(string)`: Responds to the user and terminates the session.
22
107
 
23
- ## Usage
108
+ Here's how you can use these methods in Ruby:
109
+ ```ruby
110
+ render_and_await("Please select an option:")
111
+ ```
112
+ This method sends the message "Please select an option:" to the user and awaits their response.
113
+ ```ruby
114
+ render_and_end("Thank you for using our service. Goodbye!")
115
+ ```
116
+ This method sends the message "Thank you for using our service. Goodbye!" to the user and terminates the session.
117
+
118
+ ### Page Session
119
+
120
+ In Bianchi USSD applications, the `session` serves as an information tracker between all the pages and records all interactions between the user and the application. It contains valuable information necessary for the USSD application to function properly.
121
+
122
+ Here are the methods available within the page session:
123
+
124
+ - `session_params`: Returns a hash containing all the data the provider sent.
125
+ - `session_input_body`: Retrieves the user's current input.
126
+ - `session_mobile_number`: Returns the current user's phone number dialing the code.
127
+ - `session_session_id`: Provides the session's ID.
128
+ - `session.store`: Accesses the current session's cache. Further details about this will be discussed later.
129
+
130
+ ### Page Store
131
+
132
+ In Bianchi USSD applications, the `session.store` serves as the main application cache, where data can be stored and accessed across all pages.
133
+
134
+ Here are the methods available within the page store:
135
+
136
+ - `session.store.set(key, value)`: Stores a key-value pair in the cache.
137
+ - `session.store.get(key)`: Retrieves the value associated with a given key from the cache.
138
+ - `session.store.all`: Returns the entire cache as a hash.
24
139
 
25
- TODO: Write usage instructions here
140
+ Here's how you can use these methods to manage your application's cache:
26
141
 
27
- ## Development
142
+ ```ruby
143
+ # Storing data in the cache
144
+ session.store.set('user_id', 123)
145
+
146
+ # Retrieving data from the cache
147
+ user_id = session.store.get('user_id')
148
+
149
+ # Retrieving the entire cache
150
+ all_data = session.store.all
151
+ ```
152
+
153
+ ### Page Redirects
154
+
155
+ In Bianchi USSD applications, any method that starts with `redirect_to` initiates another page request to be sent to the user. An example use case is when you want to transition to another menu page after processing the user's response.
28
156
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
157
+ For instance, in the snippet of the main menu page provided earlier, we use `redirect_to_greetings_menu_page_1` when the user selects "1". This directs the application to the greetings menu page 1's request.
30
158
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
159
+ Here are the methods available within the page redirects:
32
160
 
33
- ## Contributing
161
+ - `redirect_to_menu_[menu_name]_page_[page_number]`: Redirects to a specified menu using the `menu_name` and loads the page specified by `page_number`.
162
+ - `redirect_to_[next|previous]_page`: Redirects to the next or previous page within the same menu.
34
163
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bianchi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/bianchi/blob/master/CODE_OF_CONDUCT.md).
164
+ Here's how you can use these methods within your Bianchi USSD application:
36
165
 
37
- ## License
166
+ ```ruby
167
+ # Redirecting to a specific menu page
168
+ redirect_to_menu_greetings_page_1
169
+
170
+ # Redirecting to the next or previous page in the same menu
171
+ redirect_to_next_page
172
+ redirect_to_previous_page
173
+ ```
38
174
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
175
+ ## Responding to the Provider
40
176
 
41
- ## Code of Conduct
177
+ Once all processes are completed, the next step is to prepare the response to send to the provider. The provider prompt data is accessible via the engine instance's `prompt_data` attribute.
42
178
 
43
- Everyone interacting in the bianchi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/bianchi/blob/master/CODE_OF_CONDUCT.md).
179
+ Here's an example of how you can retrieve the provider prompt data in your USSD application:
180
+
181
+ ```ruby
182
+ module USSD
183
+ class Engine
184
+ def self.start(params)
185
+ Bianchi::USSD::Engine.start(params, provider: :africastalking) do
186
+ menu :main, options
187
+ menu :greetings
188
+ menu :repeat
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ # Retrieving provider prompt data
195
+ provider_data = USSD::Engine.new(provider_params).prompt_data
196
+ ```
data/bianchi.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.name = "bianchi"
7
7
  spec.version = Bianchi::VERSION
8
8
  spec.authors = ["Dapilah Sydney"]
9
- spec.email = ['dapilah.sydney@gmail.com']
9
+ spec.email = ["dapilah.sydney@gmail.com"]
10
10
 
11
11
  spec.summary = "Write a short summary, because RubyGems requires one."
12
12
  spec.homepage = "https://github.com/SydDaps/Bianchi"
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "json"
35
35
  spec.add_dependency "redis"
36
36
  spec.add_dependency "thor"
37
+ spec.metadata["rubygems_mfa_required"] = "true"
37
38
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/ParameterLists
3
+ # rubocop:disable Metrics/ParameterLists
4
4
  module Bianchi
5
5
  module Cli
6
6
  class Main < Thor
@@ -20,15 +20,15 @@ module Bianchi
20
20
  desc "setup", "sets up a new ussd project"
21
21
  long_desc <<-LONG_DESC
22
22
  Usage: bianchi setup optional(-p|--provider)
23
- \x5 Providers: [:africa_is_talking]
23
+ \x5 Providers: [:africa_is_talking, :appsnmobile]
24
24
  \x5 Example: `bianchi setup`
25
25
  \x5 Example: `bianchi setup -p :africa_is_talking`
26
26
 
27
27
  LONG_DESC
28
- method_option :provider, :aliases => "-p", type: :string, :desc => "Set up ussd project for provider"
28
+ method_option :provider, aliases: "-p", type: :string, desc: "Set up ussd project for provider"
29
29
  def setup
30
30
  @provider = options[:provider]
31
- unless ["africa_is_talking", "none"].include? @provider
31
+ unless %w[africastalking none appsnmobile].include? @provider
32
32
  say("Error: provider #{@provider} is not yet configured.", :yellow)
33
33
  exit(1)
34
34
  end
@@ -63,4 +63,4 @@ module Bianchi
63
63
  end
64
64
  end
65
65
  end
66
- # rubocop:enable Metrics/ParameterLists
66
+ # rubocop:enable Metrics/ParameterLists
@@ -1,11 +1,9 @@
1
1
  module USSD
2
2
  module <%= @menu_name.camelize %>Menu
3
3
  class Page<%= @page_number %> < Bianchi::USSD::Page
4
- def request
5
- end
4
+ def request; end
6
5
 
7
- def response
8
- end
6
+ def response; end
9
7
  end
10
8
  end
11
9
  end
@@ -110,10 +110,7 @@ module Bianchi
110
110
  "#{constant_name} is supposed to be defined to process #{session.menu.name} menu #{page_number}"
111
111
  end
112
112
 
113
- page.new(session).tap do |p|
114
- p.ensure_methods_defined(%i[request response])
115
- p.send(action)
116
- end
113
+ page.call(session, action)
117
114
  end
118
115
  end
119
116
  end
@@ -6,5 +6,7 @@ module Bianchi
6
6
  class PageLoadError < StandardError; end
7
7
  class MethodNameError < StandardError; end
8
8
  class ProviderError < StandardError; end
9
+ class ProviderError < StandardError; end
10
+ class DispatchRenderException < StandardError; end
9
11
  end
10
12
  end
@@ -11,6 +11,15 @@ module Bianchi
11
11
  @session = session
12
12
  end
13
13
 
14
+ def self.call(session, action)
15
+ new(session).tap do |p|
16
+ p.ensure_methods_defined(%i[request response])
17
+ p.send(action)
18
+ rescue DispatchRenderException
19
+ p
20
+ end
21
+ end
22
+
14
23
  def render(body, options = {})
15
24
  raise ArgumentError, "render body expected to be a string" unless body.is_a? String
16
25
 
@@ -22,6 +31,8 @@ module Bianchi
22
31
  s.page_number = self.class.name.split("::").last
23
32
  s.store.track_session
24
33
  end
34
+
35
+ raise DispatchRenderException
25
36
  end
26
37
 
27
38
  def load_page(page_number, menu_name)
@@ -29,8 +40,11 @@ module Bianchi
29
40
  page = constant_name.safe_constantize
30
41
 
31
42
  unless page
32
- raise PageLoadError,
33
- "#{constant_name} is supposed to be defined to process #{menu_name} menu #{page_number}"
43
+ raise PageLoadError, <<~MSG
44
+ \n
45
+ #{constant_name} is supposed to be defined to process #{menu_name} menu #{page_number}.
46
+ generate menu page with `bianchi g menu #{menu_name} page #{page_number[4..]}`
47
+ MSG
34
48
  end
35
49
 
36
50
  session.menu = Menu.new(menu_name)
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Style/MissingRespondToMissing
3
4
  module Bianchi
4
5
  module USSD
5
6
  module PageDelegators
@@ -100,3 +101,4 @@ module Bianchi
100
101
  end
101
102
  end
102
103
  end
104
+ # rubocop:enable Style/MissingRespondToMissing
@@ -3,10 +3,14 @@
3
3
  module Bianchi
4
4
  module USSD
5
5
  module ProviderConfigurations
6
+ include ProviderParsers::Africastalking
7
+ include ProviderParsers::Appsnmobile
8
+
6
9
  def parse_params(params)
7
10
  provider_parsers = {
8
11
  none: proc { params },
9
- africa_is_talking: proc { africa_is_talking_params_parser(params) }
12
+ africastalking: proc { africastalking_params_parser(params) },
13
+ appsnmobile: proc { appsnmobile_params_parser(params) }
10
14
  }.with_indifferent_access
11
15
 
12
16
  parser = provider_parsers[@provider]
@@ -16,27 +20,11 @@ module Bianchi
16
20
  parser.call
17
21
  end
18
22
 
19
- def africa_is_talking_params_parser(params)
20
- required_params = %w[sessionId phoneNumber text serviceCode]
21
- left_required_params = required_params - params.keys.map(&:to_s)
22
-
23
- unless left_required_params.empty?
24
- raise ArgumentError, "#{left_required_params} required in params to start engine for provider #{@provider}"
25
- end
26
-
27
- {
28
- session_id: params["sessionId"],
29
- mobile_number: params["phoneNumber"],
30
- activity_state: params["text"] && params["text"].empty? ? "initial" : "subsequent",
31
- input_body: params["text"],
32
- service_code: params["serviceCode"]
33
- }
34
- end
35
-
36
23
  def parser_prompt_data(prompt_data)
37
24
  provider_parsers = {
38
25
  none: proc { prompt_data },
39
- africa_is_talking: proc { africa_is_talking_prompt_data_parser(prompt_data) }
26
+ africastalking: proc { africastalking_prompt_data_parser(prompt_data) },
27
+ appsnmobile: proc { appsnmobile_prompt_data_parser(prompt_data) }
40
28
  }.with_indifferent_access
41
29
 
42
30
  parser = provider_parsers[@provider]
@@ -45,10 +33,6 @@ module Bianchi
45
33
 
46
34
  parser.call
47
35
  end
48
-
49
- def africa_is_talking_prompt_data_parser(prompt_data)
50
- prompt_data["activity_state"] == :await ? "CON #{prompt_data['body']}" : "END #{prompt_data['body']}"
51
- end
52
36
  end
53
37
  end
54
38
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bianchi
4
+ module USSD
5
+ module ProviderParsers
6
+ module Africastalking
7
+ def africastalking_params_parser(params)
8
+ required_params = %w[sessionId phoneNumber text serviceCode]
9
+ left_required_params = required_params - params.keys.map(&:to_s)
10
+
11
+ unless left_required_params.empty?
12
+ raise ArgumentError, "#{left_required_params} required in params to start engine for provider #{@provider}"
13
+ end
14
+
15
+ {
16
+ session_id: params["sessionId"],
17
+ mobile_number: params["phoneNumber"],
18
+ activity_state: params["text"] && params["text"].empty? ? "initial" : "subsequent",
19
+ input_body: params["text"].split("*").last,
20
+ service_code: params["serviceCode"]
21
+ }
22
+ end
23
+
24
+ def africastalking_prompt_data_parser(prompt_data)
25
+ prompt_data["activity_state"] == :await ? "CON #{prompt_data['body']}" : "END #{prompt_data['body']}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bianchi
4
+ module USSD
5
+ module ProviderParsers
6
+ module Appsnmobile
7
+ def appsnmobile_params_parser(params)
8
+ required_params = %w[session_id msisdn msg_type ussd_body nw_code service_code]
9
+ left_required_params = required_params - params.keys.map(&:to_s)
10
+
11
+ unless left_required_params.empty?
12
+ raise ArgumentError, "#{left_required_params} required in params to start engine for provider #{@provider}"
13
+ end
14
+
15
+ {
16
+ session_id: params["session_id"],
17
+ mobile_number: params["msisdn"],
18
+ activity_state: return_activity_state(params["msg_type"]),
19
+ input_body: params["ussd_body"],
20
+ nw_code: params["nw_code"],
21
+ service_code: params["service_code"]
22
+ }
23
+ end
24
+
25
+ def appsnmobile_prompt_data_parser(prompt_data)
26
+ msg_type = prompt_data["activity_state"] == :await ? "1" : "2"
27
+
28
+ {
29
+ session_id: prompt_data["session_id"],
30
+ msisdn: prompt_data["mobile_number"],
31
+ msg_type: msg_type,
32
+ ussd_body: prompt_data["body"],
33
+ nw_code: prompt_data["nw_code"],
34
+ service_code: prompt_data["service_code"]
35
+ }.to_json
36
+ end
37
+
38
+ def return_activity_state(msg_type)
39
+ case msg_type
40
+ when "0"
41
+ "initial"
42
+ when "1"
43
+ "subsequent"
44
+ else
45
+ raise ArgumentError, "#{@provider} sent in an unknown message type or msg_type"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -6,7 +6,13 @@ module Bianchi
6
6
  attr_reader :session
7
7
 
8
8
  def initialize(session)
9
- @redis = Redis.new(url: ENV.fetch("REDIS_URL", nil))
9
+ redis_url = ENV.fetch("REDIS_URL", nil)
10
+ unless redis_url.present?
11
+ raise ArgumentError,
12
+ "environment variable REDIS_URL required to track engine session."
13
+ end
14
+
15
+ @redis = Redis.new(url: redis_url)
10
16
  @session = session
11
17
  end
12
18
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bianchi
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.2"
5
5
  end
data/lib/bianchi.rb CHANGED
@@ -6,6 +6,8 @@ require "json"
6
6
  require "thor"
7
7
 
8
8
  require_relative "bianchi/version"
9
+ require_relative "bianchi/ussd/provider_parsers/africastalking"
10
+ require_relative "bianchi/ussd/provider_parsers/appsnmobile"
9
11
  require_relative "bianchi/ussd/provider_configurations"
10
12
  require_relative "bianchi/ussd/engine"
11
13
  require_relative "bianchi/ussd/menu"
@@ -13,7 +15,7 @@ require_relative "bianchi/ussd/session"
13
15
  require_relative "bianchi/ussd/page_delegators"
14
16
  require_relative "bianchi/ussd/page"
15
17
  require_relative "bianchi/ussd/store"
16
- require_relative "bianchi/ussd/errors"
18
+ require_relative "bianchi/ussd/exceptions"
17
19
 
18
20
  # cli
19
21
  require "bianchi/cli/main"
data/ussd/engine.rb CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module USSD
2
4
  class Engine
3
5
  def self.start(params)
4
-
5
6
  Bianchi::USSD::Engine.start(params, provider: :africa_is_talking) do
6
7
  # e.g menu :main, options
7
8
  end
@@ -1,11 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module USSD
2
4
  module MainMenu
3
5
  class Page1 < Bianchi::USSD::Page
4
- def request
5
- end
6
+ def request; end
6
7
 
7
- def response
8
- end
8
+ def response; end
9
9
  end
10
10
  end
11
- end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bianchi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dapilah Sydney
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-25 00:00:00.000000000 Z
11
+ date: 2024-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -74,9 +74,11 @@ executables:
74
74
  extensions: []
75
75
  extra_rdoc_files: []
76
76
  files:
77
+ - ".github/workflows/ci.yml"
77
78
  - ".gitignore"
78
79
  - ".rspec"
79
80
  - ".rubocop.yml"
81
+ - ".vscode/settings.json"
80
82
  - CODE_OF_CONDUCT.md
81
83
  - Gemfile
82
84
  - Gemfile.lock
@@ -93,11 +95,13 @@ files:
93
95
  - lib/bianchi/cli/templates/engine.erb
94
96
  - lib/bianchi/cli/templates/page.erb
95
97
  - lib/bianchi/ussd/engine.rb
96
- - lib/bianchi/ussd/errors.rb
98
+ - lib/bianchi/ussd/exceptions.rb
97
99
  - lib/bianchi/ussd/menu.rb
98
100
  - lib/bianchi/ussd/page.rb
99
101
  - lib/bianchi/ussd/page_delegators.rb
100
102
  - lib/bianchi/ussd/provider_configurations.rb
103
+ - lib/bianchi/ussd/provider_parsers/africastalking.rb
104
+ - lib/bianchi/ussd/provider_parsers/appsnmobile.rb
101
105
  - lib/bianchi/ussd/session.rb
102
106
  - lib/bianchi/ussd/store.rb
103
107
  - lib/bianchi/version.rb
@@ -111,6 +115,7 @@ metadata:
111
115
  homepage_uri: https://github.com/SydDaps/Bianchi
112
116
  source_code_uri: https://github.com/SydDaps/Bianchi
113
117
  changelog_uri: https://rubygems.org/
118
+ rubygems_mfa_required: 'true'
114
119
  post_install_message:
115
120
  rdoc_options: []
116
121
  require_paths: