Giter VIP home page Giter VIP logo

stealth's Introduction

Stealth Logo

Stealth is a Ruby framework for creating text and voice chatbots. It's design is inspired by Ruby on Rails's philosophy of convention over configuration. It has an MVC architecture with the slight caveat that views are aptly named replies.

CircleCI Gem (including prereleases)

Features

  • Deploy anywhere, it's just a Rack app
  • Variants allow you to use a single codebase on multiple messaging platforms
  • Structured, universal reply format
  • Sessions utilize a state-machine concept and are Redis backed
  • Highly scalable. Incoming webhooks are processed via a Sidekiq queue
  • Built-in best practices: catch-alls (error handling), hello flows, goodbye flows

Getting Started

Getting started with Stealth is simple:

> gem install stealth
> stealth new <bot>

Service Integrations

Stealth is extensible. All service integrations are split out into separate Ruby Gems. Things like analytics and natural language processing (NLP) can be added in as gems as well.

Currently, there are gems for:

Messaging

Voice

Natural Language Processing

Analytics

Docs

You can find our full docs here. If something is not clear in the docs, please file an issue! We consider all shortcomings in the docs as bugs.

Versioning

Stealth is versioned using Semantic Versioning, but it's more like the Linux Kernel. Major version releases are just as arbitrary as minor version releases. We strive to never break anything with any version change. Patches are still issues as the "third dot" in the version string.

License

"Stealth" and the Stealth logo are Copyright (c) 2017-2023 MAV Automated Ventures Inc.

stealth's People

Contributors

debasish117 avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar emorissettegregoire avatar ignaciosiri avatar luizcarvalho avatar malkovro avatar matthewblack avatar mgomes avatar skateman avatar sunny avatar tersor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stealth's Issues

Non-ERB variants fail to load

When a variant is named say_hello.yml+facebook.erb it loads just fine. But say_hello.yml+facebook is getting skipped.

Donation button

Please, put a donation button on README. This project deserves donations!

Rename current_user_id to current_session_id

Is your feature request related to a problem? Please describe.
N/A

Describe the solution you'd like
I'm currently using stealth as a micro service. In my main app, a User has multiple Encounters with the bot I'm building. I'm currently using the Encounter's ID as the session id within Stealth. This was done for quite a few reasons.

I feel that 'current_session_id' would provide more flexibility over 'current_user_id' to refer to the id of the current session. Curious about your guys' thoughts on this.

Describe alternatives you've considered
Additional context

When exceptions happens, CatchAll->level1 is not called.

Describe the bug
If an exception occurs in my hellosController, for instance, the message CatchAll level1 triggered for error is showed, but leve1 action is never called. I investigate more deep and, for me, in lib/stealth/controller/catch_all.rb:16 CatchAllFlow is not defined, and neve enter in if condition.

To Reproduce
Steps to reproduce the behavior:

  1. Create a new project stealth new teste
  2. Add gem 'stealth-facebook' and configure it.
  3. Add a exception exemple in HellosController->say_hello, like a 1/0
class HellosController < BotController
  def say_hello
    1/0
    send_replies
  end
end
  1. Send a simple message on Messenger
  2. Is showed
    [catch_all] CatchAll level1 triggered for error-1540684789327149-hello-say_hello: divided by 0
    But level1 action is never called.

Expected behavior
CatchAll->level1 action should be called?

Desktop (please complete the following information):

  • OS: Ubuntu
  • Browser Chrome

Additional context

The FlowMap generated

class FlowMap

  include Stealth::Flow

  flow :hello do
    state :say_hello
  end

  flow :goodbye do
    state :say_goodbye
  end

  flow :catch_all do
    state :level1
  end

end

Support other Ruby versions than 2.5.1

The Gemfile generated by stealth new <app> hardcodes ruby version to be 2.5.1, however, the gem itself doesn't specify 2.5.1 as the required_ruby_version in the gemspec.

I was able to install the gem on 2.4.4p296 and then got the following error when running bundle install:

Your Ruby version is 2.4.4, but your Gemfile specified 2.5.1

I have three suggestions to fix this:

  • Drop the ruby version from the Gemfile
  • Set the required_ruby_version in the gemspec
  • Use a template Gemfile and set the ruby version dynamically when generating a new app

What do you think?

Catch all error backtrace more readable

Is your feature request related to a problem? Please describe.
The backgrace showed when happens a error, are formatted like a array

Describe the solution you'd like
Can we join each line with a line break? is more readable.
https://github.com/hellostealth/stealth/blob/master/lib/stealth/controller/controller.rb#L66

How is it

[catch_all] ["/home/desenvolvimento/dev/riki/bot/controllers/hellos_controller.rb:5:in `say_hello'", "/home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:63:in `block in action'", "/home/desenvolvimento/.rvm/gems/ruby-2.5.1/gems/activesupport-5.2.0/lib/active_support/callbacks.rb:98:in `run_callbacks'", "/home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:61:in `action'", "/home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:118:in `step'", (...) ,"/home/desenvolvimento/.rvm/gems/ruby-2.5.1/gems/sidekiq-5.1.3/lib/sidekiq/processor.rb:73:in `run'", "/home/desenvolvimento/.rvm/gems/ruby-2.5.1/gems/sidekiq-5.1.3/lib/sidekiq/util.rb:16:in `watchdog'", "/home/desenvolvimento/.rvm/gems/ruby-2.5.1/gems/sidekiq-5.1.3/lib/sidekiq/util.rb:25:in `block in safe_thread'"]
14:40:05 sidekiq.1 | [catch_all] CatchAll level2 triggered for error-1540684789327149-hello-say_hello: undefined local variable or method `b' for #<HellosController:0x00007f6fb40086d0>

As for me, it would be more readable

[catch_all] /home/desenvolvimento/dev/riki/bot/controllers/hellos_controller.rb:5:in `say_hello'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:63:in `block in action'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:61:in `action'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:118:in `step'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/controller/controller.rb:96:in `step_to'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/bot/controllers/bot_controller.rb:12:in `route'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/dispatcher.rb:34:in `process'
14:42:19 sidekiq.1 | /home/desenvolvimento/dev/riki/vendor/gems/stealth/lib/stealth/services/jobs/handle_message_job.rb:17:in `perform'
(...)
14:27:40 sidekiq.1 | [catch_all] CatchAll level1 triggered for error-1540684789327149-hello-say_hello: undefined local variable or method `b' for #<HellosController:0x0000564c20188608>

Dynamic Delays

Add an option for delay reply types called dynamic. It would look this within a reply:

- reply_type: text
  text: "Hello World!"
- reply_type: delay
  duration: dynamic

The calculated delay would be dynamic and based on the previous reply type and length. So for example, a short text like "Hello World!" would require a short delay of maybe 1 second. An image or a long paragraph would require a larger one.

Allow FlowMap to specify a redirect_to flow and state

It's possible that over a bot's lifecycle a state gets deleted from a flow. It's possible a past user may have previous session that still points to that state. If we simply delete the state, when the user re-engages the bot they will be sent to CatchAll.

Just like fails_to, we should allow a state to specify a redirects_to parameter. This allows the FlowMap to handle legacy states in a deliberate way.

bug in sidekiq service

there is bug in messageHandle job if message is empty then it gives error. if you take it as a bug then i will love to fix the issue

Allow an expiration duration to be specified for sessions

Even though session keys are quite small, large scale bots might want these to be cleared out. If an expiration duration is specified, Stealth should purge sessions that are as old (or older) than the specified duration.

If a key is accessed, the TTL should be reset. This ensures active sessions are not purged.

Stealth route system

A more power route system.
I not found a way to build several routes to evaluate content message and forward to specific flow/state. Like a Rails Routes or a Lita Chat Routes. Maybe this be a good idea to avoid many if's in Controllers.

I Would like a config file to put my routes using regex
When message coming, try match with any route described in this file, and, if match, step_to the specific flow/state.

Same like

Stealth.routes.draw do
  match /^hello/, step_to: flow: 'hello', state: 'say_hello'
  match /^bye/, step_to: flow: 'goodbye', state: 'say_goodbye'
end

before_action don't work for BotController actions

Describe the bug
I tried use before_action to handle messages in BotController.route, but not work, I tried use in action of another Controller, and works. Why?

To Reproduce

class BotController < Stealth::Controller

  helper :all
  before_action :print_this, only: :route


  def route
    if current_session.present?
      step_to session: current_session
    else
      step_to flow: 'hello', state: 'say_hello'
    end
  end

  def print_this
    puts "\n\n\n\n\n aaaaaaaaaaaaaaaaaaa \n\n\n\n"
  end

end

Expected behavior
Show print_this message in log

Dependabot can't evaluate your Ruby dependency files

Dependabot can't evaluate your Ruby dependency files.

As a result, Dependabot couldn't check whether any of your dependencies are out-of-date.

The error Dependabot encountered was:

Bundler::Dsl::DSLError with message: 
[!] There was an error parsing `Gemfile`: 
[!] There was an error while loading `stealth.gemspec`: No such file or directory @ rb_sysopen - /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-1fq4h9t/VERSION. Bundler cannot continue.

 #  from /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-1fq4h9t/stealth.gemspec:3
 #  -------------------------------------------
 #  
 >  version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
 #  
 #  -------------------------------------------
. Bundler cannot continue.

 #  from /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-1fq4h9t/Gemfile:2
 #  -------------------------------------------
 #  source 'https://rubygems.org'
 >  gemspec
 #  
 #  -------------------------------------------

You can mention @dependabot in the comments below to contact the Dependabot team.

Add suport to handle message reads event

The stealth framework has support to referrals, payloads, attachments and normal messages. Have any chance to add a read or another generic attribute in Stealth::ServiceMessage to be possible to implement MessageReadsEvent or another event?

Or maybe another strategy to solve this?

I implement this event in my fork, but I put the value in payload attribute. :X

So, I can't send the pull request this feature!

Thanks =)

How can I use typing indicator to slow tasks?

Hello awesome Stealth Team, I'm using Stealth to make a chatbot to get information from a internal API.

How can I enable typing indicator before a slow task, send replies with results and after to disable typing indicator? Something like this:

    class HellosController < BotController
    
      def say_hello
        # enable_typing_indicator
        # Execute a expensive task
        send_replies
        # disable_typing_indicator
      end
    
    end

It's possible? make sense? another way?

Running stealth generate flow <NAME> generates wrong controller name

Describe the bug
Running stealth generate flow <NAME> generates wrong controller name . The name is not according to naming conventions. The name is generated in pluralised form but not in camel case.

To Reproduce
Steps to reproduce the behavior:

  1. run stealth generate flow camel_cased

Expected behavior
CamelCasedController < BotController

Current behaviour
camelCasedController < BotController

Additional Information
I am working on the issue, just wanted to inform about the bug which i came across while using the gem. Just awaiting for your response and I will be raising a PR once i get a response from your side .

BotController Load Order

I think I found a load order issue for BotController.

Take the following example: I have a controller named abouts_controller.rb that looks like the following:

class AboutsController < BotController

  def say_about_haven_life
    send_replies
  end

end

When I load the app I receive the following error:

12:45:18 web.1     | ! Unable to load application: NameError: uninitialized constant BotController
12:45:18 web.1     | bundler: failed to load command: puma (/Users/matthew/.rbenv/versions/2.4.1/bin/puma)
12:45:18 web.1     | NameError: uninitialized constant BotController
12:45:18 web.1     |   /Users/matthew/Documents/Development/haven-chatbot/bot/controllers/abouts_controller.rb:1:in `<top (required)>'

However, when I rename the controller to contacts_controller.rb it's able to find BotController. Would this possibly be because AboutsController starts with an A and is loading before BotController which starts with a B?

Possibility to pass a specific yaml_reply file name to send_replies

Is your feature request related to a problem? Please describe.
would be extremely useful if we can pass to send_replies method a specific yaml_reply name in cases with expensive task or reuse a specific yaml_reply, etc

Describe the solution you'd like

  def say_meus_atendimentos
    send_replies('wait_reply')
   @data = Google.search('stealth chatbots')
    send_replies
  end

Or

  def say_meus_atendimentos
   @data = Google.search('stealth chatbots')
    if @data
       send_replies
    else
      send_replies('no_results')
    end    
  end

and many others cases

In https://github.com/hellostealth/stealth/blob/master/lib/stealth/controller/replies.rb, something like this, maybe works.

(...)
def send_replies(yml_reply_file_name = nil)
yaml_reply, preprocessor = action_replies(yml_reply_file_name)
(...)

      def action_replies(yml_reply_file_name=nil)
        reply_dir = [*self._replies_path, replies_folder]
        reply_filename = "#{yml_reply_file_name || current_session.state_string}.yml"

(...)

Dependabot can't evaluate your Ruby dependency files

Dependabot can't evaluate your Ruby dependency files.

As a result, Dependabot couldn't check whether any of your dependencies are out-of-date.

The error Dependabot encountered was:

Bundler::Dsl::DSLError with message: 
[!] There was an error parsing `Gemfile`: 
[!] There was an error while loading `stealth.gemspec`: No such file or directory @ rb_sysopen - /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-ceoh69/VERSION. Bundler cannot continue.

 #  from /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-ceoh69/stealth.gemspec:3
 #  -------------------------------------------
 #  
 >  version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
 #  
 #  -------------------------------------------
. Bundler cannot continue.

 #  from /home/dependabot/dependabot-updater/tmp/dependabot_20180921-6-ceoh69/Gemfile:2
 #  -------------------------------------------
 #  source 'https://rubygems.org'
 >  gemspec
 #  
 #  -------------------------------------------

You can mention @dependabot in the comments below to contact the Dependabot team.

Raise error if there are migrations pending

In Rails 5.x+, if migrations are pending, an error is raised and error page is shown in development.

Since we don't have an "error page" I was thinking we would just raise an error (when in development).

Any way to record all incoming and outgoing messages?

Hello friends,

Exist any way to record all incoming and outgoing messages?

Example if I need store messages for analysis like a chatbase.com or botanalytics.co or to check if a message was read by user (I need store message before)

Thanks for help guys =)

Allow bots to run without ActiveRecord

There is preliminary support for this, but it needs to be more polished.

  • Add activerecord and railties gems to Gemfile so the user can remove them
  • Attempting to configure ActiveRecord from a DATABASE_URL or database.yml file should both detect the presence of ActiveRecord.

Document How to Write an Integration

Hello,

How does the integration API work? Details on how to write a custom integration is missing from the documentation. Currently the only way to write a custom driver seems to go through source code of existing integrations.

A guided example for getting started would be great!

Thanks.

Complete example

A good idea would be a complete sample of chatbot using Stealth
Complete samples is a awesome way to start and learn fast.

Sample trivia, or weather reporter, or another.

PS: congratulations form this project this is fantastic, I'm very excited to use in my chatbot projects and help it grow.

Example of CatchAll use

Hello my friends,

  1. Anybody have a simple and practical example of catch all use with multiples levels?

  2. Exist the possibility to get a rescued error in levels actions to customize messages?

Thanks :D

Generating a new flow generates invalid files

Describe the bug
Generating a new flow adds a model file and a module that attempts to inherit (not allowed).

To Reproduce
Steps to reproduce the behavior:

  1. stealth g flow <flow_name>

Expected behavior
The generator generates:

  1. A controller (with pluralized flow name)
  2. A replies directory with only a single example
  3. A flow_map.rb entry with only a single example
  4. A correct helper module (modules cannot inherit)

uninitialized constant BotRecord (NameError)

Describe the bug
Can not run stealth console in terminal.

To Reproduce
Steps to reproduce the behavior:

  1. open new terminal tab while in stealth generated app, invoke stealth console

Expected behavior
I expect after invoking stealth console in the terminal. I expect that a console prompt
for stealth will show.

Screenshots
screen shot 2018-06-03 at 5 43 19 pm

Instead of a prompt, there is a stack trace that says that BotRecord is uninitialized.
However, there is a BotRecord file in the Bot/Models folder and my stealth generated flow named: Appointment Inherits from it.

Desktop (please complete the following information):

  • OS: OS Mac Os 10.9.5
  • Browser: Chrome 67.0.3396

Additional context
Add any other context about the problem here.

Add support for service variants

The bot should be able to send service-based replies. For example, if the bot is replying to a message via a state called hello:

Facebook users would receive the reply in hello.yml+facebook.erb
Twilio SMS users would receive the reply in hello.yml+twilio.erb
Every other service would receive the reply in hello.yml.erb

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.