numbr5 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'numbr5'
3
+
4
+ EventMachine.run do
5
+ EventMachine.connect 'irc.freenode.net', 6667, Numbr5::Bot, *ARGV
6
+ end
@@ -0,0 +1 @@
1
+ default: features
@@ -0,0 +1,9 @@
1
+ require 'eventmachine'
2
+ require 'rest_client'
3
+ require 'json'
4
+
5
+ require 'numbr5/bot'
6
+
7
+ module Numbr5
8
+ #
9
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe Numbr5 do
4
+ #
5
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'numbr5'
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+
8
+ Spec::Runner.configure do |config|
9
+ #
10
+ end
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