daneel 0.0.1 → 0.1
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.
- data/.gitignore +12 -18
- data/Gemfile +8 -0
- data/Procfile +1 -0
- data/bin/daneel +18 -0
- data/daneel.gemspec +19 -12
- data/lib/daneel.rb +4 -2
- data/lib/daneel/adapter.rb +37 -0
- data/lib/daneel/adapters/campfire.rb +93 -0
- data/lib/daneel/adapters/shell.rb +36 -0
- data/lib/daneel/bot.rb +89 -0
- data/lib/daneel/data.rb +30 -0
- data/lib/daneel/logger.rb +19 -0
- data/lib/daneel/message.rb +28 -0
- data/lib/daneel/options.rb +37 -0
- data/lib/daneel/plugin.rb +21 -0
- data/lib/daneel/room.rb +19 -0
- data/lib/daneel/script.rb +39 -0
- data/lib/daneel/scripts/chatty.rb +106 -0
- data/lib/daneel/scripts/echo.rb +21 -0
- data/lib/daneel/scripts/help.rb +35 -0
- data/lib/daneel/scripts/image_search.rb +50 -0
- data/lib/daneel/scripts/meme.rb +51 -0
- data/lib/daneel/scripts/reload.rb +51 -0
- data/lib/daneel/scripts/vine_search.rb +51 -0
- data/lib/daneel/server.rb +39 -0
- data/lib/daneel/user.rb +26 -0
- data/lib/daneel/version.rb +1 -1
- data/lib/daneel/web.rb +12 -0
- metadata +125 -71
data/.gitignore
CHANGED
@@ -1,18 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
rdoc/
|
14
|
-
spec/reports/
|
15
|
-
test/tmp/
|
16
|
-
test/version_tmp/
|
17
|
-
tmp/
|
18
|
-
vendor/bundle/
|
1
|
+
/*.gem
|
2
|
+
/.bundle/
|
3
|
+
/.env
|
4
|
+
/.yardoc/
|
5
|
+
/coverage/
|
6
|
+
/doc/
|
7
|
+
/exec/
|
8
|
+
/Gemfile.lock
|
9
|
+
/pkg/
|
10
|
+
/rdoc/
|
11
|
+
/tmp/
|
12
|
+
/vendor/bundle/
|
data/Gemfile
CHANGED
@@ -2,3 +2,11 @@ source 'http://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in daneel.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
# Override gem dependencies that aren't released yet
|
7
|
+
gem 'sinatra', :github => 'sinatra'
|
8
|
+
gem 'sparks', :github => 'indirect/sparks', :branch => 'master'
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem 'pry-debugger'
|
12
|
+
end
|
data/Procfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
web: ruby -Ku -rbundler/setup -Ilib -S ./bin/daneel -v -s -a campfire -n "wes" -f "Wesabot"
|
data/bin/daneel
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'daneel'
|
2
|
+
|
3
|
+
trap(:INT) do
|
4
|
+
puts "Quitting...\n"
|
5
|
+
exit 0
|
6
|
+
end
|
7
|
+
|
8
|
+
options = Daneel::Options.parse(ARGV)
|
9
|
+
options[:logger] = Daneel::Logger.new(STDOUT, options[:verbose])
|
10
|
+
|
11
|
+
if options.has_key?(:server)
|
12
|
+
require 'daneel/server'
|
13
|
+
sopts = options.delete(:server)
|
14
|
+
sopts[:logger] = options[:logger]
|
15
|
+
Daneel::Server.new(sopts).run
|
16
|
+
end
|
17
|
+
|
18
|
+
Daneel::Bot.new(options).run
|
data/daneel.gemspec
CHANGED
@@ -1,20 +1,27 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# encoding: UTF-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'daneel/version'
|
3
5
|
|
4
6
|
Gem::Specification.new do |gem|
|
5
|
-
gem.
|
7
|
+
gem.name = "daneel"
|
8
|
+
gem.version = Daneel::VERSION
|
9
|
+
gem.summary = %q{A 19,230-year-old robot}
|
10
|
+
gem.description = %q{Daneel is a chatbot inspired by the late, lamented Wesabot. And also Hubot.}
|
11
|
+
gem.authors = ["André Arko"]
|
6
12
|
gem.email = ["andre@arko.net"]
|
7
|
-
gem.description = %q{A 19,230-year-old Campfire bot}
|
8
|
-
gem.summary = %q{A library and rack app to bot up your Campfire rooms}
|
9
13
|
gem.homepage = "http://github.com/indirect/daneel"
|
14
|
+
gem.license = "MIT"
|
10
15
|
|
11
|
-
gem.
|
12
|
-
gem.
|
13
|
-
gem.test_files =
|
14
|
-
gem.name = "daneel"
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
19
|
gem.require_paths = ["lib"]
|
16
|
-
gem.version = Daneel::VERSION
|
17
20
|
|
18
|
-
gem.add_dependency "
|
19
|
-
gem.add_dependency "
|
21
|
+
gem.add_dependency "sparks", "~> 0.4"
|
22
|
+
gem.add_dependency "sinatra", "~> 1.4.0"
|
23
|
+
gem.add_dependency "puma", "~> 1.6.3"
|
24
|
+
|
25
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
26
|
+
gem.add_development_dependency "bundler", "~> 1.2"
|
20
27
|
end
|
data/lib/daneel.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'daneel/message'
|
2
|
+
require 'daneel/plugin'
|
3
|
+
require 'daneel/room'
|
4
|
+
require 'daneel/user'
|
5
|
+
|
6
|
+
module Daneel
|
7
|
+
class Adapter < Plugin
|
8
|
+
|
9
|
+
def run
|
10
|
+
# listen to rooms and dispatch messages to robot.receive
|
11
|
+
end
|
12
|
+
|
13
|
+
def say(room_id, message)
|
14
|
+
# get the message into the room!
|
15
|
+
end
|
16
|
+
|
17
|
+
def announce(message)
|
18
|
+
# say the message into every room the bot is in
|
19
|
+
end
|
20
|
+
|
21
|
+
def me
|
22
|
+
@me ||= User.new(0, "R. Daneel Olivaw").tap do |me|
|
23
|
+
me.short_name = "Daneel"
|
24
|
+
me.initials = "DO"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def named(name)
|
30
|
+
require File.join('daneel/adapters', name.downcase)
|
31
|
+
adapter = Daneel::Adapters.const_get(name.capitalize)
|
32
|
+
adapter || raise("Couldn't find Daneel::Adapters::#{a.capitalize}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'sparks'
|
2
|
+
require 'daneel/adapter'
|
3
|
+
|
4
|
+
module Daneel
|
5
|
+
class Adapters
|
6
|
+
class Campfire < Daneel::Adapter
|
7
|
+
requires_env %w(CAMPFIRE_SUBDOMAIN CAMPFIRE_API_TOKEN CAMPFIRE_ROOM_IDS)
|
8
|
+
|
9
|
+
def initialize(robot)
|
10
|
+
super
|
11
|
+
domain = ENV['CAMPFIRE_SUBDOMAIN']
|
12
|
+
token = ENV['CAMPFIRE_API_TOKEN']
|
13
|
+
@fire = Sparks.new(domain, token, :logger => logger)
|
14
|
+
|
15
|
+
ENV['CAMPFIRE_ROOM_IDS'].split(",").map(&:to_i).map do |id|
|
16
|
+
# Get info about the room state
|
17
|
+
room = Room.new(id, self, @fire.room(id))
|
18
|
+
robot.data.rooms[id] = room
|
19
|
+
# Save the user info for all the users in the room
|
20
|
+
room.data["users"].each do |data|
|
21
|
+
user = User.new(data["id"], data["name"], data)
|
22
|
+
robot.data.users[user.id] = user
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
@threads ||= []
|
29
|
+
robot.data.rooms.each do |id, room|
|
30
|
+
t = Thread.new { watch_room(room) } until t
|
31
|
+
t.abort_on_exception = true
|
32
|
+
@threads << t
|
33
|
+
end
|
34
|
+
@threads.each{|t| t.join }
|
35
|
+
end
|
36
|
+
|
37
|
+
def say(id, *texts)
|
38
|
+
texts.each do |text|
|
39
|
+
text =~ /\n/ ? @fire.paste(id, text) : @fire.speak(id, text)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def announce(*texts)
|
44
|
+
robot.data.rooms.each do |id, room|
|
45
|
+
say id, *texts
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def leave
|
50
|
+
# stop the listening threads
|
51
|
+
@threads.each{|t| t.kill }
|
52
|
+
# leave each room
|
53
|
+
robot.data.rooms.each{|r| @fire.room(r.id).leave }
|
54
|
+
end
|
55
|
+
|
56
|
+
def me
|
57
|
+
@me ||= begin
|
58
|
+
data = @fire.me
|
59
|
+
me = User.new(data["id"], data["name"], data)
|
60
|
+
robot.data.users[me.id] = me
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def find_user(id)
|
67
|
+
robot.data.users[id] ||= begin
|
68
|
+
data = @fire.user(id)
|
69
|
+
User.new(data["id"], data["name"], data)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def watch_room(room)
|
74
|
+
@fire.watch(room.id) do |data|
|
75
|
+
next if data["type"] == "TimestampMessage"
|
76
|
+
|
77
|
+
# TODO pass through self-messages, once they are filtered by
|
78
|
+
# the accept? method on scripts
|
79
|
+
next if data["user_id"] == me.id
|
80
|
+
|
81
|
+
text = data["body"]
|
82
|
+
time = Time.parse(data["created_at"]) rescue Time.now
|
83
|
+
type = data["type"].gsub(/Message$/, '').downcase
|
84
|
+
mesg = Message.new(text, time, type)
|
85
|
+
room = robot.data.rooms[data["room_id"]]
|
86
|
+
user = find_user(data["user_id"])
|
87
|
+
robot.receive room, mesg, user
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'daneel/adapter'
|
2
|
+
require 'readline'
|
3
|
+
|
4
|
+
module Daneel
|
5
|
+
class Adapters
|
6
|
+
class Shell < Daneel::Adapter
|
7
|
+
|
8
|
+
def initialize(robot)
|
9
|
+
super
|
10
|
+
@room = Room.new("shell", self)
|
11
|
+
@user = User.new(1, ENV['USER'])
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
# End the line we were on when we exit
|
16
|
+
trap(:EXIT){ print "\n" }
|
17
|
+
|
18
|
+
while text = Readline.readline("> ", true)
|
19
|
+
next if text.empty?
|
20
|
+
message = Message.new(text, Time.now, "text")
|
21
|
+
robot.receive @room, message, @user
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def say(id, *strings)
|
26
|
+
puts *strings
|
27
|
+
end
|
28
|
+
|
29
|
+
def announce(*strings)
|
30
|
+
puts
|
31
|
+
say @room.id, *strings
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/daneel/bot.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'daneel/adapter'
|
2
|
+
require 'daneel/script'
|
3
|
+
require 'daneel/data'
|
4
|
+
|
5
|
+
module Daneel
|
6
|
+
class Bot
|
7
|
+
attr_reader :adapter, :data, :full_name, :logger, :name, :scripts
|
8
|
+
attr_accessor :debug_mode
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@logger = options[:logger] || Daneel::Logger.new
|
12
|
+
@name = options[:name] || "daneel"
|
13
|
+
@full_name = options[:full_name] || options[:name] || "R. Daneel Olivaw"
|
14
|
+
@debug_mode = options[:verbose] && options[:adapter] && options[:adapter] != "shell"
|
15
|
+
|
16
|
+
@data = Data.new
|
17
|
+
logger.debug "Data source #{data.class}"
|
18
|
+
|
19
|
+
Script.files.each{|file| try_require file }
|
20
|
+
# TODO add script priorities to replicate this
|
21
|
+
list = Script.list
|
22
|
+
list.push list.delete(Scripts::ImageSearch)
|
23
|
+
list.push list.delete(Scripts::Chatty)
|
24
|
+
@scripts = list.map{|s| s.new(self) }
|
25
|
+
logger.debug "Booted with scripts: #{@scripts.map(&:class).inspect}"
|
26
|
+
|
27
|
+
@adapter = Adapter.named(options[:adapter] || "shell").new(self)
|
28
|
+
logger.debug "Using the #{adapter.class} adapter"
|
29
|
+
end
|
30
|
+
|
31
|
+
def receive(room, message, user)
|
32
|
+
logger.debug "[room #{room.id}] #{user.name}: #{message.text}"
|
33
|
+
message.command = command_from(message.text)
|
34
|
+
|
35
|
+
scripts.each do |script|
|
36
|
+
next unless script.accepts?(room, message, user)
|
37
|
+
script.receive(room, message, user)
|
38
|
+
break if message.done
|
39
|
+
end
|
40
|
+
message
|
41
|
+
rescue => e
|
42
|
+
msg = %|#{e.class}: #{e.message}\n #{e.backtrace.join("\n ")}|
|
43
|
+
logger.error msg
|
44
|
+
adapter.announce "crap, something went wrong. :(", msg if @debug_mode
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
# TODO add i18n so that people can customize their bot's attitude
|
49
|
+
# http://titusd.co.uk/2010/03/04/i18n-internationalization-without-rails/
|
50
|
+
# TODO add Confabulator processing so the bot can be chatty without being static
|
51
|
+
|
52
|
+
# Heroku cycles every process at least once per day by sending it a TERM
|
53
|
+
trap(:TERM) do
|
54
|
+
@adapter.announce "asked to stop, brb"
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
@adapter.announce "hey guys"
|
59
|
+
@adapter.run
|
60
|
+
rescue Interrupt
|
61
|
+
adapter.leave
|
62
|
+
end
|
63
|
+
|
64
|
+
def user
|
65
|
+
@adapter.me
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
%|#<#{self.class}:#{object_id} @name="#{name}" @adapter=#{adapter.class}>|
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def command_from(text)
|
75
|
+
return if text.nil? || text.empty?
|
76
|
+
m = text.match(/^@#{name}\s+(.*)/i)
|
77
|
+
m ||= text.match(/^#{name}(?:[,:]\s*|\s+)(.*)/i)
|
78
|
+
m ||= text.match(/^\s*(.*?)(?:,\s*)?\b#{name}[.!?\s]*$/i)
|
79
|
+
m && m[1]
|
80
|
+
end
|
81
|
+
|
82
|
+
def try_require(name)
|
83
|
+
require name
|
84
|
+
rescue Script::DepError => e
|
85
|
+
logger.warn "Couldn't load #{File.basename(name)}: #{e.message}"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/lib/daneel/data.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Daneel
|
2
|
+
# Non-persistent storage, extend if you want more.
|
3
|
+
class Data
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@store = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def save
|
10
|
+
# it's a hash, what do you want?
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@store[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
@store[key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def users
|
22
|
+
@store["users"] ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def rooms
|
26
|
+
@store["rooms"] ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
class Logger < ::Logger
|
5
|
+
|
6
|
+
def initialize(io = $stdout, verbose = false)
|
7
|
+
super
|
8
|
+
self.level = Logger::INFO unless verbose
|
9
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
10
|
+
"#{severity} #{msg}\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
%|#<#{self.class}:#{object_id} @level=#{level}>|
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
class Message
|
5
|
+
attr_reader :command, :done, :room, :text, :time, :type
|
6
|
+
attr_accessor :args
|
7
|
+
|
8
|
+
def initialize(text, time = Time.now, type = :text)
|
9
|
+
@text, @time, @type = text, time, type
|
10
|
+
end
|
11
|
+
|
12
|
+
def command=(text)
|
13
|
+
@command = text
|
14
|
+
@args = text ? Shellwords.split(text) : nil
|
15
|
+
rescue ArgumentError # shellwords didn't like this
|
16
|
+
@args = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def done!
|
20
|
+
@done = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class} #{text.inspect} #{time.inspect} #{type.inspect}>"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
module Options
|
5
|
+
|
6
|
+
def self.parse(args)
|
7
|
+
options = {}
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: daneel [options]"
|
11
|
+
|
12
|
+
opts.on("-v", "--verbose", "Print debugging information") do |v|
|
13
|
+
options[:verbose] = v
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on("-s", "--server [PORT]", "Run the HTTP server on PORT (default 3333)") do |port|
|
17
|
+
options[:server] = {:port => port}
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-a", "--adapter ADAPTER", "Which interaction adapter to use") do |name|
|
21
|
+
options[:adapter] = name
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("-n", "--name NAME", "The name your bot should respond to") do |name|
|
25
|
+
options[:name] = name
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-f", "--full-name FULLNAME", "The name your bot will use to refer to itself") do |name|
|
29
|
+
options[:full_name] = name
|
30
|
+
end
|
31
|
+
end.parse(args)
|
32
|
+
|
33
|
+
return options
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Daneel
|
2
|
+
class Plugin
|
3
|
+
attr_reader :robot
|
4
|
+
|
5
|
+
def initialize(robot)
|
6
|
+
@robot = robot
|
7
|
+
end
|
8
|
+
|
9
|
+
def logger
|
10
|
+
robot.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
class DepError < LoadError; end
|
14
|
+
|
15
|
+
def self.requires_env(*keys)
|
16
|
+
missing = keys.flatten.select{|k| ENV[k].nil? || ENV[k].empty? }
|
17
|
+
raise DepError, "#{missing.join(',')} must be set" if missing.any?
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/daneel/room.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Daneel
|
2
|
+
class Room
|
3
|
+
attr_reader :id
|
4
|
+
attr_accessor :data
|
5
|
+
|
6
|
+
def initialize(id, adapter, data = nil)
|
7
|
+
@id, @adapter, @data = id, adapter, data
|
8
|
+
end
|
9
|
+
|
10
|
+
def say(*strings)
|
11
|
+
@adapter.say @id, *strings
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
%|#<#{self.class} @id=#{@id.inspect} @adapter=#{@adapter.class}>|
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'daneel/plugin'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
class Script < Plugin
|
5
|
+
|
6
|
+
def accepts?(room, message, user)
|
7
|
+
true unless user.id == robot.user.id
|
8
|
+
end
|
9
|
+
|
10
|
+
def receive(room, message, user)
|
11
|
+
# do stuff here!
|
12
|
+
end
|
13
|
+
|
14
|
+
def help
|
15
|
+
# return a hash of commands and descriptions for help listings
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Track scripts that have loaded so we can use them
|
20
|
+
class << self
|
21
|
+
|
22
|
+
def list
|
23
|
+
@list ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def inherited(subclass)
|
27
|
+
list << subclass
|
28
|
+
end
|
29
|
+
|
30
|
+
def files
|
31
|
+
Dir[File.expand_path("../scripts/*.rb", __FILE__)]
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO accept method for script classes
|
35
|
+
# should handle options like :text, :enter, :leave, :topic, :all
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'daneel/script'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
module Scripts
|
5
|
+
class Chatty < Daneel::Script
|
6
|
+
# TODO make this script the last-priority script
|
7
|
+
# priority 20
|
8
|
+
|
9
|
+
def receive(room, message, user)
|
10
|
+
# Said to the room in general
|
11
|
+
case message.text
|
12
|
+
when /^(night|good ?night)(,?\s(all|every(body|one)))$/i
|
13
|
+
room.say "goodnight, #{user}"
|
14
|
+
when /^(morning|good ?morning)(,?\s(all|every(body|one)))$/i
|
15
|
+
room.say "good morning, #{user}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Said directly to the bot
|
19
|
+
case message.command
|
20
|
+
when nil
|
21
|
+
# don't reply to things not addressed to the bot
|
22
|
+
when /^\s*$/
|
23
|
+
# question questioners, exclaim at exclaimers, dot dotters
|
24
|
+
message.text.match(/([?!.])$/)
|
25
|
+
room.say "#{user}#{$1}"
|
26
|
+
when /^(hey|hi|hello|sup|howdy)/i
|
27
|
+
room.say "#{$1} #{user}"
|
28
|
+
when /how are (you|things)|how\'s it (going|hanging)/i
|
29
|
+
room.say [
|
30
|
+
"Oh, you know, the usual.",
|
31
|
+
"can't complain",
|
32
|
+
"alright, how about you?",
|
33
|
+
"people say things, I say things back"
|
34
|
+
].sample
|
35
|
+
when /(^later|(?:good\s*)?bye)/i
|
36
|
+
room.say("#{$1} #{user}")
|
37
|
+
when /you rock|awesome|cool|nice/i
|
38
|
+
room.say [
|
39
|
+
"Thanks, #{user}, you're pretty cool yourself.",
|
40
|
+
"I try.",
|
41
|
+
"Aw, shucks. Thanks, #{user}."
|
42
|
+
].sample
|
43
|
+
when /(^|you|still)\s*there/i, /\byt\b/i
|
44
|
+
room.say %w{Yup y}.sample
|
45
|
+
when /wake up|you awake/i
|
46
|
+
room.say("yo")
|
47
|
+
when /thanks|thank you/i
|
48
|
+
room.say ["No problem.", "np", "any time", "that's what I'm here for", "You're welcome."].sample
|
49
|
+
when /^(good\s?night|(?:g')?night)$/i
|
50
|
+
room.say [
|
51
|
+
"see you later, #{user}",
|
52
|
+
"later, #{user}",
|
53
|
+
"night",
|
54
|
+
"goodnight",
|
55
|
+
"bye",
|
56
|
+
"have a good night"
|
57
|
+
].sample
|
58
|
+
when /^(see you(?: later)?)$/i
|
59
|
+
room.say [
|
60
|
+
"see you later, #{user}",
|
61
|
+
"later, #{user}",
|
62
|
+
"bye",
|
63
|
+
"later",
|
64
|
+
"see ya",
|
65
|
+
].sample
|
66
|
+
when /^(?:(?:get|grab|fetch|bring) (.*?)|i need|time for)(?: (?:a|some))? coffee$/i
|
67
|
+
person = $1
|
68
|
+
if person =~ /i|me|us/
|
69
|
+
person, do_they = "you", "do you"
|
70
|
+
else
|
71
|
+
do_they = "does #{person}"
|
72
|
+
end
|
73
|
+
|
74
|
+
room.say [
|
75
|
+
"would #{person} like cream or sugar?",
|
76
|
+
"how #{do_they} take it?",
|
77
|
+
"coming right up",
|
78
|
+
"It is by caffeine alone I set my mind in motion",
|
79
|
+
"It is by the beans of Java that thoughts acquire speed",
|
80
|
+
"The hands acquire shakes, the shakes become a warning",
|
81
|
+
"It is by caffeine alone I set my mind in motion"
|
82
|
+
].sample
|
83
|
+
else
|
84
|
+
room.say [
|
85
|
+
"I have no idea what you're talking about, #{user}.",
|
86
|
+
"eh?",
|
87
|
+
"oh, interesting",
|
88
|
+
"say more, #{user}",
|
89
|
+
"#{user}, you do realize that you're talking to a bot with a very limited vocabulary, don't you?",
|
90
|
+
"Whatever, #{user}.",
|
91
|
+
# TODO implement Bot#other_person
|
92
|
+
# "#{bot.other_person(user)}, tell #{user} to leave me alone.",
|
93
|
+
"Not now, #{user}.",
|
94
|
+
"brb crying",
|
95
|
+
"what do you think, #{user}?",
|
96
|
+
"That's really something.",
|
97
|
+
"but what can I do? I'm just a lowly bot",
|
98
|
+
"I'll get some electrons on that right away",
|
99
|
+
"How do you feel when someone says '#{message.command}' to you, #{user}?"
|
100
|
+
].sample
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'daneel/script'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
module Scripts
|
5
|
+
class Echo < Daneel::Script
|
6
|
+
|
7
|
+
def receive(room, message, user)
|
8
|
+
case message.command
|
9
|
+
when /^(?:echo|say)\s(.+)/
|
10
|
+
room.say $1
|
11
|
+
message.done!
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def help
|
16
|
+
{"echo TEXT" => "are you copying me? stop copying me!"}
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'daneel/script'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
module Scripts
|
5
|
+
class Help < Daneel::Script
|
6
|
+
|
7
|
+
def receive(room, message, user)
|
8
|
+
case message.command
|
9
|
+
when /help$/
|
10
|
+
col = helps.keys.map(&:length).max + 2
|
11
|
+
room.say helps.map{|k,v| "%-#{col}s %s" % [k,v] }.sort.join("\n")
|
12
|
+
message.done!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def help
|
17
|
+
{"help" => "show this help summary"}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def helps
|
23
|
+
@helps ||= begin
|
24
|
+
helps = {}
|
25
|
+
robot.scripts.each do |script|
|
26
|
+
helps.merge!(script.help)
|
27
|
+
end
|
28
|
+
logger.debug "Found helps: #{helps.inspect}"
|
29
|
+
helps
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'net/http/persistent'
|
3
|
+
|
4
|
+
module Daneel
|
5
|
+
# You need an Azure Marketplace Bing Search API key. You can sign up for one here:
|
6
|
+
# https://datamarket.azure.com/dataset/bing/search
|
7
|
+
# A free subscription allows 5,000 searches per month.
|
8
|
+
module Scripts
|
9
|
+
class ImageSearch < Daneel::Script
|
10
|
+
requires_env "AZURE_API_KEY"
|
11
|
+
|
12
|
+
def initialize(robot)
|
13
|
+
super
|
14
|
+
@token = ENV['AZURE_API_KEY']
|
15
|
+
@http = Net::HTTP::Persistent.new('daneel')
|
16
|
+
end
|
17
|
+
|
18
|
+
def receive(room, message, user)
|
19
|
+
case message.command
|
20
|
+
when /image me (.*?)/, /^(?:find) (?:me )?(?:a |another )?(?:picture of )?(.*)$/
|
21
|
+
room.say find_image_url_for($1)
|
22
|
+
message.done!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def help
|
27
|
+
{"find a THING" => "scours the internets for a picture of THING to show you"}
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_image_url_for(search)
|
31
|
+
logger.debug "Searching for images of #{search}"
|
32
|
+
query = CGI.escape("'#{search.gsub(/'/, "\\\\'")}'")
|
33
|
+
uri = URI("https://api.datamarket.azure.com/Bing/Search/v1/Composite")
|
34
|
+
uri.query = "Sources=%27image%27&Adult=%27Moderate%27&$format=JSON&Query=#{query}"
|
35
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
36
|
+
request.basic_auth 'x', @token
|
37
|
+
response = @http.request uri, request
|
38
|
+
logger.debug "GET #{uri}"
|
39
|
+
results = JSON.parse(response.body)["d"]["results"].first["Image"]
|
40
|
+
logger.debug "got back #{results.size} images"
|
41
|
+
# Random image from the first 50 results
|
42
|
+
results.sample["MediaUrl"]
|
43
|
+
rescue => e
|
44
|
+
logger.error "#{e.class}: #{e.message}"
|
45
|
+
room.say "Sorry, something went wrong when I looked for '#{query}'"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'daneel/script'
|
2
|
+
|
3
|
+
module Daneel
|
4
|
+
module Scripts
|
5
|
+
class Meme < Daneel::Script
|
6
|
+
|
7
|
+
def receive(room, message, user)
|
8
|
+
case message.text
|
9
|
+
when /^[\:\*\(e]?facepalm[\:\*\)]?|EFACEPALM|:fp|m\($/i
|
10
|
+
room.say [
|
11
|
+
# picard facepalm
|
12
|
+
"https://img.skitch.com/20110224-875h7w1654tgdhgrxm9bikhkwq.jpg",
|
13
|
+
# polar bear facepalm
|
14
|
+
"https://img.skitch.com/20110224-bsd2ds251eit8d3t1y2mkjtfx8.jpg"
|
15
|
+
].sample
|
16
|
+
when /^[\:\*\(e]?double ?facepalm[\:\*\)]?|:fpfp|m\( m\($/i
|
17
|
+
# picard + riker facepalm
|
18
|
+
room.say "https://img.skitch.com/20110224-ncacgpudhfr2s4te6nswenxaqt.jpg"
|
19
|
+
when /^i see your problem/i
|
20
|
+
# pony mechanic
|
21
|
+
room.say "https://img.skitch.com/20110224-8fmfwdmg6kkrcpijhamhqu7tm6.jpg"
|
22
|
+
when /^works on my machine|womm$/i
|
23
|
+
# works on my machine
|
24
|
+
room.say "https://img.skitch.com/20110224-jrcf6e4gc936a2mxc3mueah2in.png"
|
25
|
+
when /^stacktrace or gtfo|stacktrace or it didn't happen|stacktrace\!$/i
|
26
|
+
# stacktrace or gtfo
|
27
|
+
room.say "https://img.skitch.com/20110224-pqtmiici9wp9nygqi4nw8gs6hg.png"
|
28
|
+
when /^this is sparta\!*$/i
|
29
|
+
# this is sparta
|
30
|
+
room.say "https://img.skitch.com/20110225-k9xpadr2hk37pe5ed4crcqria1.png"
|
31
|
+
when /^i have no idea what i'm doing$/i
|
32
|
+
# I have no idea what I'm doing
|
33
|
+
room.say "https://img.skitch.com/20110304-1tcmatkhapiq6t51wqqq9igat5.jpg"
|
34
|
+
when /^party[?!.]*$/i
|
35
|
+
# party party party party party cat
|
36
|
+
room.say "https://img.skitch.com/20110309-qtd33sy8k5yrdaeqa9e119bwd1.jpg"
|
37
|
+
when /^bomb|system error$/i
|
38
|
+
# sorry, a system error has occurred
|
39
|
+
room.say "https://img.skitch.com/20110312-8g31a37spacdjgatr82g3g98j1.jpg"
|
40
|
+
when /^stop hitting yourself$/i
|
41
|
+
# and the angel said to him, Stop hitting yourself!
|
42
|
+
room.say "https://img.skitch.com/20110316-q7h49p69pjhhyy8756rha2a1jf.jpg"
|
43
|
+
when /^[:*(]php[:*)]$/
|
44
|
+
# PHP: training wheels without the bike
|
45
|
+
room.say "http://tnx.nl/php.jpg"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'daneel/script'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Daneel
|
5
|
+
module Scripts
|
6
|
+
class Reload < Daneel::Script
|
7
|
+
|
8
|
+
def receive(room, message, user)
|
9
|
+
case message.command
|
10
|
+
when /^update$/
|
11
|
+
return unless in_git?
|
12
|
+
system("cd #{root} && git pull origin master && bundle install")
|
13
|
+
room.say "updated, brb"
|
14
|
+
restart
|
15
|
+
when /^reload$/
|
16
|
+
room.say "k, brb"
|
17
|
+
restart
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def help
|
22
|
+
reload = {"reload" => "restarts and reloads #{robot.name}'s code"}
|
23
|
+
update = {"update" => "updates #{robot.name}'s code from git and restarts"}
|
24
|
+
in_git? ? reload.merge(update) : reload
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def restart
|
30
|
+
bin = root.join("bin/daneel").realpath.to_s
|
31
|
+
cmd = [Gem.ruby, "-S", bin, *ARGV]
|
32
|
+
|
33
|
+
logger.debug "Reloading: #{cmd.join(' ')}"
|
34
|
+
exec *cmd
|
35
|
+
end
|
36
|
+
|
37
|
+
def root
|
38
|
+
@root ||= begin
|
39
|
+
root = Pathname.new(__FILE__).join("../../../..").expand_path
|
40
|
+
logger.debug "Found root directory #{root}"
|
41
|
+
root
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def in_git?
|
46
|
+
@in_git ||= root.join(".git").directory?
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http/persistent'
|
4
|
+
|
5
|
+
module Daneel
|
6
|
+
module Scripts
|
7
|
+
class VineSearch < Daneel::Script
|
8
|
+
|
9
|
+
def initialize(robot)
|
10
|
+
super
|
11
|
+
@http = Net::HTTP::Persistent.new('daneel')
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive(room, message, user)
|
15
|
+
case message.command
|
16
|
+
when /vine me (.+)$/, /^(?:find) (?:me )?(?:a |another )?(?:vine of )(.*)$/
|
17
|
+
url = find_vine_url_for($1)
|
18
|
+
if url
|
19
|
+
room.say url
|
20
|
+
else
|
21
|
+
room.say "Sorry, Twitter didn't have any Vines about that."
|
22
|
+
end
|
23
|
+
message.done!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def help
|
28
|
+
{"find a vine of THING" => "shows you a vine that was tweeted mentioning THING"}
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_vine_url_for(search)
|
32
|
+
logger.debug "Searching for a vine of #{search}"
|
33
|
+
uri = URI("http://search.twitter.com/search.json")
|
34
|
+
query = CGI.escape("#{search} source:vine_for_ios")
|
35
|
+
uri.query = "rpp=5&include_entities=true&q=#{query}"
|
36
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
37
|
+
response = @http.request uri, request
|
38
|
+
logger.debug "GET #{uri}"
|
39
|
+
results = JSON.parse(response.body)["results"]
|
40
|
+
logger.debug "got back #{results.size} vines"
|
41
|
+
return nil if results.empty?
|
42
|
+
# Pull out the Vine display url of a random result
|
43
|
+
results.sample["entities"]["urls"].first["expanded_url"]
|
44
|
+
rescue => e
|
45
|
+
logger.error "#{e.class}: #{e.message}"
|
46
|
+
room.say "Sorry, something went wrong when I looked for '#{query}'"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/http/persistent'
|
2
|
+
require 'daneel'
|
3
|
+
|
4
|
+
module Daneel
|
5
|
+
class Server
|
6
|
+
attr_reader :logger
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
require 'daneel/web'
|
10
|
+
@logger = options[:logger] || Daneel::Logger.new
|
11
|
+
@options = {:app => Daneel::Web, :server => "puma"}.merge(options)
|
12
|
+
@options[:port] = ENV["PORT"] if ENV["PORT"]
|
13
|
+
# Rack expects the port key to be capitalized. Sad day.
|
14
|
+
@options[:Port] = @options.delete(:port) if @options[:port]
|
15
|
+
logger.debug "Server with options: #{@options}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
Thread.new { run_server }
|
20
|
+
sleep 0.1 # boot server before allowing possible interaction
|
21
|
+
Thread.new { run_self_ping }
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_server
|
25
|
+
Rack::Server.start(@options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_self_ping
|
29
|
+
return unless ENV['HEROKU_URL']
|
30
|
+
uri = URI(ENV['HEROKU_URL'])
|
31
|
+
http = Net::HTTP::Persistent.new 'daneel'
|
32
|
+
loop do
|
33
|
+
http.request uri
|
34
|
+
sleep (60 * 20) # 20m
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/lib/daneel/user.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module Daneel
|
3
|
+
# Represents a generic user. Doesn't have a room, because multiple rooms can
|
4
|
+
# contain the same user. The user's unique identifier is supplied by the
|
5
|
+
# adapter, and only needs to be unique within the context of that adapter.
|
6
|
+
class User
|
7
|
+
attr_reader :id, :name
|
8
|
+
attr_accessor :data, :initials, :short_name
|
9
|
+
|
10
|
+
def initialize(id, name, data = nil)
|
11
|
+
@id, @name, @data = id, name, data
|
12
|
+
|
13
|
+
# First, try to get initials from the upper-case letters
|
14
|
+
@initials = name.gsub(/\P{Upper}/,'')
|
15
|
+
# If that fails, just go with the first letter of each word
|
16
|
+
@initials = name.gsub(/(?<!^|\s)./,'') if @initials.empty?
|
17
|
+
# Short name is just the bit up to the first space
|
18
|
+
@short_name = name.match(/^(\S+)/)[0]
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
[short_name, short_name.downcase, initials].sample
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/daneel/version.rb
CHANGED
data/lib/daneel/web.rb
ADDED
metadata
CHANGED
@@ -1,104 +1,158 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: daneel
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 1
|
10
|
-
version: 0.0.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
13
|
-
-
|
7
|
+
authors:
|
8
|
+
- André Arko
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
date: 2013-01-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sparks
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.4'
|
22
|
+
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.4'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sinatra
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
25
33
|
none: false
|
26
|
-
requirements:
|
34
|
+
requirements:
|
27
35
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 1
|
32
|
-
- 2
|
33
|
-
- 0
|
34
|
-
version: 1.2.0
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.4.0
|
35
38
|
type: :runtime
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: activerecord
|
39
39
|
prerelease: false
|
40
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
|
-
requirements:
|
42
|
+
requirements:
|
43
43
|
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.4.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: puma
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.6.3
|
51
54
|
type: :runtime
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.6.3
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '10.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.2'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.2'
|
94
|
+
description: Daneel is a chatbot inspired by the late, lamented Wesabot. And also
|
95
|
+
Hubot.
|
96
|
+
email:
|
55
97
|
- andre@arko.net
|
56
|
-
executables:
|
57
|
-
|
98
|
+
executables:
|
99
|
+
- daneel
|
58
100
|
extensions: []
|
59
|
-
|
60
101
|
extra_rdoc_files: []
|
61
|
-
|
62
|
-
files:
|
102
|
+
files:
|
63
103
|
- .gitignore
|
64
104
|
- Gemfile
|
105
|
+
- Gemfile.lock
|
106
|
+
- Procfile
|
65
107
|
- Rakefile
|
108
|
+
- bin/daneel
|
66
109
|
- daneel.gemspec
|
67
110
|
- lib/daneel.rb
|
111
|
+
- lib/daneel/adapter.rb
|
112
|
+
- lib/daneel/adapters/campfire.rb
|
113
|
+
- lib/daneel/adapters/shell.rb
|
114
|
+
- lib/daneel/bot.rb
|
115
|
+
- lib/daneel/data.rb
|
116
|
+
- lib/daneel/logger.rb
|
117
|
+
- lib/daneel/message.rb
|
118
|
+
- lib/daneel/options.rb
|
119
|
+
- lib/daneel/plugin.rb
|
120
|
+
- lib/daneel/room.rb
|
121
|
+
- lib/daneel/script.rb
|
122
|
+
- lib/daneel/scripts/chatty.rb
|
123
|
+
- lib/daneel/scripts/echo.rb
|
124
|
+
- lib/daneel/scripts/help.rb
|
125
|
+
- lib/daneel/scripts/image_search.rb
|
126
|
+
- lib/daneel/scripts/meme.rb
|
127
|
+
- lib/daneel/scripts/reload.rb
|
128
|
+
- lib/daneel/scripts/vine_search.rb
|
129
|
+
- lib/daneel/server.rb
|
130
|
+
- lib/daneel/user.rb
|
68
131
|
- lib/daneel/version.rb
|
69
|
-
|
132
|
+
- lib/daneel/web.rb
|
70
133
|
homepage: http://github.com/indirect/daneel
|
71
|
-
licenses:
|
72
|
-
|
134
|
+
licenses:
|
135
|
+
- MIT
|
73
136
|
post_install_message:
|
74
137
|
rdoc_options: []
|
75
|
-
|
76
|
-
require_paths:
|
138
|
+
require_paths:
|
77
139
|
- lib
|
78
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
141
|
none: false
|
80
|
-
requirements:
|
81
|
-
- -
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
|
84
|
-
|
85
|
-
- 0
|
86
|
-
version: "0"
|
87
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
147
|
none: false
|
89
|
-
requirements:
|
90
|
-
- -
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
|
93
|
-
segments:
|
94
|
-
- 0
|
95
|
-
version: "0"
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
96
152
|
requirements: []
|
97
|
-
|
98
153
|
rubyforge_project:
|
99
|
-
rubygems_version: 1.
|
154
|
+
rubygems_version: 1.8.24
|
100
155
|
signing_key:
|
101
156
|
specification_version: 3
|
102
|
-
summary: A
|
157
|
+
summary: A 19,230-year-old robot
|
103
158
|
test_files: []
|
104
|
-
|