newton 0.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/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