beerbot 0.1.5 → 0.2.0.pre.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.
- checksums.yaml +4 -4
- data/bin/{run-irc.rb → beerbot-run-irc.rb} +9 -3
- data/lib/RunIRC.rb +46 -18
- data/lib/beerbot.rb +17 -0
- data/lib/{BeerBot → beerbot}/00.utils/InOut.rb +0 -0
- data/lib/beerbot/00.utils/utils.rb +34 -0
- data/lib/{BeerBot → beerbot}/01.bot/Bot.rb +49 -21
- data/lib/{BeerBot → beerbot}/01.bot/BotModule.rb +0 -0
- data/lib/{BeerBot → beerbot}/01.bot/botmsg.rb +51 -0
- data/lib/{BeerBot → beerbot}/01.connect/IRCConnection.rb +39 -19
- data/lib/{BeerBot → beerbot}/02.protocols/irc.rb +1 -1
- data/lib/{BeerBot → beerbot}/06.dispatchers/dispatcher.rb +32 -67
- data/lib/{BeerBot → beerbot}/70.scheduler/scheduler.rb +0 -0
- data/lib/{BeerBot → beerbot}/Config.rb +36 -3
- metadata +17 -24
- data/lib/BeerBot.rb +0 -22
- data/lib/BeerBot/00.utils/DataFile.rb +0 -103
- data/lib/BeerBot/00.utils/More.rb +0 -68
- data/lib/BeerBot/00.utils/param_expand.rb +0 -105
- data/lib/BeerBot/00.utils/sentence_expand.rb +0 -77
- data/lib/BeerBot/00.utils/utils.rb +0 -92
- data/lib/BeerBot/00.utils/world/IRCWorld.rb +0 -47
- data/lib/BeerBot/00.utils/world/World.rb +0 -74
- data/lib/BeerBot/01.bot/BotMsgMore.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87f292c599a9b6b747cf3946ba5693c3eac5e6fc
|
4
|
+
data.tar.gz: 0a3a0612f3cedbb8c52508dba7cf67af4f5cae6f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e3d16c56405388a6183dc99057dc1953c18f3df825e295ca07f4c851ddff2d3103449f89d8e56ad9a12b56fa7c3f67a0f3d53cb1ce90f088e29e5fa74107eb66
|
7
|
+
data.tar.gz: 4b629d26bd44bd7a0a9e1590f07ed74f41323b3575a9415db57518a28d7c8c24d9f91a7f1163ec83bb5e7ec5a31a4715b16f23b7f172c65896c152281040cb6f
|
@@ -7,6 +7,11 @@
|
|
7
7
|
# enclosed with this project in the file LICENSE. If not
|
8
8
|
# see <http://www.gnu.org/licenses/>.
|
9
9
|
|
10
|
+
# If you're running this WITHOUT the gem, you probably want to do
|
11
|
+
# something like this (in beerbot's root directory):
|
12
|
+
#
|
13
|
+
# ruby -Ilib bin/beerbot-run-irb.rb path/to/conf.json
|
14
|
+
|
10
15
|
raise "Needs ruby 2" if /^1/===RUBY_VERSION
|
11
16
|
require_relative '../lib/RunIRC'
|
12
17
|
|
@@ -17,8 +22,9 @@ if ARGV.size == 0 then
|
|
17
22
|
end
|
18
23
|
|
19
24
|
conffile = ARGV[0]
|
20
|
-
BeerBot::Config.
|
21
|
-
|
25
|
+
config = BeerBot::Config.new
|
26
|
+
config.load JSON.load(File.read(conffile))
|
27
|
+
config.validate!
|
22
28
|
|
23
|
-
$runirc = BeerBot::RunIRC.new
|
29
|
+
$runirc = BeerBot::RunIRC.new(config)
|
24
30
|
$runirc.start
|
data/lib/RunIRC.rb
CHANGED
@@ -9,7 +9,7 @@ require 'set'
|
|
9
9
|
require 'rubygems'
|
10
10
|
require 'pry'
|
11
11
|
require 'json'
|
12
|
-
require_relative '
|
12
|
+
require_relative 'beerbot'
|
13
13
|
|
14
14
|
# Run the irc bot.
|
15
15
|
#
|
@@ -23,15 +23,15 @@ module BeerBot; end
|
|
23
23
|
class BeerBot::RunIRC
|
24
24
|
|
25
25
|
Utils = BeerBot::Utils
|
26
|
-
IRCWorld = BeerBot::Utils::IRCWorld
|
27
26
|
InOut = BeerBot::Utils::InOut
|
28
27
|
IRCConnection = BeerBot::IRCConnection
|
29
28
|
IRC = BeerBot::Protocol::IRC
|
30
29
|
Bot = BeerBot::Bot
|
30
|
+
BotMsg = BeerBot::BotMsg
|
31
31
|
Dispatcher = BeerBot::Dispatchers::Dispatcher
|
32
32
|
Scheduler = BeerBot::Scheduler
|
33
33
|
|
34
|
-
attr_accessor :config,:bot,:scheduler,:dispatcher,:
|
34
|
+
attr_accessor :config,:bot,:scheduler,:dispatcher,:conn,:postq,:parse,:more
|
35
35
|
|
36
36
|
# Initialize all parts of the system here.
|
37
37
|
#
|
@@ -41,27 +41,27 @@ class BeerBot::RunIRC
|
|
41
41
|
|
42
42
|
def initialize config
|
43
43
|
|
44
|
+
@echo = true
|
44
45
|
@path = File.expand_path(File.dirname(__FILE__)+'/..')
|
45
46
|
@module_path = config['moduledir']
|
46
47
|
@config = config
|
47
48
|
|
48
49
|
# Create the bot.
|
49
|
-
@bot = Bot.new
|
50
|
-
|
51
|
-
|
52
|
-
# (lists channels and users we know about)
|
53
|
-
@world = IRCWorld.new(config['nick'])
|
50
|
+
@bot = Bot.new
|
51
|
+
@bot.load!(config['modules'],@module_path)
|
52
|
+
config.bot = @bot
|
54
53
|
|
55
54
|
# Dispatcher which receives messages and interacts with the bot.
|
56
55
|
@dispatcher = Dispatcher.new(
|
57
56
|
@bot,
|
58
57
|
config['nick'],
|
59
58
|
prefix:config['cmd_prefix'],
|
60
|
-
|
59
|
+
config:config
|
61
60
|
)
|
62
61
|
|
63
62
|
# Set up scheduler (this doesn't start it yet)...
|
64
63
|
@scheduler = Scheduler.instance(config['timezone'])
|
64
|
+
config.scheduler = @scheduler
|
65
65
|
|
66
66
|
# Create but don't open the irc connection.
|
67
67
|
@conn = IRCConnection.new(
|
@@ -85,12 +85,32 @@ class BeerBot::RunIRC
|
|
85
85
|
# which also need to be dispatched.
|
86
86
|
|
87
87
|
@scheduler_thread = InOut.new(inq:@scheduler.queue,outq:@conn.writeq) {|cron_job|
|
88
|
-
puts "<< scheduler #{cron_job.inspect}"
|
89
|
-
puts "<< scheduler #{@scheduler.time}"
|
88
|
+
puts "<< scheduler #{cron_job.inspect}" if @echo
|
89
|
+
puts "<< scheduler #{@scheduler.time}" if @echo
|
90
90
|
IRC.to_irc(cron_job.run)
|
91
91
|
}
|
92
92
|
@scheduler_thread.start!
|
93
93
|
|
94
|
+
# Active messaging queue.
|
95
|
+
#
|
96
|
+
# 'config' will be injected into bot modules.
|
97
|
+
# config.out should be a queue that we can dequeue.
|
98
|
+
|
99
|
+
@active_thread = InOut.new(inq:@config.out,outq:@conn.writeq) {|replies|
|
100
|
+
puts "<< active #{replies}" if @echo
|
101
|
+
# TODO: almost identical logic in the dispatcher class (in
|
102
|
+
# @dispatcher_thread).
|
103
|
+
case replies
|
104
|
+
when String # assume protocol string eg irc
|
105
|
+
replies
|
106
|
+
when Hash,Array,Proc
|
107
|
+
IRC.to_irc(BotMsg.to_a(replies))
|
108
|
+
else
|
109
|
+
[]
|
110
|
+
end
|
111
|
+
}
|
112
|
+
@active_thread.start!
|
113
|
+
|
94
114
|
# Set up a repl in a separate thread.
|
95
115
|
#
|
96
116
|
# In pry, you can then do:
|
@@ -102,6 +122,9 @@ class BeerBot::RunIRC
|
|
102
122
|
binding.pry
|
103
123
|
}
|
104
124
|
|
125
|
+
@bot.init(@config)
|
126
|
+
@bot.update_config(@config)
|
127
|
+
|
105
128
|
# Do stuff once we've identified with the irc server...
|
106
129
|
#
|
107
130
|
# Join channels.
|
@@ -118,6 +141,15 @@ class BeerBot::RunIRC
|
|
118
141
|
}
|
119
142
|
end
|
120
143
|
|
144
|
+
# Toggle whether inputs and outputs show on the repl screen.
|
145
|
+
#
|
146
|
+
# Call this from the pry repl.
|
147
|
+
|
148
|
+
def echo
|
149
|
+
@echo = !@echo
|
150
|
+
@conn.echo = @echo
|
151
|
+
end
|
152
|
+
|
121
153
|
# Start the connection.
|
122
154
|
|
123
155
|
def start
|
@@ -142,14 +174,10 @@ class BeerBot::RunIRC
|
|
142
174
|
@conn.writeq.enq(IRC.join(chan))
|
143
175
|
end
|
144
176
|
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# You could use
|
177
|
+
# Convenience method to leave a channel.
|
148
178
|
|
149
|
-
def
|
150
|
-
@
|
151
|
-
@bot = Bot.new(@module_path,modules)
|
152
|
-
@dispatcher.bot = @bot
|
179
|
+
def leave chan
|
180
|
+
@conn.writeq.enq(IRC.leave(chan))
|
153
181
|
end
|
154
182
|
|
155
183
|
end
|
data/lib/beerbot.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'CronR' # For scheduler
|
2
|
+
|
3
|
+
require_relative 'beerbot/00.utils/utils'
|
4
|
+
require_relative 'beerbot/00.utils/InOut'
|
5
|
+
require_relative 'beerbot/01.connect/IRCConnection'
|
6
|
+
require_relative 'beerbot/01.bot/botmsg'
|
7
|
+
require_relative 'beerbot/01.bot/BotModule'
|
8
|
+
require_relative 'beerbot/01.bot/Bot'
|
9
|
+
require_relative 'beerbot/02.protocols/irc'
|
10
|
+
require_relative 'beerbot/06.dispatchers/dispatcher'
|
11
|
+
require_relative 'beerbot/70.scheduler/scheduler'
|
12
|
+
require_relative 'beerbot/Config'
|
13
|
+
|
14
|
+
module BeerBot
|
15
|
+
module Modules
|
16
|
+
end
|
17
|
+
end
|
File without changes
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# The files in this directory are part of BeerBot, a a ruby irc bot library.
|
2
|
+
# Copyright (C) 2014 Daniel Bush
|
3
|
+
# This program is distributed under the terms of the GNU
|
4
|
+
# General Public License. A copy of the license should be
|
5
|
+
# enclosed with this project in the file LICENSE. If not
|
6
|
+
# see <http://www.gnu.org/licenses/>.
|
7
|
+
|
8
|
+
|
9
|
+
module BeerBot
|
10
|
+
|
11
|
+
module Utils
|
12
|
+
|
13
|
+
# Return a parser that takes string msg and extracts a specified
|
14
|
+
# prefix at beginning.
|
15
|
+
#
|
16
|
+
# The prefix might be a nick or a command prefix.
|
17
|
+
#
|
18
|
+
# Use this to get commands issued to the bot through a channel.
|
19
|
+
#
|
20
|
+
# TODO: make sure this returns msg without the prefix, or nil
|
21
|
+
# otherwise.
|
22
|
+
|
23
|
+
def self.make_prefix_parser prefix
|
24
|
+
rx = Regexp.new("^#{prefix}\\W?(.*)",'i')
|
25
|
+
lambda {|msg|
|
26
|
+
if m = rx.match(msg) then
|
27
|
+
m[1].strip
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -16,17 +16,41 @@ module BeerBot
|
|
16
16
|
|
17
17
|
attr_accessor :module_path,:module_names
|
18
18
|
|
19
|
-
def initialize
|
19
|
+
def initialize
|
20
20
|
super()
|
21
|
-
@module_path = module_path
|
22
|
-
@module_names = module_names
|
23
|
-
self.load!
|
24
21
|
end
|
25
22
|
|
26
|
-
|
23
|
+
# Call all init methods on bot modules that have them.
|
24
|
+
#
|
25
|
+
# Should only be called once.
|
26
|
+
|
27
|
+
def init config
|
28
|
+
self.valid_modules.each {|botmodule|
|
29
|
+
if botmodule[:mod].respond_to?(:init) then
|
30
|
+
botmodule[:mod].init(config)
|
31
|
+
end
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Call #config on all valid bot modules.
|
36
|
+
|
37
|
+
def update_config config
|
38
|
+
self.valid_modules.each {|botmodule|
|
39
|
+
if botmodule[:mod].respond_to?(:config) then
|
40
|
+
botmodule[:mod].config(config)
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Purge existing modules from this array and load modules with
|
46
|
+
# names in module_names in module_path on disk into memory.
|
47
|
+
|
48
|
+
def load! module_names,module_path
|
49
|
+
@module_path = module_path
|
50
|
+
@module_names = module_names
|
27
51
|
self.reject!{true} unless self.empty? # ick :)
|
28
|
-
Dir.chdir(
|
29
|
-
|
52
|
+
Dir.chdir(module_path) {
|
53
|
+
module_names.each {|name|
|
30
54
|
initfile = "#{name}/init.rb"
|
31
55
|
modname = "::BeerBot::Modules::#{name}"
|
32
56
|
mod = nil
|
@@ -88,12 +112,16 @@ module BeerBot
|
|
88
112
|
def run meth,*args,**kargs
|
89
113
|
self.valid_modules.inject([]) {|arr,bot_module|
|
90
114
|
name,mod = bot_module.values_at(:name,:mod)
|
91
|
-
|
92
|
-
|
115
|
+
unless mod.respond_to?(meth) then
|
116
|
+
next arr
|
117
|
+
end
|
118
|
+
reply = mod.send(meth,*args,**kargs)
|
119
|
+
suppress,botmsg = BotMsg.to_reply_format(reply)
|
93
120
|
if botmsg then
|
94
|
-
|
95
|
-
|
96
|
-
|
121
|
+
arr += botmsg
|
122
|
+
end
|
123
|
+
if suppress then
|
124
|
+
break arr
|
97
125
|
else
|
98
126
|
arr
|
99
127
|
end
|
@@ -102,26 +130,26 @@ module BeerBot
|
|
102
130
|
|
103
131
|
# Process messages addressed directly to the bot.
|
104
132
|
|
105
|
-
def cmd msg,from:nil,to:nil,me:false,
|
133
|
+
def cmd msg,from:nil,to:nil,me:false,config:nil
|
106
134
|
if @cmd then
|
107
|
-
@cmd.call(msg,from:from,to:to,
|
135
|
+
@cmd.call(msg,from:from,to:to,config:config,me:me)
|
108
136
|
else
|
109
|
-
self.run(:cmd,msg,from:from,to:to,
|
137
|
+
self.run(:cmd,msg,from:from,to:to,config:config,me:me)
|
110
138
|
end
|
111
139
|
end
|
112
140
|
|
113
141
|
# Process messages the bot overhears.
|
114
142
|
|
115
|
-
def hear msg,from:nil,to:nil,me:false,
|
143
|
+
def hear msg,from:nil,to:nil,me:false,config:nil
|
116
144
|
if @hear then
|
117
|
-
@hear.call(msg,from:from,to:to,me:me,
|
145
|
+
@hear.call(msg,from:from,to:to,me:me,config:config)
|
118
146
|
else
|
119
|
-
self.run(:hear,msg,from:from,to:to,me:me,
|
147
|
+
self.run(:hear,msg,from:from,to:to,me:me,config:config)
|
120
148
|
end
|
121
149
|
end
|
122
150
|
|
123
|
-
def action action,from:nil,to:nil,me:false,
|
124
|
-
self.run(:action,action,from:from,to:to,me:me,
|
151
|
+
def action action,from:nil,to:nil,me:false,config:nil
|
152
|
+
self.run(:action,action,from:from,to:to,me:me,config:config)
|
125
153
|
end
|
126
154
|
|
127
155
|
# Handle events other than being messaged.
|
@@ -135,7 +163,7 @@ module BeerBot
|
|
135
163
|
self.run(:event,event,**kargs)
|
136
164
|
end
|
137
165
|
|
138
|
-
def help arr,from:nil,to:nil,
|
166
|
+
def help arr,from:nil,to:nil,config:nil,me:false
|
139
167
|
m = []
|
140
168
|
modname,*topics = arr
|
141
169
|
|
File without changes
|
@@ -99,6 +99,57 @@ module BeerBot
|
|
99
99
|
botmsg
|
100
100
|
end
|
101
101
|
end
|
102
|
+
|
103
|
+
# Transform all replies from bot modules to ARRAY FORMAT.
|
104
|
+
#
|
105
|
+
# A module may reply in either format. But we convert it here. The
|
106
|
+
# single return format is the simplest, which is why we have 2
|
107
|
+
# formats.
|
108
|
+
#
|
109
|
+
# ARRAY FORMAT
|
110
|
+
#
|
111
|
+
# [<bool>,<reply>]
|
112
|
+
#
|
113
|
+
# where bool = true => "use <reply> and keep going"
|
114
|
+
# = false => "use <reply> but stop here"
|
115
|
+
# where reply = whatever the bot returns.
|
116
|
+
# NOTE: any other type of array will assumed to be in
|
117
|
+
# single return form...
|
118
|
+
#
|
119
|
+
# SINGLE RETURN FORMAT
|
120
|
+
#
|
121
|
+
# This form assumes you return one thing, either nil/false or a
|
122
|
+
# botmsg. Returning nil/false won't suppress subsequent modules
|
123
|
+
# from being evaluated.
|
124
|
+
#
|
125
|
+
# Note, that "whatever the bot returns" will need to be one of the
|
126
|
+
# valid forms of a botmsg for it to get sent out.
|
127
|
+
|
128
|
+
def self.to_reply_format thing
|
129
|
+
case thing
|
130
|
+
when Array
|
131
|
+
bool,botmsg = thing
|
132
|
+
case bool
|
133
|
+
when TrueClass,FalseClass
|
134
|
+
# Assume array format:
|
135
|
+
[bool,self.to_a(botmsg)]
|
136
|
+
else
|
137
|
+
# Assume single return format...
|
138
|
+
# Array of any sort is truthy, so suppress.
|
139
|
+
[true,self.to_a(thing)]
|
140
|
+
end
|
141
|
+
else
|
142
|
+
# Asume single return format...
|
143
|
+
#
|
144
|
+
# Look at truthiness of thing to determine whether to suppress
|
145
|
+
# further responses (true) or continue (false).
|
146
|
+
if thing then
|
147
|
+
[true,self.to_a(thing)]
|
148
|
+
else
|
149
|
+
[false,self.to_a(thing)]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
102
153
|
|
103
154
|
end
|
104
155
|
|
@@ -21,8 +21,10 @@ module BeerBot
|
|
21
21
|
# Queue containing received messages from the server.
|
22
22
|
attr_accessor :queue,:writeq,:readyq
|
23
23
|
attr_accessor :connection,:server,:port,:nick,:thread
|
24
|
+
attr_accessor :echo
|
24
25
|
|
25
26
|
def initialize server:nil,port:6667,nick:'beerbot'
|
27
|
+
@echo = true
|
26
28
|
@server = server
|
27
29
|
@port = port
|
28
30
|
@nick = nick
|
@@ -33,6 +35,7 @@ module BeerBot
|
|
33
35
|
# to the irc server isn't ready yet:
|
34
36
|
@readyq = Queue.new
|
35
37
|
@ready = false
|
38
|
+
@ready_blocks = []
|
36
39
|
@ready_mutex = Mutex.new
|
37
40
|
@write_mutex = Mutex.new
|
38
41
|
|
@@ -44,9 +47,13 @@ module BeerBot
|
|
44
47
|
|
45
48
|
def ready!
|
46
49
|
@ready_mutex.synchronize {
|
50
|
+
unless @ready_blocks.empty? then
|
51
|
+
@ready_blocks.each{|b| @readyq.enq(b)}
|
52
|
+
end
|
47
53
|
@ready = true
|
48
54
|
while @readyq.size > 0
|
49
55
|
block = @readyq.deq
|
56
|
+
@ready_blocks.push(block)
|
50
57
|
block.call
|
51
58
|
end
|
52
59
|
}
|
@@ -70,30 +77,43 @@ module BeerBot
|
|
70
77
|
# It should respond to whatever is called on @connection
|
71
78
|
# eg open,gets,write.
|
72
79
|
# Use for testing this class.
|
80
|
+
#
|
81
|
+
# May throw errors.
|
82
|
+
# - @connection.eof? can throw things like ECONNRESET etc
|
73
83
|
|
74
84
|
def open connection=nil
|
75
|
-
if connection then
|
76
|
-
@connection = connection
|
77
|
-
@connection.open(self.server, self.port)
|
78
|
-
else
|
79
|
-
@connection = TCPSocket.open(self.server, self.port)
|
80
|
-
end
|
81
|
-
self.write("USER #{@nick} #{@nick} #{@nick} :#{@nick}")
|
82
|
-
self.write("NICK #{@nick}")
|
83
85
|
@thread = Thread.new {
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
self.
|
86
|
+
loop do
|
87
|
+
begin
|
88
|
+
if connection then
|
89
|
+
@connection = connection
|
90
|
+
@connection.open(self.server, self.port)
|
91
|
+
else
|
92
|
+
@connection = TCPSocket.open(self.server, self.port)
|
93
|
+
end
|
94
|
+
self.write("USER #{@nick} #{@nick} #{@nick} :#{@nick}")
|
95
|
+
self.write("NICK #{@nick}")
|
96
|
+
while not @connection.eof? do
|
97
|
+
str = @connection.gets()
|
98
|
+
puts "<< #{str}" if @echo
|
99
|
+
case str
|
100
|
+
when /^PING (.*)$/
|
101
|
+
self.write "PONG #{$1}"
|
102
|
+
when / 001 / # ready
|
103
|
+
self.ready!
|
104
|
+
else
|
105
|
+
self.queue.enq(str)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue => e
|
109
|
+
puts "Connection whoops: #{e}"
|
94
110
|
end
|
111
|
+
@ready = false
|
112
|
+
puts "Sleeping #{10} then try again..."
|
113
|
+
sleep 10
|
95
114
|
end
|
96
115
|
}
|
116
|
+
|
97
117
|
@write_thread = Thread.new {
|
98
118
|
loop do
|
99
119
|
thing = @writeq.deq
|
@@ -115,7 +135,7 @@ module BeerBot
|
|
115
135
|
case message
|
116
136
|
when String
|
117
137
|
message = message.chomp + "\r\n"
|
118
|
-
puts ">> #{message}"
|
138
|
+
puts ">> #{message}" if @echo
|
119
139
|
@write_mutex.synchronize {
|
120
140
|
@connection.print(message)
|
121
141
|
}
|