ego 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +94 -1
- data/ego.gemspec +2 -0
- data/lib/ego.rb +5 -5
- data/lib/ego/handler/default.rb +18 -2
- data/lib/ego/handler/echo.rb +1 -2
- data/lib/ego/handler/handlers.rb +1 -0
- data/lib/ego/listener.rb +2 -1
- data/lib/ego/options.rb +4 -0
- data/lib/ego/robot.rb +0 -11
- data/lib/ego/runner.rb +40 -9
- data/lib/ego/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2250f057ded898c375e1c7007080abbddb3ddbcb
|
4
|
+
data.tar.gz: 05c37f206fa38f7ba8a6879d4519dba59dfd74c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e3b434aece55fb5a6f7b0c1ce850cc2b9f41cec5b092bd59e28773ff03c16c92ae9445828981eb147129f8d6c17c3ffccd1834448396325e04661dae2bde0e3
|
7
|
+
data.tar.gz: 1c3d3feb0fb8d531a931c5522ce2803e32d6a82d5875bed4b3b4d12b5c632c083b1eb3657439cedb5b88b03a218ef3582c4b9911df1d6b376548684b15353f9d
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Ego
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/ego)
|
4
|
+
|
3
5
|
Ego is a personal command-line assistant that provides a flexible, natural
|
4
6
|
language interface (sort of) for interacting with other programs. Think of
|
5
7
|
it as a single-user IRC bot that can be extended with handlers for various
|
@@ -7,15 +9,106 @@ natural-language queries.
|
|
7
9
|
|
8
10
|
## Installation
|
9
11
|
|
12
|
+
Ego requires Ruby. If you already have Ruby installed, run:
|
13
|
+
|
10
14
|
$ gem install ego
|
11
15
|
|
12
16
|
## Usage
|
13
17
|
|
18
|
+
Ego responds to natural-language queries at the command-line:
|
19
|
+
|
14
20
|
$ ego what can you do?
|
15
21
|
|
22
|
+
If you want to interact with ego as a REPL, try:
|
23
|
+
|
24
|
+
$ ego --shell
|
25
|
+
|
26
|
+
## Extending
|
27
|
+
|
28
|
+
Ego does very few things out of the box, but it's designed to be extensible!
|
29
|
+
You can personalize ego and teach it to do new things by defining "handlers",
|
30
|
+
which are small scripts that tell ego what queries to listen for and how to
|
31
|
+
respond to them. Here's what a handler looks like that responds to a query
|
32
|
+
beginning with "hello...", "hi...", or "hey..." with its own random greeting:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
Ego::Handler.register do |handler|
|
36
|
+
handler.description = 'greet you'
|
37
|
+
|
38
|
+
handler.listen /^(hello|hi|hey)/i, priority: 3
|
39
|
+
|
40
|
+
handler.run do |robot|
|
41
|
+
robot.respond [
|
42
|
+
'Hello.',
|
43
|
+
'Hi.',
|
44
|
+
'Ciao.',
|
45
|
+
].sample
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
$ ego hello
|
51
|
+
> Hi.
|
52
|
+
|
53
|
+
Let's break that down:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
handler.description = 'greet you'
|
57
|
+
```
|
58
|
+
|
59
|
+
The description is for your own reference, and answers the question "What can
|
60
|
+
this handler do?"
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
handler.listen /^(hello|hi|hey)/i, priority: 3
|
64
|
+
```
|
65
|
+
|
66
|
+
This is the listener, which specifies what queries should invoke the handler.
|
67
|
+
The first argument is a regular expression to match the query against.
|
68
|
+
Sometimes you may want to match very specific things and sometimes something
|
69
|
+
broader. To help ego respond the right way when two or more listeners match
|
70
|
+
your query, you can optionally specify a `:priority` (higher number = higher
|
71
|
+
priority). One handler can have as many listeners as you want, even ones with
|
72
|
+
different priorities.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
handler.run do |robot|
|
76
|
+
# ...
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
This is the part that gets run when your handler matches the query. From here
|
81
|
+
you can do anything you want including deferring to external programs. The
|
82
|
+
`robot` is made available to you to respond to the user. Usually, you'll want
|
83
|
+
to make use of part of the query in your handler. You can access named match
|
84
|
+
groups through the optional `params` parameter:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Ego::Handler.register do |handler|
|
88
|
+
handler.description = 'repeat what you say'
|
89
|
+
|
90
|
+
handler.listen /^say (?<input>.+)/i
|
91
|
+
|
92
|
+
handler.run do |robot, params|
|
93
|
+
robot.respond params[:input]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Try it out (this one is already included):
|
99
|
+
|
100
|
+
$ ego say something
|
101
|
+
> something
|
102
|
+
|
103
|
+
Ego looks for user-defined handlers in `$XDG_CONFIG_HOME/ego/handlers/`
|
104
|
+
(that's `~/.config/ego/handlers/` by default), and registers them
|
105
|
+
automatically at runtime. Each handler goes in it's own file with an `.rb`
|
106
|
+
extension (e.g., `~/.config/ego/handlers/my_handler.rb`). Be careful—ego will
|
107
|
+
execute any Ruby scripts in this directory indiscriminately.
|
108
|
+
|
16
109
|
## License
|
17
110
|
|
18
|
-
Copyright (C) 2016 Noah Frederick
|
111
|
+
Copyright (C) 2016-2017 Noah Frederick
|
19
112
|
|
20
113
|
This program is free software: you can redistribute it and/or modify
|
21
114
|
it under the terms of the GNU General Public License as published by
|
data/ego.gemspec
CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
+
spec.required_ruby_version = "~> 2.0"
|
27
|
+
|
26
28
|
spec.add_development_dependency "bundler", "~> 1.6"
|
27
29
|
spec.add_development_dependency "rake"
|
28
30
|
spec.add_development_dependency "rspec", "~> 3.4"
|
data/lib/ego.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
require_relative 'ego/version'
|
2
|
+
require_relative 'ego/options'
|
3
|
+
require_relative 'ego/filesystem'
|
4
|
+
require_relative 'ego/robot'
|
5
|
+
require_relative 'ego/handler'
|
data/lib/ego/handler/default.rb
CHANGED
@@ -6,10 +6,26 @@ Ego::Handler.register do |handler|
|
|
6
6
|
handler.run do |robot, params|
|
7
7
|
robot.respond %Q{I don't understand "#{params[0]}".}
|
8
8
|
|
9
|
+
handler_slug = params[0]
|
10
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
11
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
12
|
+
.tr('\'', '')
|
13
|
+
.gsub(/\W+/, '_')
|
14
|
+
.gsub(/__+/, '_')
|
15
|
+
.downcase
|
16
|
+
|
9
17
|
STDERR.puts <<-EOF
|
10
|
-
Perhaps add a handler to #{Ego::Filesystem.config
|
18
|
+
Perhaps add a handler to #{Ego::Filesystem.config "handler/#{handler_slug}.rb"}:
|
19
|
+
|
20
|
+
Ego::Handler.register do |handler|
|
21
|
+
handler.description = 'do something'
|
22
|
+
|
23
|
+
handler.listen /^#{params[0]}$/
|
11
24
|
|
12
|
-
|
25
|
+
handler.run do |robot, params|
|
26
|
+
# ...
|
27
|
+
end
|
28
|
+
end
|
13
29
|
EOF
|
14
30
|
end
|
15
31
|
end
|
data/lib/ego/handler/echo.rb
CHANGED
data/lib/ego/handler/handlers.rb
CHANGED
@@ -5,6 +5,7 @@ Ego::Handler.register do |handler|
|
|
5
5
|
|
6
6
|
handler.listen /^(show me|show|tell me|list)\s+(handlers|what you can do|what (?:you are|you're) able to do|what you do|what (?:queries )?you (?:can )?understand)$/i
|
7
7
|
handler.listen /^what (?:can you|are you able to|do you) (?:do|handle|understand)\??$/i
|
8
|
+
handler.listen /^help/i, priority: 2
|
8
9
|
|
9
10
|
handler.run do |robot|
|
10
11
|
robot.respond 'I know how to...'
|
data/lib/ego/listener.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Ego
|
2
|
+
# Listeners map user queries to handlers.
|
2
3
|
class Listener
|
3
4
|
include Comparable
|
4
5
|
|
@@ -16,7 +17,7 @@ module Ego
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def match query
|
19
|
-
return false unless matches = @pattern.match(query)
|
20
|
+
return false unless (matches = @pattern.match(query))
|
20
21
|
|
21
22
|
@parser.call matches
|
22
23
|
end
|
data/lib/ego/options.rb
CHANGED
@@ -24,6 +24,10 @@ module Ego
|
|
24
24
|
@robot_name = opts.program_name.capitalize
|
25
25
|
opts.banner = "Usage: #{opts.program_name} [ options ] query..."
|
26
26
|
|
27
|
+
opts.on("-s", "--shell", "Start in REPL-mode") do
|
28
|
+
@mode = :shell
|
29
|
+
end
|
30
|
+
|
27
31
|
opts.on("-v", "--version", "Print version number") do
|
28
32
|
@mode = :version
|
29
33
|
end
|
data/lib/ego/robot.rb
CHANGED
@@ -10,26 +10,15 @@ module Ego
|
|
10
10
|
|
11
11
|
def respond message
|
12
12
|
@formatter.robot_respond message
|
13
|
-
true
|
14
13
|
end
|
15
14
|
|
16
15
|
def it message
|
17
16
|
@formatter.robot_action message
|
18
|
-
true
|
19
17
|
end
|
20
18
|
|
21
19
|
def debug message
|
22
20
|
return unless @options.verbose
|
23
21
|
@formatter.debug message
|
24
|
-
true
|
25
|
-
end
|
26
|
-
|
27
|
-
def stop
|
28
|
-
true
|
29
|
-
end
|
30
|
-
|
31
|
-
def continue
|
32
|
-
false
|
33
22
|
end
|
34
23
|
end
|
35
24
|
end
|
data/lib/ego/runner.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative 'options'
|
3
|
-
require_relative 'filesystem'
|
4
|
-
require_relative 'robot'
|
5
|
-
require_relative 'handler'
|
1
|
+
require_relative '../ego'
|
6
2
|
|
7
3
|
module Ego
|
8
4
|
# The Ego::Runner class, given an array of arguments, initializes the
|
9
5
|
# required objects and executes the request.
|
10
6
|
class Runner
|
7
|
+
PROMPT = 'ego, '.green.freeze
|
8
|
+
QUIT = /^q(uit)?|exit|(good)?bye$/.freeze
|
9
|
+
|
11
10
|
# Takes an array of arguments and parses them into options:
|
12
11
|
#
|
13
12
|
# runner = Ego::Runner.new(ARGV)
|
@@ -31,12 +30,44 @@ module Ego
|
|
31
30
|
exit(-1) if @options.usage_error
|
32
31
|
when :version
|
33
32
|
@formatter.puts "ego v#{Ego::VERSION}"
|
33
|
+
when :shell
|
34
|
+
init_robot
|
35
|
+
start_repl
|
34
36
|
else
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
init_robot
|
38
|
+
handle_query @options.query
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def init_robot
|
45
|
+
@robot = Ego::Robot.new(@options, @formatter)
|
46
|
+
Ego::Handler.load Ego::Filesystem.user_handlers
|
47
|
+
Ego::Handler.load Ego::Filesystem.builtin_handlers
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_query(query)
|
51
|
+
Ego::Handler.dispatch @robot, query
|
52
|
+
end
|
53
|
+
|
54
|
+
def prompt
|
55
|
+
Readline.readline(PROMPT, true)
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_repl
|
59
|
+
require 'readline'
|
60
|
+
|
61
|
+
# Store the state of the terminal
|
62
|
+
stty_save = `stty -g`.chomp
|
63
|
+
|
64
|
+
loop do
|
65
|
+
query = prompt
|
66
|
+
break if query.nil? || query.strip =~ QUIT
|
67
|
+
handle_query query.strip
|
39
68
|
end
|
69
|
+
rescue Interrupt => e
|
70
|
+
system('stty', stty_save) # Restore state
|
40
71
|
end
|
41
72
|
end
|
42
73
|
end
|
data/lib/ego/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ego
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Noah Frederick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -128,9 +128,9 @@ require_paths:
|
|
128
128
|
- lib
|
129
129
|
required_ruby_version: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
|
-
- - "
|
131
|
+
- - "~>"
|
132
132
|
- !ruby/object:Gem::Version
|
133
|
-
version: '0'
|
133
|
+
version: '2.0'
|
134
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - ">="
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
138
|
version: '0'
|
139
139
|
requirements: []
|
140
140
|
rubyforge_project:
|
141
|
-
rubygems_version: 2.
|
141
|
+
rubygems_version: 2.6.8
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: An extensible personal command-line assistant
|