├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── examples ├── basic.rb ├── celebrities.rb ├── joke.rb ├── wit-example-celebrities.zip └── wit-example-joke-bot.zip ├── lib └── wit.rb └── wit.gemspec /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Do you want to request a *feature*, report a *bug*, or ask a *question* about wit-ruby?** 2 | 3 | **What is the current behavior?** 4 | 5 | **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.** 6 | 7 | **What is the expected behavior?** 8 | 9 | **If applicable, what is the App ID where you are experiencing this issue? If you do not provide this, we cannot help.** 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.swo 3 | *.swp 4 | .idea/ 5 | Gemfile.lock 6 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## v7.0.1 2 | - Updating encode function to encode more special characters that could be passed in when constructing a request URL. 3 | 4 | ## v7.0.0 5 | - Updated API version to latest: `20200513`. Browse the latest HTTP API documentation [here](https://wit.ai/docs/http/20200513#get__message_link). 6 | - Added intents and traits CRUD methods. 7 | - More consistent, transparent naming of entities methods. 8 | 9 | ## v6.0.0 10 | The most important change is the removal of `.converse()` and `.run_actions()`. Follow the migration tutorial [here](https://github.com/wit-ai/wit-stories-migration-tutorial), or [read more here](https://wit.ai/blog/2017/07/27/sunsetting-stories). 11 | 12 | ### Breaking changes 13 | 14 | - `converse` and `run_actions` are removed 15 | - updated and added new examples that leverage the /message API 16 | 17 | ## v5.0.0 18 | 19 | - `converse` and `run_actions` are deprecated 20 | - `interactive` now calls `message` 21 | 22 | ### Breaking changes 23 | - Renamed WitException to Wit::Error 24 | - Changed Wit::Error to inherit from StandardError instead of Exception 25 | - Moved constants inside Wit namespace 26 | - Moved #req and #validate_actions to private methods within Wit namespace 27 | 28 | ## v4.1.0 29 | 30 | - `converse` now takes `reset` as optional parameter. 31 | 32 | ### Breaking changes 33 | 34 | - `run_actions` now resets the last turn on new messages and errors. 35 | 36 | ## v4.0.0 37 | 38 | After a lot of internal dogfooding and bot building, we decided to change the API in a backwards-incompatible way. The changes are described below and aim to simplify user code and accommodate upcoming features. 39 | 40 | See `./examples` to see how to use the new API. 41 | 42 | ### Breaking changes 43 | 44 | - `say` renamed to `send` to reflect that it deals with more than just text 45 | - Removed built-in actions `merge` and `error` 46 | - Actions signature simplified with `request` and `response` arguments 47 | - INFO level replaces LOG level 48 | 49 | ## v3.4 50 | 51 | - allows for overriding API version, by setting `WIT_API_VERSION` 52 | - `interactive()` mode 53 | - warns instead of throwing when validating actions 54 | 55 | ### breaking 56 | 57 | - bumped default API version from `20160330` to `20160516` 58 | 59 | ## v3.3.1 60 | 61 | - fixed recursive call when running actions 62 | - throws an exception if context passed is not a Hash 63 | 64 | ## v3.3 65 | 66 | Unifying action parameters 67 | 68 | ### breaking 69 | 70 | - the `say` action now takes 3 parameters: `session_id`, `context`, `msg` 71 | - the `error` action now takes 3 parameters: `session_id`, `context`, `error` 72 | 73 | ## v3.2 74 | 75 | Updating action parameters 76 | 77 | ### breaking 78 | 79 | - the `merge` action now takes 4 parameters: `session_id`, `context`, `entities`, `msg` 80 | - the `error` action now takes `context` as second parameter 81 | - custom actions now take 2 parameters: `session_id`, `context` 82 | 83 | ## v3.1 84 | 85 | - allows for custom logging 86 | - better error message 87 | 88 | ## v3.0 89 | 90 | Bot Engine integration 91 | 92 | ### breaking 93 | 94 | - the `message` API is wrapped around a `Wit` class, and doesn't take the token as first parameter 95 | 96 | ## v2.0 97 | 98 | Rewrite in pure Ruby 99 | 100 | ### breaking 101 | 102 | - audio recording and streaming have been removed because: 103 | - many people only needed access to the HTTP API, and audio recording did not make sense for server-side use cases 104 | - dependent on platform, choice best left to developers 105 | - forced us to maintain native bindings as opposed to a pure Pythonic library 106 | - we renamed the functions to match the HTTP API more closely 107 | - `.text_query(string, access_token)` becomes `.message(access_token, string)` 108 | - all functions now return a Ruby dict instead of a JSON string 109 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to wit-ruby 2 | 3 | We want to make contributing to this project as easy and transparent as 4 | possible. 5 | 6 | ## Pull Requests 7 | We actively welcome your pull requests. 8 | 9 | 1. Fork the repo and create your branch from `master`. 10 | 2. If you've added code that should be tested, add tests. 11 | 3. If you've changed APIs, update the documentation. 12 | 4. Ensure the test suite passes. 13 | 5. If you haven't already, complete the Contributor License Agreement ("CLA"). 14 | 15 | ## Contributor License Agreement ("CLA") 16 | 17 | In order to accept your pull request, we need you to submit a CLA. You only need 18 | to do this once to work on any of Facebook's open source projects. 19 | 20 | Complete your CLA here: 21 | 22 | ## Issues 23 | 24 | We use GitHub issues to track public bugs. Please ensure your description is 25 | clear and has sufficient instructions to be able to reproduce the issue. 26 | 27 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 28 | disclosure of security bugs. In those cases, please go through the process 29 | outlined on that page and do not file a public issue. 30 | 31 | ## License 32 | 33 | By contributing to wit-ruby, you agree that your contributions will be licensed 34 | under the LICENSE file in the root directory of this source tree. 35 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | gemspec 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014, Wit.ai, Inc. All rights reserved. 3 | * 4 | * You are hereby granted a non-exclusive, worldwide, royalty-free license to 5 | * use, copy, modify, and distribute this software in source code or binary 6 | * form for use in connection with the web services and APIs provided by 7 | * Wit.ai. 8 | * 9 | * As with any software that integrates with the Wit.ai platform, your use 10 | * of this software is subject to the Wit.ai Terms of Service 11 | * [https://wit.ai/terms]. This copyright notice shall be included in all 12 | * copies or substantial portions of the software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wit-ruby 2 | 3 | `wit-ruby` is the Ruby SDK for [Wit.ai](http://wit.ai). 4 | 5 | ## Install 6 | 7 | From RubyGems: 8 | ```bash 9 | gem install wit 10 | ``` 11 | 12 | From source: 13 | ```bash 14 | git clone https://github.com/wit-ai/wit-ruby 15 | gem build wit.gemspec 16 | gem install wit-*.gem 17 | ``` 18 | 19 | ## Quickstart 20 | 21 | Run in your terminal: 22 | 23 | ```bash 24 | ruby examples/basic.rb 25 | ``` 26 | 27 | See the `examples` folder for more examples. 28 | 29 | ## API 30 | 31 | ### Overview 32 | 33 | `wit-ruby` provides a Wit class with the following methods: 34 | * `message` - the Wit [message API](https://wit.ai/docs/http/20200513#get-intent-via-text-link) 35 | * `interactive` - starts an interactive conversation with your bot 36 | 37 | ### Wit class 38 | 39 | The Wit constructor takes a `Hash` with the following symbol keys: 40 | * `:access_token` - the access token of your Wit instance 41 | 42 | A minimal example looks like this: 43 | ```ruby 44 | require 'wit' 45 | 46 | client = Wit.new(access_token: access_token) 47 | client.message('set an alarm tomorrow at 7am') 48 | ``` 49 | 50 | ### Create new App Using the Access Token 51 | 52 | Creates new app using the server token. 53 | See [POST /apps](https://wit.ai/docs/http/20200513#post__apps_link). 54 | 55 | ```ruby 56 | new_app_payload = {name: "new-app-1", lang: "en", private: true} 57 | # set_new_app_token will make the client use the new app's token. 58 | # that flag is set to false by default. 59 | client.create_new_app(new_app_payload, set_new_app_token = true) 60 | ``` 61 | 62 | ### Train the app programatically using '/utterances' 63 | 64 | Trains and annotates an utterance or more. 65 | See [POST /utterances](https://wit.ai/docs/http/20200513#post__utterances_link). 66 | 67 | ```ruby 68 | # you have to create the intent and entity before using any of them. 69 | utterance_payload = { 70 | text: "I want to fly to china", 71 | intent: "flight_request", 72 | entities: [ 73 | { 74 | "entity": "wit$location:to", 75 | "start": 17, 76 | "end": 22, 77 | "body": "china", 78 | "entities": [] 79 | } 80 | ], 81 | traits: [] 82 | } 83 | 84 | # utterance payload can be a list of utterances or a single one 85 | client.post_utterances(utterance_payload) 86 | ``` 87 | 88 | ### .message() 89 | 90 | The Wit [message API](https://wit.ai/docs/http/20200513#get-intent-via-text-link). 91 | 92 | Takes the following parameters: 93 | * `msg` - the text you want Wit.ai to extract the information from 94 | 95 | Example: 96 | ```ruby 97 | rsp = client.message('what is the weather in London?') 98 | puts("Yay, got Wit.ai response: #{rsp}") 99 | ``` 100 | 101 | ### .interactive() 102 | 103 | Starts an interactive conversation with your bot. 104 | 105 | Example: 106 | ```ruby 107 | client.interactive 108 | ``` 109 | 110 | ### CRUD operations for intents 111 | `payload` in the parameters is a hash containing API arguments. 112 | 113 | #### .get_intents() 114 | Returns a list of available intents for the app. 115 | See [GET /intents](https://wit.ai/docs/http/20200513#get__intents_link). 116 | 117 | #### .get_intent(intent) 118 | Returns all available information about an intent. 119 | See [GET /intents/:intent](https://wit.ai/docs/http/20200513#get__intents__intent_link). 120 | 121 | #### .post_intents(payload) 122 | Creates a new intent. 123 | See [POST /intents](https://wit.ai/docs/http/20200513#post__intents_link). 124 | 125 | #### .delete_intents(intent) 126 | Permanently deletes the intent. 127 | See [DELETE /intents/:intent](https://wit.ai/docs/http/20200513#delete__intents__intent_link). 128 | 129 | ### CRUD operations for entities 130 | `payload` in the parameters is a hash containing API arguments. 131 | 132 | #### .get_entities() 133 | Returns a list of available entities for the app. 134 | See [GET /entities](https://wit.ai/docs/http/20200513#get--entities-link) 135 | 136 | #### .post_entities(payload) 137 | Creates a new entity with the given attributes. 138 | See [POST /entities](https://wit.ai/docs/http/20200513#post--entities-link) 139 | 140 | #### .get_entity(entity) 141 | Returns all the information available for an entity. 142 | See [GET /entities/:entity](https://wit.ai/docs/http/20200513#get--entities-:entity-link) 143 | 144 | #### .put_entities(entity, payload) 145 | Updates an entity with the given attributes. 146 | See [PUT /entities/:entity](https://wit.ai/docs/http/20200513#put--entities-:entity-link) 147 | 148 | #### .delete_entities(entity) 149 | Permanently removes the entity. 150 | See [DELETE /entities/:entity](https://wit.ai/docs/http/20200513#delete--entities-:entity-link) 151 | 152 | #### .post_entities_keywords(entity, payload) 153 | Adds a possible value into the list of keywords for the keywords entity. 154 | See [POST /entities/:entity/keywords](https://wit.ai/docs/http/20160526#post--entities-:entity-id-values-link) 155 | 156 | #### .delete_entities_keywords(entity, keyword) 157 | Deletes a keyword from the entity. 158 | See [DELETE /entities/:entity/keywords/:keyword](https://wit.ai/docs/http/20200513#delete--entities-:entity-keywords-link) 159 | 160 | #### .post_entities_keywords_synonyms(entity, keyword, payload) 161 | Creates a new synonym for the keyword of the entity. 162 | See [POST /entities/:entity/keywords/:keyword/synonyms](https://wit.ai/docs/http/20200513#post--entities-:entity-keywords-:keyword-synonyms-link) 163 | 164 | #### delete_entities_keywords_synonyms(entity, keyword, synonym) 165 | Deletes a synonym of the keyword of the entity. 166 | See [DELETE /entities/:entity/keywords/:keyword/synonyms/:synonym](https://wit.ai/docs/http/20200513#delete--entities-:entity-keywords-:keyword-synonyms-link) 167 | 168 | ### CRUD operations for traits 169 | `payload` in the parameters is a hash containing API arguments. 170 | 171 | #### .get_traits() 172 | Returns a list of available traits for the app. 173 | See [GET /traits](https://wit.ai/docs/http/20200513#get__traits_link). 174 | 175 | #### .get_trait(trait) 176 | Returns all available information about a trait. 177 | See [GET /traits/:trait](https://wit.ai/docs/http/20200513#get__traits__trait_link). 178 | 179 | #### .post_traits(payload) 180 | Creates a new trait. 181 | See [POST /traits](https://wit.ai/docs/http/20200513#post__traits_link). 182 | 183 | #### .post_traits_values(trait, payload) 184 | Adds a new value to an existing trait. 185 | See [POST /traits/:trait/values](https://wit.ai/docs/http/20200513#post__traits__trait_values_link). 186 | 187 | #### .delete_traits_values(trait, value) 188 | Permanently deletes a value of an existing trait. 189 | See [POST /traits/:trait/values](https://wit.ai/docs/http/20200513#delete__traits__trait_values_link). 190 | 191 | #### .delete_traits(trait) 192 | Permanently deletes the trait. 193 | See [DELETE /traits/:trait](https://wit.ai/docs/http/20200513#delete__traits__trait_link). 194 | 195 | 196 | See the [docs](https://wit.ai/docs) for more information. 197 | 198 | ### Logging 199 | 200 | Default logging is to `STDOUT` with `INFO` level. 201 | 202 | You can setup your logging level as follows: 203 | ```ruby 204 | Wit.logger.level = Logger::WARN 205 | ``` 206 | See the [Logger class](http://ruby-doc.org/stdlib-2.1.0/libdoc/logger/rdoc/Logger.html) docs for more information. 207 | 208 | ## Thanks 209 | 210 | Thanks to [Justin Workman](http://github.com/xtagon) for releasing a first version in October 2013. We really appreciate! 211 | 212 | 213 | ## License 214 | 215 | The license for wit-ruby can be found in LICENSE file in the root directory of this source tree. 216 | 217 | 218 | ## Terms of Use 219 | 220 | Our terms of use can be found at https://opensource.facebook.com/legal/terms. 221 | 222 | 223 | ## Privacy Policy 224 | 225 | Our privacy policy can be found at https://opensource.facebook.com/legal/privacy. 226 | -------------------------------------------------------------------------------- /examples/basic.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | 3 | require 'wit' 4 | 5 | if ARGV.length == 0 6 | puts("usage: #{$0} ") 7 | exit 1 8 | end 9 | 10 | access_token = ARGV[0] 11 | ARGV.shift 12 | 13 | client = Wit.new(access_token: access_token) 14 | client.interactive 15 | -------------------------------------------------------------------------------- /examples/celebrities.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | 3 | require 'wit' 4 | 5 | if ARGV.length == 0 6 | puts("usage: #{$0} ") 7 | exit 1 8 | end 9 | 10 | access_token = ARGV[0] 11 | ARGV.shift 12 | 13 | # Celebrities example 14 | # See https://wit.ai/aleka/wit-example-celebrities/ 15 | 16 | def first_entity_resolved_value(entities, entity) 17 | return nil unless entities.has_key? entity 18 | val = entities[entity][0]['resolved']['values'][0] 19 | return nil if val.nil? 20 | return val 21 | end 22 | 23 | def first_trait_value(traits, trait) 24 | return nil unless traits.has_key? trait 25 | val = traits[trait][0]['value'] 26 | return nil if val.nil? 27 | return val 28 | end 29 | 30 | def handle_message(response) 31 | greetings = first_trait_value(response['traits'], 'wit$greetings') 32 | celebrity = first_entity_resolved_value(response['entities'], 'wit$notable_person:notable_person') 33 | 34 | case 35 | when celebrity 36 | return wikidata_description(celebrity) 37 | when greetings 38 | return "Hi! You can say something like 'Tell me about Beyonce'" 39 | else 40 | return "Um. I don't recognize that name. " \ 41 | "Which celebrity do you want to learn about?" 42 | end 43 | end 44 | 45 | def wikidata_description(celebrity) 46 | return "I recognize #{celebrity['name']}" unless celebrity.dig('external', 'wikidata') 47 | wikidata_id = celebrity.fetch('external').fetch('wikidata') 48 | api = URI("https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&ids=#{wikidata_id}&props=descriptions&languages=en") 49 | rsp = Net::HTTP.get_response(api) 50 | wikidata = JSON.parse(rsp.body) 51 | description = wikidata['entities'][wikidata_id]['descriptions']['en']['value'] 52 | return "ooo yes I know #{celebrity['name']} -- #{description}" 53 | end 54 | 55 | client = Wit.new(access_token: access_token) 56 | client.interactive(method(:handle_message)) 57 | -------------------------------------------------------------------------------- /examples/joke.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | 3 | require 'wit' 4 | 5 | if ARGV.length == 0 6 | puts("usage: #{$0} ") 7 | exit 1 8 | end 9 | 10 | access_token = ARGV[0] 11 | ARGV.shift 12 | 13 | # Joke example 14 | # See https://wit.ai/aleka/wit-example-joke-bot/ 15 | 16 | def first_value(obj, key) 17 | return nil unless obj.has_key? key 18 | val = obj[key][0]['value'] 19 | return nil if val.nil? 20 | return val 21 | end 22 | 23 | $all_jokes = { 24 | 'chuck' => [ 25 | 'Chuck Norris counted to infinity - twice.', 26 | 'Death once had a near-Chuck Norris experience.', 27 | ], 28 | 'tech' => [ 29 | 'Did you hear about the two antennas that got married? The ceremony was long and boring, but the reception was great!', 30 | 'Why do geeks mistake Halloween and Christmas? Because Oct 31 === Dec 25.', 31 | ], 32 | 'default' => [ 33 | 'Why was the Math book sad? Because it had so many problems.', 34 | ], 35 | } 36 | 37 | def handle_message(response) 38 | entities = response['entities'] 39 | traits = response['traits'] 40 | get_joke = first_value(traits, 'getJoke') 41 | greetings = first_value(traits, 'wit$greetings') 42 | category = first_value(entities, 'category:category') 43 | sentiment = first_value(traits, 'wit$sentiment') 44 | 45 | case 46 | when get_joke 47 | return $all_jokes[category || 'default'].sample 48 | when sentiment 49 | return sentiment == 'positive' ? 'Glad you liked it.' : 'Hmm.' 50 | when greetings 51 | return 'Hey this is joke bot :)' 52 | else 53 | return 'I can tell jokes! Say "tell me a joke about tech"!' 54 | end 55 | end 56 | 57 | client = Wit.new(access_token: access_token) 58 | client.interactive(method(:handle_message)) 59 | -------------------------------------------------------------------------------- /examples/wit-example-celebrities.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wit-ai/wit-ruby/194f39940eadb606acecae09299ee29305884639/examples/wit-example-celebrities.zip -------------------------------------------------------------------------------- /examples/wit-example-joke-bot.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wit-ai/wit-ruby/194f39940eadb606acecae09299ee29305884639/examples/wit-example-joke-bot.zip -------------------------------------------------------------------------------- /lib/wit.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | 3 | require 'cgi' 4 | require 'json' 5 | require 'logger' 6 | require 'net/http' 7 | require 'securerandom' 8 | 9 | class Wit 10 | class Error < StandardError; end 11 | 12 | WIT_API_HOST = ENV['WIT_URL'] || 'https://api.wit.ai' 13 | WIT_API_VERSION = ENV['WIT_API_VERSION'] || '20200513' 14 | LEARN_MORE = 'Learn more at https://wit.ai/docs/quickstart' 15 | 16 | def initialize(opts = {}) 17 | @access_token = opts[:access_token] 18 | 19 | if opts[:logger] 20 | @logger = opts[:logger] 21 | end 22 | end 23 | 24 | def logger 25 | @logger ||= begin 26 | x = Logger.new(STDOUT) 27 | x.level = Logger::INFO 28 | x 29 | end 30 | end 31 | 32 | def message(msg, n=nil, verbose=nil) 33 | params = {} 34 | params[:q] = msg unless msg.nil? 35 | params[:n] = n unless n.nil? 36 | params[:verbose] = verbose unless verbose.nil? 37 | res = req(logger, @access_token, Net::HTTP::Get, '/message', params) 38 | return res 39 | end 40 | 41 | def interactive(handle_message=nil, context={}) 42 | while true 43 | print '> ' 44 | msg = STDIN.gets.strip 45 | if msg == '' 46 | next 47 | end 48 | 49 | begin 50 | if handle_message.nil? 51 | puts(message(msg)) 52 | else 53 | puts(handle_message.call(message(msg))) 54 | end 55 | rescue Error => exp 56 | logger.error("error: #{exp.message}") 57 | end 58 | end 59 | rescue Interrupt => _exp 60 | puts 61 | end 62 | 63 | def create_new_app(payload, set_new_app_token = false) 64 | response = req(logger, @access_token, Net::HTTP::Post, "/apps", {}, payload) 65 | @access_token = response['access_token'] if set_new_app_token 66 | return response 67 | end 68 | 69 | def post_utterances(payload) 70 | payload = [payload] if payload.is_a? Hash 71 | 72 | payload.each do |utterance| 73 | unless utterance[:entities].empty? 74 | utterance[:entities] = validate_entities utterance[:entities] 75 | end 76 | validate_payload utterance 77 | end 78 | req(logger, @access_token, Net::HTTP::Post, "/utterances", {}, payload) 79 | end 80 | 81 | def get_intents 82 | req(logger, @access_token, Net::HTTP::Get, "/intents") 83 | end 84 | 85 | def get_intent(intent) 86 | req(logger, @access_token, Net::HTTP::Get, "/intents/#{CGI::escape(intent)}") 87 | end 88 | 89 | def post_intents(payload) 90 | req(logger, @access_token, Net::HTTP::Post, "/intents", {}, payload) 91 | end 92 | 93 | def delete_intents(intent) 94 | req(logger, @access_token, Net::HTTP::Delete, "/intents/#{CGI::escape(intent)}") 95 | end 96 | 97 | def get_entities 98 | req(logger, @access_token, Net::HTTP::Get, "/entities") 99 | end 100 | 101 | def post_entities(payload) 102 | payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:name, :roles, :lookups, :keywords].include?(k) } 103 | validate_payload payload 104 | req(logger, @access_token, Net::HTTP::Post, "/entities", {}, payload) 105 | end 106 | 107 | def get_entity(entity) 108 | req(logger, @access_token, Net::HTTP::Get, "/entities/#{CGI::escape(entity)}") 109 | end 110 | 111 | def put_entities(entity, payload) 112 | payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:name, :roles, :lookups, :keywords].include?(k) } 113 | validate_payload payload 114 | req(logger, @access_token, Net::HTTP::Put, "/entities/#{CGI::escape(entity)}", {}, payload) 115 | end 116 | 117 | def delete_entities(entity) 118 | req(logger, @access_token, Net::HTTP::Delete, "/entities/#{CGI::escape(entity)}") 119 | end 120 | 121 | def post_entities_keywords(entity, payload) 122 | payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:keyword, :synonyms].include?(k) } 123 | validate_payload payload 124 | req(logger, @access_token, Net::HTTP::Post, "/entities/#{CGI::escape(entity)}/keywords", {}, payload) 125 | end 126 | 127 | def delete_entities_keywords(entity, keyword) 128 | req(logger, @access_token, Net::HTTP::Delete, "/entities/#{CGI::escape(entity)}/keywords/#{CGI::escape(keyword)}") 129 | end 130 | 131 | def post_entities_keywords_synonyms(entity, keyword, payload) 132 | payload = payload.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| ![:synonym].include?(k) } 133 | validate_payload payload 134 | req(logger,@access_token, Net::HTTP::Post, "/entities/#{CGI::escape(entity)}/keywords/#{CGI::escape(keyword)}/synonyms", {}, payload) 135 | end 136 | 137 | def delete_entities_keywords_synonyms(entity, keyword, synonym) 138 | req(logger,@access_token, Net::HTTP::Delete, "/entities/#{CGI::escape(entity)}/keywords/#{CGI::escape(keyword)}/synonyms/#{CGI::escape(synonym)}") 139 | end 140 | 141 | def get_traits 142 | req(logger, @access_token, Net::HTTP::Get, "/traits") 143 | end 144 | 145 | def get_trait(trait) 146 | req(logger, @access_token, Net::HTTP::Get, "/traits/#{CGI::escape(trait)}") 147 | end 148 | 149 | def post_traits(payload) 150 | req(logger, @access_token, Net::HTTP::Post, "/traits", {}, payload) 151 | end 152 | 153 | def post_traits_values(trait, payload) 154 | req(logger, @access_token, Net::HTTP::Post, "/traits/#{CGI::escape(trait)}/values", {}, payload) 155 | end 156 | 157 | def delete_traits_values(trait, value) 158 | req(logger, @access_token, Net::HTTP::Delete, "/traits/#{CGI::escape(trait)}/values/#{CGI::escape(value)}") 159 | end 160 | 161 | def delete_traits(trait) 162 | req(logger, @access_token, Net::HTTP::Delete, "/traits/#{CGI::escape(trait)}") 163 | end 164 | 165 | private 166 | 167 | def validate_payload(payload) 168 | key_types = { 169 | id: String, 170 | name: String, 171 | roles: Array, 172 | lookups: Array, 173 | keywords: Array, 174 | text: String, 175 | intent: String, 176 | entities: Array, 177 | traits: Array 178 | } 179 | payload.each do |k, v| 180 | raise Error.new("#{k.to_s} in request body must be #{key_types[k].to_s} type") unless key_types[k] == v.class 181 | end 182 | end 183 | 184 | def validate_entities(entities) 185 | entity_keys = { 186 | entity: String, 187 | start: Integer, 188 | end: Integer, 189 | body: String, 190 | entities: Array 191 | } 192 | entities.each do |entity| 193 | entity = entity.map {|k, v| [(k.to_sym rescue k), v]}.to_h.reject{ |k| !entity_keys.keys.include?(k) } 194 | entity.each do |k, v| 195 | if k == :entities && !v.empty? 196 | validate_entities(v) 197 | end 198 | raise Error.new("#{k.to_s} in entities body must be #{entity_keys[k].to_s} type") unless entity_keys[k] == v.class 199 | end 200 | end 201 | 202 | return entities 203 | end 204 | 205 | def req(logger, access_token, meth_class, path, params={}, payload={}) 206 | uri = URI(WIT_API_HOST + path) 207 | uri.query = URI.encode_www_form(params) 208 | 209 | logger.debug("#{meth_class} #{uri}") 210 | 211 | request = meth_class.new(uri) 212 | request['authorization'] = 'Bearer ' + access_token 213 | request['accept'] = 'application/vnd.wit.' + WIT_API_VERSION + '+json' 214 | request.add_field 'Content-Type', 'application/json' 215 | request.body = payload.to_json 216 | 217 | Net::HTTP.start(uri.host, uri.port, {:use_ssl => uri.scheme == 'https'}) do |http| 218 | rsp = http.request(request) 219 | json = JSON.parse(rsp.body) 220 | if rsp.code.to_i != 200 221 | error_msg = (json.is_a?(Hash) and json.has_key?('error')) ? json['error'] : json 222 | raise Error.new("Wit responded with an error: #{error_msg}") 223 | end 224 | logger.debug("#{meth_class} #{uri} #{json}") 225 | json 226 | end 227 | end 228 | end 229 | -------------------------------------------------------------------------------- /wit.gemspec: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 2 | 3 | Gem::Specification.new do |s| 4 | s.name = 'wit' 5 | s.version = '7.0.1' 6 | s.summary = 'Ruby SDK for Wit.ai' 7 | s.description = 'Ruby SDK for Wit.ai' 8 | s.authors = ['The Wit Team'] 9 | s.email = 'help@wit.ai' 10 | s.homepage = 'https://wit.ai' 11 | s.license = 'GPL-2.0' 12 | s.platform = Gem::Platform::RUBY 13 | s.required_ruby_version = '>= 1.9.3' 14 | s.require_paths = ['lib'] 15 | s.files = `git ls-files`.split("\n") 16 | end 17 | --------------------------------------------------------------------------------