pandorified 0.9.3 → 0.9.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +12 -0
- data/Gemfile +2 -0
- data/README.md +1 -1
- data/Rakefile +3 -1
- data/lib/pandorified/result.rb +19 -11
- data/lib/pandorified/session.rb +34 -10
- data/lib/pandorified/version.rb +3 -1
- data/lib/pandorified.rb +18 -5
- data/pandorified.gemspec +16 -6
- data/spec/lib/pandorified_spec.rb +21 -15
- data/spec/spec_helper.rb +26 -17
- metadata +37 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d2a49f35f2909d4a603bbf2da9ecbf720362d88cf38082b0dfc034353a907ef
|
4
|
+
data.tar.gz: 0d82e5ac786ba52b5c6531ad6c624165ee89803fde05fd4155aca0aa06c8803b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e268ea3fd5473d26a5f186cd8cc4fe0d7d4d0f69d2f97b9c468f1f84438b6df3a6bf128016e696a22200ef80d46a8cde7e5cd6cf76d4d55eecd197d993a9b76e
|
7
|
+
data.tar.gz: 0dc01ce8fe8a948eaec3af2fac3541234a54c40c161b9f10f20795eea8a37e07112034119f87cc3cda59e5557d9b4631a4586bcf61f2a6f985efab70c7f3d713
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -53,7 +53,7 @@ Author
|
|
53
53
|
License
|
54
54
|
-------
|
55
55
|
|
56
|
-
Copyright © 2011-
|
56
|
+
Copyright © 2011-2021 Justin Workman
|
57
57
|
|
58
58
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
59
59
|
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rspec/core/rake_task'
|
3
5
|
|
@@ -7,5 +9,5 @@ task default: :spec
|
|
7
9
|
|
8
10
|
desc 'Open an irb session preloaded with this library'
|
9
11
|
task :console do
|
10
|
-
sh
|
12
|
+
sh 'irb -rubygems -I lib -r pandorified.rb'
|
11
13
|
end
|
data/lib/pandorified/result.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rest_client'
|
2
4
|
require 'nokogiri'
|
3
5
|
|
4
6
|
module Pandorified
|
5
7
|
API_URL = 'https://www.pandorabots.com/pandora/talk-xml'
|
6
8
|
|
9
|
+
# The result of sending a message to a bot, including the response message if
|
10
|
+
# successful.
|
7
11
|
class Result
|
8
12
|
def initialize(params)
|
9
13
|
@xml = Nokogiri::XML(RestClient.post(API_URL, params))
|
@@ -14,7 +18,7 @@ module Pandorified
|
|
14
18
|
@that ||= @xml.xpath('/result/that').first.text.strip
|
15
19
|
end
|
16
20
|
|
17
|
-
|
21
|
+
alias to_s that
|
18
22
|
|
19
23
|
# Check the status of this result. See {#success?} and {#error?}.
|
20
24
|
#
|
@@ -23,29 +27,33 @@ module Pandorified
|
|
23
27
|
@status ||= @xml.xpath('/result/@status').first.value.to_i
|
24
28
|
end
|
25
29
|
|
26
|
-
# @return `true` if this result was successful (no error was returned by
|
30
|
+
# @return `true` if this result was successful (no error was returned by
|
31
|
+
# Pandorabots), `false` otherwise.
|
27
32
|
def success?
|
28
|
-
|
33
|
+
status.zero?
|
29
34
|
end
|
30
35
|
|
31
|
-
|
32
|
-
|
36
|
+
alias ok? success?
|
37
|
+
alias successful? success?
|
33
38
|
|
34
|
-
# @note After checking if there is an error, you can read the error message
|
39
|
+
# @note After checking if there is an error, you can read the error message
|
40
|
+
# with {#message}.
|
35
41
|
#
|
36
42
|
# @return `true` if Pandorabots returned an error.
|
37
43
|
def error?
|
38
|
-
!
|
44
|
+
!success?
|
39
45
|
end
|
40
46
|
|
41
|
-
# @return [String] The error message as returned by Pandorabots, if an
|
47
|
+
# @return [String] The error message as returned by Pandorabots, if an
|
48
|
+
# error occured.
|
42
49
|
def message
|
43
|
-
return nil if
|
50
|
+
return nil if success?
|
51
|
+
|
44
52
|
@message ||= @xml.xpath('/result/message').first.text
|
45
53
|
end
|
46
54
|
|
47
|
-
|
48
|
-
|
55
|
+
alias error message
|
56
|
+
alias error_message message
|
49
57
|
|
50
58
|
# @return [String] The botid of the bot this result is for.
|
51
59
|
def botid
|
data/lib/pandorified/session.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pandorified/result'
|
2
4
|
|
3
5
|
module Pandorified
|
4
6
|
# Raised when Pandorabots returns an API result with a non-zero status.
|
5
7
|
class PandorabotsError < StandardError; end
|
6
8
|
|
9
|
+
# Represents a session (or conversation) for interacting with a bot.
|
7
10
|
class Session
|
8
11
|
# A new session for conversing with a bot.
|
9
12
|
#
|
10
|
-
# @note If you choose not to specify a {custid}, one will be automatically
|
13
|
+
# @note If you choose not to specify a {custid}, one will be automatically
|
14
|
+
# chosen and remembered throughout the session.
|
11
15
|
#
|
12
16
|
# @param [String] botid A valid Pandorabots botid.
|
13
|
-
# @param [String] custid An identifier used to keep track of this
|
17
|
+
# @param [String] custid An identifier used to keep track of this
|
18
|
+
# conversation.
|
14
19
|
def initialize(botid, custid = nil)
|
15
20
|
@botid = botid
|
16
21
|
@custid = custid
|
@@ -18,29 +23,48 @@ module Pandorified
|
|
18
23
|
|
19
24
|
# Send a message to this session's bot and receive a response.
|
20
25
|
#
|
21
|
-
# See {Pandorified::Result} for how to check for an error response and get
|
22
|
-
#
|
26
|
+
# See {Pandorified::Result} for how to check for an error response and get
|
27
|
+
# the error message.
|
28
|
+
#
|
29
|
+
# Alternatively, you can use {#talk!} instead of this method, which raises
|
30
|
+
# an exception when Pandorabots API returns an error.
|
23
31
|
#
|
24
32
|
# @param [String] input Text to say to the bot.
|
25
33
|
#
|
26
34
|
# @return [Pandorified::Result] The bot's response as a result object.
|
27
35
|
def talk(input)
|
28
|
-
result = Pandorified::Result.new(
|
36
|
+
result = Pandorified::Result.new(
|
37
|
+
botid: @botid,
|
38
|
+
custid: @custid,
|
39
|
+
input: input
|
40
|
+
)
|
41
|
+
|
29
42
|
@custid ||= result.custid if result.success?
|
43
|
+
|
30
44
|
result
|
31
45
|
end
|
32
46
|
|
33
|
-
# Send a message to this session's bot and receive a response (if
|
47
|
+
# Send a message to this session's bot and receive a response (if
|
48
|
+
# successful).
|
34
49
|
#
|
35
|
-
# If Pandorabots API responds with an error,
|
36
|
-
#
|
50
|
+
# If Pandorabots API responds with an error,
|
51
|
+
# {Pandorified::PandorabotsError} is raised with the specific error
|
52
|
+
# message.
|
53
|
+
#
|
54
|
+
# If you'd like to check for and handle the error yourself, you can use
|
55
|
+
# {#talk} instead of this method.
|
37
56
|
#
|
38
57
|
# @param [String] input Text to say to the bot.
|
39
58
|
#
|
40
59
|
# @return [String] The bot's response text.
|
41
60
|
def talk!(input)
|
42
|
-
result =
|
43
|
-
|
61
|
+
result = talk(input)
|
62
|
+
|
63
|
+
if result.error?
|
64
|
+
msg = "Pandorabots returned status #{result.status}: #{result.message}"
|
65
|
+
raise Pandorified::PandorabotsError, msg
|
66
|
+
end
|
67
|
+
|
44
68
|
result.that
|
45
69
|
end
|
46
70
|
end
|
data/lib/pandorified/version.rb
CHANGED
data/lib/pandorified.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pandorified/version'
|
2
4
|
require 'pandorified/result'
|
3
5
|
require 'pandorified/session'
|
4
6
|
|
7
|
+
# This is the top-level module for interacting with the Pandorabots API.
|
8
|
+
#
|
9
|
+
# There are some sugar methods which delegate to {Pandorified::Session} under
|
10
|
+
# the hood.
|
5
11
|
module Pandorified
|
6
12
|
# Send a message to a bot and receive a response.
|
7
13
|
#
|
8
|
-
# See {Pandorified::Result} for ways to handle the response, or use {.talk!}
|
9
|
-
#
|
14
|
+
# See {Pandorified::Result} for ways to handle the response, or use {.talk!}
|
15
|
+
# which raises an exception on errors.
|
16
|
+
#
|
17
|
+
# If you want to remember the {botid} and {custid} between multiple calls,
|
18
|
+
# you should use {Pandorabots::Session} instead of this method.
|
10
19
|
#
|
11
20
|
# @param [String] input Text to say to the bot.
|
12
21
|
# @param [String] botid A valid Pandorabots botid.
|
@@ -19,10 +28,14 @@ module Pandorified
|
|
19
28
|
|
20
29
|
# Send a message to a bot and receive a response (if successsful).
|
21
30
|
#
|
22
|
-
# If Pandorabots responds with an error, {Pandorified::PandorabotsError} is
|
23
|
-
#
|
31
|
+
# If Pandorabots responds with an error, {Pandorified::PandorabotsError} is
|
32
|
+
# raised.
|
33
|
+
#
|
34
|
+
# If you'd rather check for and handle the error yourself, use {.talk}
|
35
|
+
# instead of this method.
|
24
36
|
#
|
25
|
-
# If you want to remember the {botid} and {custid} between multiple calls,
|
37
|
+
# If you want to remember the {botid} and {custid} between multiple calls,
|
38
|
+
# you should use {Pandorabots::Session} instead of this method.
|
26
39
|
#
|
27
40
|
# @param [String] input Text to say to the bot.
|
28
41
|
# @param [String] botid A valid Pandorabots botid.
|
data/pandorified.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
4
|
|
3
5
|
require 'pandorified/version'
|
4
6
|
|
@@ -8,16 +10,24 @@ Gem::Specification.new do |spec|
|
|
8
10
|
spec.authors = 'Justin Workman'
|
9
11
|
spec.email = 'xtagon@gmail.com'
|
10
12
|
spec.summary = 'A Ruby client for the Pandorabots API.'
|
11
|
-
spec.description = 'Pandorified makes it easy for your Ruby scripts to interact with chat bots hosted on Pandorabots.'
|
12
13
|
|
13
|
-
spec.
|
14
|
-
|
15
|
-
|
14
|
+
spec.description = <<~DOC.chomp
|
15
|
+
Pandorified makes it easy for your Ruby scripts to interact with chat bots hosted on Pandorabots.
|
16
|
+
DOC
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split("\n")
|
19
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
|
21
|
+
spec.executables =
|
22
|
+
`git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
23
|
+
|
16
24
|
spec.require_paths = ['lib']
|
17
25
|
|
26
|
+
spec.add_runtime_dependency 'nokogiri', '>= 1.12.5'
|
18
27
|
spec.add_runtime_dependency 'rest-client', '>= 1.7.3'
|
19
|
-
spec.add_runtime_dependency 'nokogiri', '>= 1.10.8'
|
20
28
|
|
21
29
|
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'rubocop'
|
22
31
|
spec.add_development_dependency 'webmock'
|
32
|
+
spec.add_development_dependency 'yard'
|
23
33
|
end
|
@@ -1,44 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
|
-
|
4
|
-
describe
|
5
|
-
let(:input) {
|
6
|
-
let(:botid) {
|
5
|
+
describe Pandorified do
|
6
|
+
describe '#talk!' do
|
7
|
+
let(:input) { 'Are you a robot?' }
|
8
|
+
let(:botid) { 'np218q9s7r346nqo' }
|
7
9
|
let(:custid) { nil }
|
8
10
|
|
9
11
|
let(:request_body) do
|
10
12
|
{
|
11
|
-
|
12
|
-
|
13
|
-
|
13
|
+
'botid' => botid,
|
14
|
+
'custid' => nil,
|
15
|
+
'input' => input
|
14
16
|
}
|
15
17
|
end
|
16
18
|
|
17
|
-
context
|
19
|
+
context 'when successful' do
|
18
20
|
subject { described_class.talk!(input, botid, custid) }
|
19
21
|
|
20
|
-
let(:that) {
|
22
|
+
let(:that) { 'Of course not!' }
|
21
23
|
|
22
24
|
let(:response_body) do
|
23
25
|
<<~XML
|
24
|
-
|
26
|
+
<result status="0" botid="#{botid}" custid="fec7cfc40e5b751a"><input>#{input}</input><that>#{that}</that></result>"
|
25
27
|
XML
|
26
28
|
end
|
27
29
|
|
28
30
|
let(:response_headers) do
|
29
|
-
{content_type:
|
31
|
+
{ content_type: 'text/xml' }
|
30
32
|
end
|
31
33
|
|
32
34
|
before :each do
|
33
|
-
stub_request(:post,
|
34
|
-
with(body: request_body)
|
35
|
-
to_return(
|
35
|
+
stub_request(:post, 'https://www.pandorabots.com/pandora/talk-xml')
|
36
|
+
.with(body: request_body)
|
37
|
+
.to_return(
|
38
|
+
status: 200,
|
39
|
+
body: response_body,
|
40
|
+
headers: response_headers
|
41
|
+
)
|
36
42
|
end
|
37
43
|
|
38
44
|
it { is_expected.to eq(that) }
|
39
45
|
end
|
40
46
|
|
41
|
-
context
|
47
|
+
context 'when unsuccessful' do
|
42
48
|
pending { is_expected.to raise_error(described_class::PandorabotsError) }
|
43
49
|
end
|
44
50
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'pandorified'
|
2
4
|
|
3
5
|
require 'webmock/rspec'
|
@@ -48,57 +50,64 @@ RSpec.configure do |config|
|
|
48
50
|
# triggering implicit auto-inclusion in groups with matching metadata.
|
49
51
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
50
52
|
|
51
|
-
# The settings below are suggested to provide a good initial experience
|
52
|
-
# with RSpec, but feel free to customize to your heart's content.
|
53
|
-
|
53
|
+
# The settings below are suggested to provide a good initial experience
|
54
|
+
# with RSpec, but feel free to customize to your heart's content.
|
55
|
+
|
54
56
|
# This allows you to limit a spec run to individual examples or groups
|
55
57
|
# you care about by tagging them with `:focus` metadata. When nothing
|
56
58
|
# is tagged with `:focus`, all examples get run. RSpec also provides
|
57
59
|
# aliases for `it`, `describe`, and `context` that include `:focus`
|
58
60
|
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
59
|
-
|
61
|
+
#
|
62
|
+
# config.filter_run_when_matching :focus
|
60
63
|
|
61
64
|
# Allows RSpec to persist some state between runs in order to support
|
62
65
|
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
63
66
|
# you configure your source control system to ignore this file.
|
64
|
-
|
67
|
+
#
|
68
|
+
# config.example_status_persistence_file_path = "spec/examples.txt"
|
65
69
|
|
66
70
|
# Limits the available syntax to the non-monkey patched syntax that is
|
67
71
|
# recommended. For more details, see:
|
68
72
|
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
69
73
|
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
70
74
|
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
71
|
-
|
75
|
+
#
|
76
|
+
# config.disable_monkey_patching!
|
72
77
|
|
73
78
|
# This setting enables warnings. It's recommended, but in some cases may
|
74
79
|
# be too noisy due to issues in dependencies.
|
75
|
-
|
80
|
+
#
|
81
|
+
# config.warnings = true
|
76
82
|
|
77
83
|
# Many RSpec users commonly either run the entire suite or an individual
|
78
84
|
# file, and it's useful to allow more verbose output when running an
|
79
85
|
# individual spec file.
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
+
#
|
87
|
+
# if config.files_to_run.one?
|
88
|
+
# # Use the documentation formatter for detailed output,
|
89
|
+
# # unless a formatter has already been configured
|
90
|
+
# # (e.g. via a command-line flag).
|
91
|
+
# config.default_formatter = "doc"
|
92
|
+
# end
|
86
93
|
|
87
94
|
# Print the 10 slowest examples and example groups at the
|
88
95
|
# end of the spec run, to help surface which specs are running
|
89
96
|
# particularly slow.
|
90
|
-
|
97
|
+
#
|
98
|
+
# config.profile_examples = 10
|
91
99
|
|
92
100
|
# Run specs in random order to surface order dependencies. If you find an
|
93
101
|
# order dependency and want to debug it, you can fix the order by providing
|
94
102
|
# the seed, which is printed after each run.
|
95
103
|
# --seed 1234
|
96
|
-
|
104
|
+
#
|
105
|
+
# config.order = :random
|
97
106
|
|
98
107
|
# Seed global randomization in this process using the `--seed` CLI option.
|
99
108
|
# Setting this allows you to use `--seed` to deterministically reproduce
|
100
109
|
# test failures related to randomization by passing the same `--seed` value
|
101
110
|
# as the one that triggered the failure.
|
102
|
-
|
103
|
-
|
111
|
+
#
|
112
|
+
# Kernel.srand config.seed
|
104
113
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pandorified
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Workman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.12.5
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.12.5
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rest-client
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -25,21 +39,21 @@ dependencies:
|
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 1.7.3
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rubocop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
description: Pandorified makes it easy for your Ruby scripts to interact with chat
|
70
98
|
bots hosted on Pandorabots.
|
71
99
|
email: xtagon@gmail.com
|
@@ -75,6 +103,7 @@ extra_rdoc_files: []
|
|
75
103
|
files:
|
76
104
|
- ".gitignore"
|
77
105
|
- ".rspec"
|
106
|
+
- ".rubocop.yml"
|
78
107
|
- Gemfile
|
79
108
|
- README.md
|
80
109
|
- Rakefile
|
@@ -103,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
132
|
- !ruby/object:Gem::Version
|
104
133
|
version: '0'
|
105
134
|
requirements: []
|
106
|
-
rubygems_version: 3.
|
135
|
+
rubygems_version: 3.0.3
|
107
136
|
signing_key:
|
108
137
|
specification_version: 4
|
109
138
|
summary: A Ruby client for the Pandorabots API.
|