newton 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Dominik Honnef <dominikh@fork-bomb.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # Newton – Adding new forces to your bots
2
+
3
+ Newton started as a fork of
4
+ [Isaac](http://github.com/ichverstehe/isaac). Nowadays, however, they
5
+ only share 1% of code and basic DSL ideas and are not compatible to each other.
6
+
7
+ ## Features/characteristics
8
+ ### yard documentation
9
+
10
+ Newton uses yard to document all available methods, providing detailed
11
+ information on expected parameters, return values and raised
12
+ exceptions. Where appropriate, examples are provided.
13
+
14
+ ### Extensive test suite using baretest
15
+ Current status: planned
16
+
17
+ Newton will use baretest to test all aspects of Newton. Right now we
18
+ are waiting for the 0.5 release of baretest before we start writing
19
+ any tests, as the 0.5 release will bring major changes.
20
+
21
+ ### Object-oriented interface
22
+
23
+ One aspect of nearly all IRC bot frameworks written in Ruby is that
24
+ they don't make use of OO but instead rely on a rather functional
25
+ approach. This might make sense for **really** simple tasks, but as
26
+ soon as you want to deal with more information or implement anything
27
+ extensive, it gets hairy.
28
+
29
+ Instead of passing around strings and having methods like
30
+ `Bot#kick(channel, user, reason)`, Newton provides proper abstraction
31
+ and object-orientation like `Channel#kick(user, reason)`.
32
+
33
+ And whenever we do have to deal with strings (e.g. when matching
34
+ commands), Newton provides `Channel(channel)` and `User(user)` helper
35
+ methods.
36
+
37
+ ### Key/value store
38
+ Current status: implemented
39
+
40
+ Newton provides a basic key/value store (a Hash) to store information
41
+ across different handler invocations:
42
+
43
+ configure do |c|
44
+
45
+ store[:messages] = []
46
+ end
47
+
48
+ on :channel, /^store this: (.+)$/ do |message|
49
+ store[:messages] << message
50
+ end
51
+
52
+ ### Threading
53
+
54
+ Unlike most other IRC bot frameworks, Newton executes handlers each in
55
+ its own thread. This prevents bots from locking up on long operations,
56
+ but also means that bots have to be written with thread-safety in
57
+ mind.
58
+
59
+ #### synchronize(key) do .. end
60
+
61
+ In order to avoid race conditions, Newton provides an easy way of
62
+ synchronizing pieces of code using `synchronize`. Each call to
63
+ synchronize expects a name and a block.
64
+
65
+ configure do |c|
66
+
67
+ store[:i] = 0
68
+ end
69
+
70
+ on :channel, /^start counting!/ do
71
+ synchronize(:my_counter) do
72
+ 10.times do
73
+ val = store[:i]
74
+ # at this point, another thread might've incremented :i already.
75
+ # this thread wouldn't know about it, though.
76
+ store[:i] = val + 1
77
+ end
78
+ end
79
+
80
+ channel.send store[:i].to_s
81
+ end
82
+
83
+ On any message that equals "start counting!", our bot will increment
84
+ the value in `store[:i]` by 10, one for one.
85
+
86
+ If we did not use `synchronize`, the value might not be higher by ten
87
+ at the end of the loop because of known possible race conditions, at
88
+ least in certain implementations of Ruby.
89
+
90
+ ### Constants
91
+
92
+ Nobody is able to remember all IRC numeric replies (e.g. 401 for "no
93
+ such nick"). Unfortunately, some frameworks, like for example Isaac,
94
+ require you to uses those codes when hooking on errors (`on :error,
95
+ 401 do`). Those codes, however, also all have a name, e.g.
96
+ ERR_NOSUCHNICK instead of 401 – and Newton provides all of those names
97
+ as constants.
98
+
99
+ ### Known signals
100
+ :ctcp
101
+ : for CTCP requests
102
+ :ping
103
+ : Whenever the **server** pings us. Do not confuse this with CTCP PING
104
+ :message
105
+ : Applies to both channel and private messages
106
+ :channel
107
+ : On channel messages
108
+ :private
109
+ : On private messages (also refered to as "queries")
110
+ :error
111
+ : On errors returned by the server
112
+ :disconnect
113
+ : gets called when the connection to the server is being interrupted
114
+
115
+ ### Colorized log output
116
+
117
+ Ever had to debug a bot and couldn't be arsed reading a wall of
118
+ unformatted log output of IRC commands? Help is on its way! Newton
119
+ will by default output colorized logs if printing to a terminal –
120
+ output redirected to a file or pipe will remain without color codes.
121
+
122
+ ### Customg logging
123
+
124
+ If you want to print debug statements, you can use
125
+ `FormattedLogger.debug(message)`.
126
+
127
+ ### Just a note on fork()
128
+
129
+ Because of how Newton internally works, reusing a bot in a forked Ruby
130
+ process will yield unexpected results.
131
+
132
+ ### Flood control
133
+
134
+ Newton tries to optimize the use of the local send queue as well as
135
+ the receive queue of the IRC server. This means that Newton will send
136
+ as many messages as possible without any delay, while at the same time
137
+ making sure that we won't be kicked for excess flood. Newton does this
138
+ by calculating the amount of messages the server is likely to have
139
+ processed and comparing that value to the maximum allowed number of
140
+ messages in the receive queue.
141
+
142
+ #### Planned feature
143
+ Note: the following is just an idea.
144
+
145
+ Furthermore, Newton sorts the send queue to make sure that important
146
+ messages (PING replies, mostly) are sent before any other messages.
147
+ Additionally, if the queue contains messages targeted at different
148
+ receivers (say, different channels), they will be sorted in a fair
149
+ way.
150
+
151
+ Example:
152
+ - we got 4 channels, "A", "B", "C" and "D"
153
+ - we send 2 messages to each channel. first 2 to "A", then 2 to "B" and so on
154
+
155
+ A naive queue would send the messages in this order:
156
+ A, A, B, B, C, C, D, D
157
+
158
+ Newton's queue, however, will order them in this way:
159
+ A, B, C, D, A, B, C, D
160
+
161
+ This will indeed mean a higher perceived delay for each individual
162
+ channel. It will, however, make sure that all channels will be treated
163
+ fair.
164
+
165
+ ### ISUPPORT
166
+
167
+ Newton parses and understands
168
+ [ISUPPORT](http://www.irc.org/tech_docs/005.html) and thus provides
169
+ adequate error handling, mode parsing and checks.
170
+
171
+ #### Strictness
172
+
173
+ Newton can operate in two different modes: _strict_ and _forgiving_.
174
+ Those can be set using the _strictness_ option.
175
+
176
+ ##### strict
177
+
178
+ When invoking unsupported IRC commands (e.g. KNOCK) or disobeying
179
+ limits like the max length of topics, Newton will raise an exception.
180
+
181
+ ##### forgiving
182
+
183
+ Unsupported IRC commands will still be sent to the server and length
184
+ restrictions will just be ignored (in most cases, the server will
185
+ truncate overlong information)
186
+
187
+ ## Examples
188
+
189
+ See _examples/_
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ task :default => :test
2
+
3
+ task :test do
4
+ require 'rake/runtest'
5
+ Rake.run_tests
6
+ end
@@ -0,0 +1,45 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'newton'
3
+
4
+ # Give this bot ops in a channel and it'll auto voice
5
+ # visitors
6
+ #
7
+ # Enable with !autovoice on
8
+ # Disable with !autovoice off
9
+
10
+ bot = Newton::Bot.new do
11
+ configure do |c|
12
+ c.nick = "newton_autovoice"
13
+ c.server = "irc.freenode.net"
14
+ c.port = 6667
15
+ c.verbose = true
16
+
17
+ store[:autovoice] = true
18
+ end
19
+
20
+ on :connect do
21
+ join "#dominikh-tests"
22
+ end
23
+
24
+ on :join do
25
+ unless user == bot # don't try to voice ourself
26
+ channel.voice(user) if store[:autovoice]
27
+ end
28
+ end
29
+
30
+ on :message, "!autovoice on" do
31
+ if channel.opped?(user)
32
+ store[:autovoice] = true
33
+ reply "Autovoicing is now enabled"
34
+ end
35
+ end
36
+
37
+ on :message, "!autovoice off" do
38
+ if channel.opped?(user)
39
+ store[:autovoice] = false
40
+ reply "Autovoicing is now disabled"
41
+ end
42
+ end
43
+ end
44
+
45
+ bot.start
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift '../lib'
2
+
3
+ require 'newton'
4
+
5
+ bot = Newton::Bot.new do
6
+ configure do |c|
7
+ c.nick = "echo_bot"
8
+ c.server = "irc.freenode.net"
9
+ c.port = 6667
10
+ c.verbose = true
11
+ end
12
+
13
+ on :connect do
14
+ join "#dominikh-tests"
15
+ end
16
+
17
+ on :message do
18
+ reply message
19
+ end
20
+ end
21
+
22
+ bot.start
@@ -0,0 +1,23 @@
1
+ $LOAD_PATH.unshift '../lib'
2
+ require 'newton'
3
+
4
+ bot = Newton::Bot.new do
5
+ configure do |c|
6
+ c.nick = "The_Echo_Bot"
7
+ c.server = "irc.freenode.net"
8
+ c.port = 6667
9
+ c.verbose = true
10
+ end
11
+
12
+ on :connect do
13
+ join "#dominikh-tests"
14
+ end
15
+
16
+ on :channel, /flood/ do
17
+ 20.times do |i|
18
+ reply "#{i}:: Let me take you down to the city for a while, just a little while, oh yes. This should never exceed, plz thx u"
19
+ end
20
+ end
21
+ end
22
+
23
+ bot.start
data/examples/memo.rb ADDED
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'newton'
3
+
4
+ class Memo < Struct.new(:user, :channel, :text, :time)
5
+ def to_s
6
+ "[%s] <%s/%s> %s" % [time.asctime, channel.name, user.nick, text]
7
+ end
8
+ end
9
+
10
+ bot = Newton::Bot.new do
11
+ configure do |c|
12
+ c.nick = "newton_memo"
13
+ c.server = "irc.freenode.net"
14
+ c.port = 6667
15
+ c.verbose = true
16
+
17
+ store[:memos] = Hash.new {|h,k| h[k] = []}
18
+ end
19
+
20
+ on :connect do
21
+ join "#dominikh-tests"
22
+ end
23
+
24
+ on :message do
25
+ unless store[:memos][user].empty?
26
+ while memo = store[:memos][user].shift
27
+ user.send memo
28
+ end
29
+ end
30
+ end
31
+
32
+ on :message, /^test (.+) (.+)$/ do |a, b|
33
+ p [a, b]
34
+ end
35
+
36
+ on :message, /^!memo (\S+) (.+)$/ do |target, text|
37
+ p [target, text]
38
+ target = User(target)
39
+ if user == target
40
+ reply "You cannot leave memos for yourself.", true
41
+ elsif target == bot
42
+ reply "You cannot leave memos for me.", true
43
+ else
44
+ store[:memos][target] << Memo.new(user, channel, text, Time.now)
45
+ reply "Memo for #{target} recorded.", true
46
+ end
47
+ end
48
+ end
49
+
50
+ bot.start
@@ -0,0 +1,41 @@
1
+ $LOAD_PATH.unshift '../lib'
2
+ require 'newton'
3
+
4
+ bot = Newton::Bot.new do
5
+ configure do |c|
6
+ c.nick = "SomeBot"
7
+ c.server = "irc.freenode.net"
8
+ c.port = 6667
9
+ c.realname = 'Isaac Hayes'
10
+ c.verbose = true
11
+ end
12
+
13
+ helpers do
14
+ def check(channel)
15
+ channel.send "this channel, #{channel}, is awesome!"
16
+ end
17
+ end
18
+
19
+ on :connect do
20
+ join "#dominikh-tests"
21
+ User("asdfhaskfdhaskdfhaskdfasdf").send "foo"
22
+ end
23
+
24
+ on :private, /^t (.*)/ do |text|
25
+ reply "You said: " + text
26
+ end
27
+
28
+ on :channel, /quote/ do
29
+ reply "#{user} requested a quote: 'Smoking, a subtle form of suicide.' - Vonnegut"
30
+ end
31
+
32
+ on :channel, /status/ do
33
+ check(channel)
34
+ end
35
+
36
+ on :error, ERR_NOSUCHNICK do
37
+ debug "Ok, #{params[1]} doesn't exist."
38
+ end
39
+ end
40
+
41
+ bot.start
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'newton'
3
+
4
+ bot = Newton::Bot.new do
5
+ configure do |c|
6
+ c.nick = "newton_eval"
7
+ c.server = "irc.freenode.net"
8
+ c.port = 6667
9
+ c.verbose = true
10
+
11
+ store[:allowed_users] = %w(DominikH)
12
+ end
13
+
14
+ helpers do
15
+ def allowed_user?(user)
16
+ # First we want to refresh the user's information (to prevent
17
+ # identity theft)…
18
+ user.whois
19
+
20
+ store[:allowed_users].include?(user.authname)
21
+ end
22
+ end
23
+
24
+ on :connect do
25
+ join "#dominikh-tests"
26
+ end
27
+
28
+ on :message, /^!eval (.+)/ do |command|
29
+ if allowed_user?(user)
30
+ reply eval(command), true
31
+ else
32
+ reply "You are not allowed to evaluate ruby statements"
33
+ end
34
+ end
35
+
36
+ on :message, /^allow (.+)/ do |target|
37
+ if allowed_user?(user)
38
+ store[:allowed_users] << target
39
+ reply "User added"
40
+ else
41
+ reply "You are not allowed to add new users", true
42
+ end
43
+ end
44
+ end
45
+
46
+ bot.start
data/lib/newton/ban.rb ADDED
@@ -0,0 +1,40 @@
1
+ require "newton/mask"
2
+ module Newton
3
+ class Ban
4
+ # @return [Mask, String]
5
+ attr_reader :mask
6
+
7
+ # @return [String]
8
+ attr_reader :by
9
+
10
+ # @return [Time]
11
+ attr_reader :created_at
12
+
13
+ # @return [Boolean]
14
+ attr_reader :extended
15
+
16
+ def initialize(mask, by, at)
17
+ @by, @created_at = by, at
18
+ if mask =~ /^\$/
19
+ @extended = true
20
+ @mask = mask
21
+ else
22
+ @extended = false
23
+ @mask = Mask.new(mask)
24
+ end
25
+ end
26
+
27
+ # @return [Boolean] true if the ban matches `user`
28
+ # @raise [Exceptions::UnsupportedFeature] Newton does not support Freenode's extended bans
29
+ def match(user)
30
+ raise UnsupportedFeature, "extended bans (freenode) are not supported yet" if @extended
31
+ @mask =~ user
32
+ end
33
+ alias_method :=~, :match
34
+
35
+ # @return [String]
36
+ def to_s
37
+ @mask.to_s
38
+ end
39
+ end
40
+ end