numbr5 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.textile +33 -0
- data/VERSION +1 -0
- data/bin/numbr5 +6 -0
- data/config/cucumber.yml +1 -0
- data/lib/numbr5.rb +9 -0
- data/lib/numbr5/bot.rb +90 -0
- data/spec/numbr5/bot_spec.rb +136 -0
- data/spec/numbr5_spec.rb +5 -0
- data/spec/spec_helper.rb +10 -0
- metadata +113 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Pat Allan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
h1. Number 5
|
2
|
+
|
3
|
+
A wary IRC bot, tracking people thanking other people, and tallying those thanks as beers owed.
|
4
|
+
|
5
|
+
h2. Installation
|
6
|
+
|
7
|
+
<pre><code>gem install numbr5</code></pre>
|
8
|
+
|
9
|
+
h2. Usage
|
10
|
+
|
11
|
+
Specify a channel name without the hash suffix.
|
12
|
+
|
13
|
+
<pre><code>numbr5 railscamp</code></pre>
|
14
|
+
|
15
|
+
In the channel, if you thank someone (using emotes/actions) for something, then you have a beer logged from you to that person.
|
16
|
+
|
17
|
+
<pre><code>/me thanks ben_a for running rails camps</code></pre>
|
18
|
+
|
19
|
+
Currently, the order must be "/me thanks [username] [reason]". If you can figure out a way to make this flexible and smart, please fork.
|
20
|
+
|
21
|
+
You'll also need the website running as well, otherwise there'll be no API around to use.
|
22
|
+
|
23
|
+
h2. Limitations
|
24
|
+
|
25
|
+
Currently hard-coded to irc.freenode.net on port 6667, and only accepts one channel. I'd like it to support multiple channels, and specify the server, so multiple bots can be running for different servers.
|
26
|
+
|
27
|
+
It's also expecting the corresponding website to be hosted locally and running on port 3000. This is far from ideal - a patch to make the bot understand the concepts of environments would be fantastic.
|
28
|
+
|
29
|
+
What also would be cool is the concept of a source for each thank-you - ie: IRC/channel. This way, the service could be extended beyond just IRC (although there's already a few Twitter-equivalents out there).
|
30
|
+
|
31
|
+
h2. Copyright
|
32
|
+
|
33
|
+
Copyright (c) 2009 Pat Allan, and released under an MIT License. Heavily inspired by "Lachie Cox's":http://smartbomb.com.au/ "numbr5 bot":http://github.com/lachie/numbr5 for the #roro channel on irc.freenode.net. This new version was initially built at "Rails Camp UK 2":http://railscamps.com, October 2009.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/numbr5
ADDED
data/config/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: features
|
data/lib/numbr5.rb
ADDED
data/lib/numbr5/bot.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Numbr5
|
2
|
+
class Bot < EventMachine::Connection
|
3
|
+
include EventMachine::Protocols::LineText2
|
4
|
+
|
5
|
+
attr_reader :channel
|
6
|
+
|
7
|
+
def initialize(channel)
|
8
|
+
@channel = channel
|
9
|
+
end
|
10
|
+
|
11
|
+
def receive_data(data)
|
12
|
+
case data
|
13
|
+
when /no ident/i
|
14
|
+
identify_and_join
|
15
|
+
when /^PING /
|
16
|
+
pong data.gsub(/^PING /i, '')
|
17
|
+
when / PRIVMSG numbr5 /
|
18
|
+
match = data.match(/:([^!]+).+ PRIVMSG numbr5 :(.+)/)
|
19
|
+
private_message match[1], match[2]
|
20
|
+
when / PRIVMSG ##{channel}/
|
21
|
+
match = data.match(/:([^!]+).+ PRIVMSG ##{channel} :(.+)$/)
|
22
|
+
public_message match[1], match[2]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def send_data(data)
|
29
|
+
puts "SENDING #{data}"
|
30
|
+
super(data + "\r\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
def identify_and_join
|
34
|
+
send_data "NICK numbr5"
|
35
|
+
send_data "USER numbr5 0 * :Number 5"
|
36
|
+
send_data "JOIN ##{channel}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def pong(data = 'irc')
|
40
|
+
send_data "PONG #{data}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def private_message(user, message)
|
44
|
+
case message.strip
|
45
|
+
when 'stats'
|
46
|
+
stats user
|
47
|
+
else
|
48
|
+
send_data "PRIVMSG #{user} :hello"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def public_message(user, message)
|
53
|
+
if message[/^.ACTION/]
|
54
|
+
match = message.match(/^.ACTION (\w+)($| .+$)/)
|
55
|
+
action user, match[1], match[2].strip
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def action(user, action, message)
|
60
|
+
case action
|
61
|
+
when 'thanks'
|
62
|
+
match = message.match(/^(\w+) (.+)$/)
|
63
|
+
thank user, match[1], match[2]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def thank(from, to, reason)
|
68
|
+
if from == to
|
69
|
+
send_data "PRIVMSG ##{channel} :#{from}: no masturbation!"
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
RestClient.post 'http://localhost:3000/api/beers.json', :beer => {
|
74
|
+
:from => from,
|
75
|
+
:to => to,
|
76
|
+
:reason => reason
|
77
|
+
}
|
78
|
+
send_data "PRIVMSG ##{channel} :#{from}: you owe #{to} a beer #{reason}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def stats(user)
|
82
|
+
json = RestClient.get "http://localhost:3000/api/users/#{user}.json"
|
83
|
+
json = JSON.parse json
|
84
|
+
|
85
|
+
send_data "PRIVMSG #{user} :You owe #{json['user']['beers_owing']} beers and are owed #{json['user']['beers_owed']} beers"
|
86
|
+
rescue RestClient::ResourceNotFound
|
87
|
+
send_data "PRIVMSG #{user} :You owe 0 beers and are owed 0 beers"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Numbr5::Bot do
|
4
|
+
before :each do
|
5
|
+
@bot = Numbr5::Bot.new(:event_machine_placeholder, 'spec')
|
6
|
+
@bot.stub!(:send_data => true)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#receive_data' do
|
10
|
+
context 'No Ident response' do
|
11
|
+
it "should send the user nickname" do
|
12
|
+
@bot.should_receive(:send_data).with("NICK numbr5")
|
13
|
+
|
14
|
+
@bot.receive_data '*** No Ident response'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should send the user details" do
|
18
|
+
@bot.should_receive(:send_data).with("USER numbr5 0 * :Number 5")
|
19
|
+
|
20
|
+
@bot.receive_data '*** No Ident response'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should join the given channel" do
|
24
|
+
@bot.should_receive(:send_data).with("JOIN #spec")
|
25
|
+
|
26
|
+
@bot.receive_data '*** No Ident response'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'PING' do
|
31
|
+
it "should respond with a pong" do
|
32
|
+
@bot.should_receive(:send_data) do |data|
|
33
|
+
data.should match(/^PONG /)
|
34
|
+
end
|
35
|
+
|
36
|
+
@bot.receive_data 'PING :default'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should direct the pong at a server" do
|
40
|
+
@bot.should_receive(:send_data) do |data|
|
41
|
+
data.should match(/PONG :default/)
|
42
|
+
end
|
43
|
+
|
44
|
+
@bot.receive_data 'PING :default'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should respect the given server name" do
|
48
|
+
@bot.should_receive(:send_data) do |data|
|
49
|
+
data.should match(/PONG :spec/)
|
50
|
+
end
|
51
|
+
|
52
|
+
@bot.receive_data 'PING :spec'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'PRIVMSG numbr5' do
|
57
|
+
it "should respond with a private message" do
|
58
|
+
@bot.should_receive(:send_data) do |data|
|
59
|
+
data.should match(/^PRIVMSG/)
|
60
|
+
end
|
61
|
+
|
62
|
+
@bot.receive_data ':pat!~pat@freelancing-god PRIVMSG numbr5 :hello?'
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should direct the message to the sender" do
|
66
|
+
@bot.should_receive(:send_data) do |data|
|
67
|
+
data.should match(/^PRIVMSG pat/)
|
68
|
+
end
|
69
|
+
|
70
|
+
@bot.receive_data ':pat!~pat@freelancing-god PRIVMSG numbr5 :hello?'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should provide beer stats if asked for" do
|
74
|
+
RestClient.stub!(
|
75
|
+
:get => '{"user":{"name":"pat","beers_owing":2,"beers_owed":0}}'
|
76
|
+
)
|
77
|
+
@bot.should_receive(:send_data).with('PRIVMSG pat :You owe 2 beers and are owed 0 beers')
|
78
|
+
|
79
|
+
@bot.receive_data ':pat!~pat@freelancing-god PRIVMSG numbr5 :stats'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should handle 404 errors if the user doesn't have any stats" do
|
83
|
+
RestClient.stub(:get).and_raise(RestClient::ResourceNotFound)
|
84
|
+
|
85
|
+
@bot.should_receive(:send_data).with('PRIVMSG pat :You owe 0 beers and are owed 0 beers')
|
86
|
+
|
87
|
+
@bot.receive_data ':pat!~pat@freelancing-god PRIVMSG numbr5 :stats'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'PRIVMSG #channel' do
|
92
|
+
before :each do
|
93
|
+
RestClient.stub!(:post => true)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should not do anything if there was no action" do
|
97
|
+
@bot.should_not_receive(:send_data)
|
98
|
+
|
99
|
+
@bot.receive_data ':pat!~pat@freelancing-god PRIVMSG #spec :cough'
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should ignore actions that aren't thanks" do
|
103
|
+
@bot.should_not_receive(:send_data)
|
104
|
+
|
105
|
+
@bot.receive_data ":pat!~pat@freelancing-god PRIVMSG #spec :\001ACTION wave"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should confirm thanks" do
|
109
|
+
@bot.should_receive(:send_data).
|
110
|
+
with("PRIVMSG #spec :pat: you owe user a beer for action")
|
111
|
+
@bot.receive_data ":pat!~pat@freelancing-god PRIVMSG #spec :\001ACTION thanks user for action"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should create the beer via the API" do
|
115
|
+
RestClient.should_receive(:post) do |url, payload|
|
116
|
+
url.should == 'http://localhost:3000/api/beers.json'
|
117
|
+
payload.should == {
|
118
|
+
:beer => {
|
119
|
+
:from => 'pat',
|
120
|
+
:to => 'user',
|
121
|
+
:reason => 'for action'
|
122
|
+
}
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
@bot.receive_data ":pat!~pat@freelancing-god PRIVMSG #spec :\001ACTION thanks user for action"
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should send a warning if the from and to users are different" do
|
130
|
+
@bot.should_receive(:send_data).
|
131
|
+
with("PRIVMSG #spec :pat: no masturbation!")
|
132
|
+
@bot.receive_data ":pat!~pat@freelancing-god PRIVMSG #spec :\001ACTION thanks pat for action"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/spec/numbr5_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: numbr5
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pat Allan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-06 00:00:00 +11:00
|
13
|
+
default_executable: numbr5
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.12.8
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rest-client
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.3
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.1.9
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: rspec
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
type: :development
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
description: A multi-server bot that tracks thanks between accounts
|
66
|
+
email: pat@freelancing-gods.com
|
67
|
+
executables:
|
68
|
+
- numbr5
|
69
|
+
extensions: []
|
70
|
+
|
71
|
+
extra_rdoc_files:
|
72
|
+
- LICENSE
|
73
|
+
- README.textile
|
74
|
+
files:
|
75
|
+
- LICENSE
|
76
|
+
- README.textile
|
77
|
+
- VERSION
|
78
|
+
- bin/numbr5
|
79
|
+
- lib/numbr5.rb
|
80
|
+
- lib/numbr5/bot.rb
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/freelancing-god/numbr5
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- --charset=UTF-8
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.3.5
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: "Number 5: Universal Thanking Bot"
|
109
|
+
test_files:
|
110
|
+
- config/cucumber.yml
|
111
|
+
- spec/numbr5/bot_spec.rb
|
112
|
+
- spec/numbr5_spec.rb
|
113
|
+
- spec/spec_helper.rb
|