beerbot 0.1.0
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 +7 -0
- data/bin/run-irc.rb +24 -0
- data/lib/BeerBot.rb +20 -0
- data/lib/BeerBot/00.utils/DataFile.rb +56 -0
- data/lib/BeerBot/00.utils/InOut.rb +46 -0
- data/lib/BeerBot/00.utils/More.rb +68 -0
- data/lib/BeerBot/00.utils/paramExpand.rb +77 -0
- data/lib/BeerBot/00.utils/utils.rb +149 -0
- data/lib/BeerBot/00.utils/world/IRCWorld.rb +47 -0
- data/lib/BeerBot/00.utils/world/World.rb +84 -0
- data/lib/BeerBot/01.connect/Connection.rb +23 -0
- data/lib/BeerBot/01.connect/IRCConnection.rb +133 -0
- data/lib/BeerBot/01.protocols/botmsg.rb +59 -0
- data/lib/BeerBot/01.protocols/irc.rb +250 -0
- data/lib/BeerBot/02.bot/bot.rb +234 -0
- data/lib/BeerBot/03.more/BotMsgMore.rb +34 -0
- data/lib/BeerBot/06.dispatchers/irc.rb +160 -0
- data/lib/BeerBot/70.scheduler/scheduler.rb +22 -0
- data/lib/BeerBot/Config.rb +62 -0
- data/lib/RunIRC.rb +153 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5711f62c9ce4965a324d3578b5555761d8c493ef
|
4
|
+
data.tar.gz: 61bdaff04c5ae69702858f623316671d305b5474
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5551c5b99e88f48ed52bbc6d3ee3581d8842c9e841551919cdeb2fc37034206239543be1228f47b509d83fe6c7a04a09134b06ff09bbb90d8540a95c96b18497
|
7
|
+
data.tar.gz: 4e189c7c8ac1ba58f3874a8e78cebaf7fdb01b29bc3702830fa43aeec1fd0fbce1afc0495eccc0a26c515be398b27b43e70251245f08d1c0cd146ae1912d6385
|
data/bin/run-irc.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# The files in this directory are part of BeerBot, a a ruby irc bot library.
|
4
|
+
# Copyright (C) 2013,2014 Daniel Bush
|
5
|
+
# This program is distributed under the terms of the GNU
|
6
|
+
# General Public License. A copy of the license should be
|
7
|
+
# enclosed with this project in the file LICENSE. If not
|
8
|
+
# see <http://www.gnu.org/licenses/>.
|
9
|
+
|
10
|
+
raise "Needs ruby 2" if /^1/===RUBY_VERSION
|
11
|
+
require_relative '../lib/RunIRC'
|
12
|
+
|
13
|
+
if ARGV.size == 0 then
|
14
|
+
puts "Usage: ruby beerbot.rb path/to/ircconf.json"
|
15
|
+
puts "See conf/irc.json"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
conffile = ARGV[0]
|
20
|
+
BeerBot::Config.load JSON.load(File.read(conffile))
|
21
|
+
BeerBot::Config.validate!
|
22
|
+
|
23
|
+
$runirc = BeerBot::RunIRC.new BeerBot::Config
|
24
|
+
$runirc.start
|
data/lib/BeerBot.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'CronR' # For scheduler
|
2
|
+
|
3
|
+
require_relative 'BeerBot/00.utils/utils'
|
4
|
+
require_relative 'BeerBot/00.utils/paramExpand'
|
5
|
+
require_relative 'BeerBot/00.utils/DataFile'
|
6
|
+
require_relative 'BeerBot/00.utils/InOut'
|
7
|
+
require_relative 'BeerBot/00.utils/world/IRCWorld'
|
8
|
+
require_relative 'BeerBot/01.connect/IRCConnection'
|
9
|
+
require_relative 'BeerBot/01.protocols/botmsg'
|
10
|
+
require_relative 'BeerBot/01.protocols/irc'
|
11
|
+
require_relative 'BeerBot/02.bot/bot'
|
12
|
+
require_relative 'BeerBot/03.more/BotMsgMore'
|
13
|
+
require_relative 'BeerBot/06.dispatchers/irc'
|
14
|
+
require_relative 'BeerBot/70.scheduler/scheduler'
|
15
|
+
require_relative 'BeerBot/Config'
|
16
|
+
|
17
|
+
module BeerBot
|
18
|
+
module Modules
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
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
|
+
require 'json'
|
9
|
+
module BeerBot
|
10
|
+
module Utils
|
11
|
+
# A class that loads data from a file and allows you to access it
|
12
|
+
# using the #data method.
|
13
|
+
#
|
14
|
+
# If the file is updated (after >=1 sec), #data will reload.
|
15
|
+
class DataFile
|
16
|
+
attr_reader :reloaded # true if last call to #data reloaded file
|
17
|
+
def initialize filepath
|
18
|
+
@filepath = filepath
|
19
|
+
@data = File.read(filepath)
|
20
|
+
@mtime = File.stat(filepath).mtime
|
21
|
+
@reloaded = false
|
22
|
+
end
|
23
|
+
def data
|
24
|
+
@reloaded = false
|
25
|
+
return @data unless File.exists?(@filepath)
|
26
|
+
mtime = File.stat(@filepath).mtime
|
27
|
+
return @data if mtime == @mtime
|
28
|
+
puts "Reloading data file #{@filepath}"
|
29
|
+
@mtime = mtime
|
30
|
+
@data = File.read(@filepath)
|
31
|
+
@reloaded = true
|
32
|
+
@data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# Specialised DataFile that parses json.
|
36
|
+
class JsonDataFile < DataFile
|
37
|
+
attr_reader :json
|
38
|
+
def initialize filepath
|
39
|
+
super
|
40
|
+
@json = JSON.parse(@data)
|
41
|
+
end
|
42
|
+
def data
|
43
|
+
super
|
44
|
+
begin
|
45
|
+
if @reloaded then
|
46
|
+
json = JSON.parse(@data)
|
47
|
+
@json = json
|
48
|
+
end
|
49
|
+
rescue => e
|
50
|
+
return @json
|
51
|
+
end
|
52
|
+
@json
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,46 @@
|
|
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
|
+
module BeerBot
|
9
|
+
module Utils
|
10
|
+
|
11
|
+
# Represents a thread that waits on an in-queue, processes any
|
12
|
+
# things received and puts them on an out-queue.
|
13
|
+
|
14
|
+
class InOut
|
15
|
+
|
16
|
+
attr_reader :inq,:outq,:run,:thread
|
17
|
+
|
18
|
+
def initialize inq:nil,outq:nil,&block
|
19
|
+
@inq = inq
|
20
|
+
@outq = outq
|
21
|
+
@run = block
|
22
|
+
raise "No block given" unless block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def start!
|
26
|
+
@thread = Thread.new {
|
27
|
+
loop {
|
28
|
+
begin
|
29
|
+
thing = @inq.deq
|
30
|
+
response = @run.call(thing)
|
31
|
+
if response then
|
32
|
+
@outq.enq(response)
|
33
|
+
else
|
34
|
+
# TODO
|
35
|
+
end
|
36
|
+
rescue => e
|
37
|
+
puts e
|
38
|
+
puts e.backtrace
|
39
|
+
end
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
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
|
+
module BeerBot
|
9
|
+
|
10
|
+
module Utils
|
11
|
+
|
12
|
+
# A hash that buffers things by some key.
|
13
|
+
#
|
14
|
+
# If 'things' exceeds a set number, then these are stored in an
|
15
|
+
# array against the key.
|
16
|
+
#
|
17
|
+
# For irc and other messaging the key should probably
|
18
|
+
# be key :to of one or more botmsg's.
|
19
|
+
# @see BotMsgMore
|
20
|
+
|
21
|
+
class More < Hash
|
22
|
+
|
23
|
+
attr_accessor :size
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
super
|
27
|
+
# Default value is empty array.
|
28
|
+
self.default_proc = lambda {|h,k|
|
29
|
+
h[k] = []
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def size
|
34
|
+
@size ||= 5 # lines
|
35
|
+
end
|
36
|
+
|
37
|
+
# Fetch array of items from buffer for key 'key'.
|
38
|
+
#
|
39
|
+
# 'key' should probably be a person or channel you are messaging.
|
40
|
+
#
|
41
|
+
# Should return an array of items (eg of botmsg hashes).
|
42
|
+
|
43
|
+
def more key
|
44
|
+
arr = self[key]
|
45
|
+
self[key] = arr.slice(self.size,arr.size) || []
|
46
|
+
return arr.slice(0,self.size-1)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Filter an array of items allowing only
|
50
|
+
# the first 'n' of these.
|
51
|
+
#
|
52
|
+
# The remainder are stored in this hash and can
|
53
|
+
# be accessed via 'key' using 'self.more'.
|
54
|
+
|
55
|
+
def filter arr,key
|
56
|
+
if arr.size <= self.size then
|
57
|
+
self[key] = [] # reset buffer
|
58
|
+
return arr
|
59
|
+
end
|
60
|
+
self[key] = arr.slice(self.size,size)
|
61
|
+
return arr.slice(0,self.size)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,77 @@
|
|
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
|
+
# Randomly generate sentences.
|
14
|
+
#
|
15
|
+
# See spec examples in spec/.
|
16
|
+
#
|
17
|
+
# Take a spec which is of form
|
18
|
+
# [<sentence>,<hash>]
|
19
|
+
# where <sentence> is a string and
|
20
|
+
# each entry in <hash>
|
21
|
+
# <key> => <value>
|
22
|
+
# where <key> is a symbol, and <value>
|
23
|
+
# is an array of <sentence>'s.
|
24
|
+
#
|
25
|
+
# <sentence> is a sentence or phrase.
|
26
|
+
# If a word starts with ':' it will be looked up
|
27
|
+
# in <hash>, an entry from <value> will be randomly
|
28
|
+
# selected.
|
29
|
+
# The entry from <value> will also undergo the
|
30
|
+
# same ':' substitution.
|
31
|
+
|
32
|
+
module ParamExpand
|
33
|
+
|
34
|
+
# Recursively expand a sentence with parameters starting with
|
35
|
+
# ':' using values sampled from params.
|
36
|
+
#
|
37
|
+
# Split, map and rejoin a sentence (str).
|
38
|
+
#
|
39
|
+
# After splitting, apply expand on parameter words ":word".
|
40
|
+
#
|
41
|
+
# Watch out, throws error, be prepared.
|
42
|
+
|
43
|
+
def self.expand(str,params,raise_error=false)
|
44
|
+
words = str.split(/\s+/) # not great, we lose additional spaces
|
45
|
+
words.map {|word|
|
46
|
+
if word[0] == ':' then
|
47
|
+
if word[1] == ':' then
|
48
|
+
word # let the bot code gsub this; ::from , ::to
|
49
|
+
else
|
50
|
+
self.expand(
|
51
|
+
self.lookup(word[1..-1],params,raise_error),
|
52
|
+
params,raise_error
|
53
|
+
)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
word
|
57
|
+
end
|
58
|
+
}.select{|word| word != nil && word != ""}.join(' ').strip
|
59
|
+
end
|
60
|
+
|
61
|
+
# Randomly select entry from params.
|
62
|
+
|
63
|
+
def self.lookup(str,params,raise_error=false)
|
64
|
+
if not params.has_key?(str) then
|
65
|
+
if raise_error then
|
66
|
+
raise "'#{str}' has no corresponding symbol in params #{params}"
|
67
|
+
else
|
68
|
+
return ""
|
69
|
+
end
|
70
|
+
end
|
71
|
+
result = params[str].sample
|
72
|
+
result ? result : ""
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,149 @@
|
|
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
|
+
# Look for parameters in a string.
|
14
|
+
#
|
15
|
+
# Numeric parameters => "... ::1 ... ::2 "
|
16
|
+
# Key parameters => "... ::foo ... ::bar "
|
17
|
+
# Multi => "... ::foo|::1 ... ::bar|::foo|::1 "
|
18
|
+
#
|
19
|
+
# (?: ) is a non-capturing group.
|
20
|
+
|
21
|
+
def self.scan_param msg
|
22
|
+
matches = msg.scan(/(?:::[^\W\s:|]*(?:\|::[^\W\s:|]*)*)/)
|
23
|
+
matches.map{|m|
|
24
|
+
a = m.split('|').map{|m2|
|
25
|
+
m2 = m2.sub('::','')
|
26
|
+
case m2
|
27
|
+
when /\d+/
|
28
|
+
m2.to_i
|
29
|
+
else
|
30
|
+
m2
|
31
|
+
end
|
32
|
+
}
|
33
|
+
[m,a]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return a parser that takes string msg and extracts a specified
|
38
|
+
# prefix at beginning.
|
39
|
+
#
|
40
|
+
# The prefix might be a nick or a command prefix.
|
41
|
+
#
|
42
|
+
# Use this to get commands issued to the bot through a channel.
|
43
|
+
#
|
44
|
+
# TODO: make sure this returns msg without the prefix, or nil
|
45
|
+
# otherwise.
|
46
|
+
|
47
|
+
def self.make_prefix_parser prefix
|
48
|
+
rx = Regexp.new("^#{prefix}\\W?(.*)",'i')
|
49
|
+
lambda {|msg|
|
50
|
+
if m = rx.match(msg) then
|
51
|
+
m[1].strip
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
# Expand a string with numeric and key parameters using data
|
57
|
+
# provided.
|
58
|
+
#
|
59
|
+
# Parameters should be preceded with a double-colon in the msg.
|
60
|
+
# Numeric parameters are matched to 'args'.
|
61
|
+
# So ::1 => args[0] etc
|
62
|
+
#
|
63
|
+
# 'expand' will return the expanded string as best it can and an
|
64
|
+
# error object.
|
65
|
+
# The error object will tell you if there weren't enough
|
66
|
+
# parameters in args to satisfy the numeric parameters in the
|
67
|
+
# string.
|
68
|
+
#
|
69
|
+
# ("::1 ::foo ::bar|::1",'a',foo:'b') => "a b a"
|
70
|
+
|
71
|
+
def self.expand msg,*args,**kargs
|
72
|
+
errargs = []
|
73
|
+
params = self.scan_param(msg)
|
74
|
+
# Do the big ones first.
|
75
|
+
params = params.sort{|a,b|b[1].size<=>a[1].size}
|
76
|
+
params.each {|i|
|
77
|
+
# pattern: "::1|::foo"
|
78
|
+
# parts: [1,'foo']
|
79
|
+
pattern,parts = i
|
80
|
+
found = false
|
81
|
+
_errargs = []
|
82
|
+
_errkargs = []
|
83
|
+
parts.each {|part|
|
84
|
+
v = nil
|
85
|
+
case part
|
86
|
+
when Fixnum
|
87
|
+
v = args[part-1]
|
88
|
+
_errargs.push(part) unless v
|
89
|
+
else
|
90
|
+
if part == '' then # pattern is or contains '::' which has part ''.
|
91
|
+
v = ''
|
92
|
+
else
|
93
|
+
v = kargs[part.to_sym]
|
94
|
+
_errkargs.push(part) unless v
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if v then
|
98
|
+
if v == '' then
|
99
|
+
#byebug
|
100
|
+
# Squeeze spaces. Not perfect, but it'll do.
|
101
|
+
msg = msg.gsub(/ ?#{pattern.gsub('|','\|')} ?/,' ')
|
102
|
+
else
|
103
|
+
msg = msg.gsub(pattern,v.to_s)
|
104
|
+
end
|
105
|
+
found = true
|
106
|
+
end
|
107
|
+
}
|
108
|
+
unless found then
|
109
|
+
errargs += _errargs
|
110
|
+
end
|
111
|
+
}
|
112
|
+
[msg,errargs]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Regex that looks for a sed command eg
|
116
|
+
# "s/pattern/replacement/flags" in a string.
|
117
|
+
#
|
118
|
+
# This regex doesn't handle backslash escapes, so if pattern or
|
119
|
+
# replacement contain '/' use another delimiter eg s#...#...# etc.
|
120
|
+
#
|
121
|
+
# Non alphanumeric delimiters are allowed.
|
122
|
+
#
|
123
|
+
# Returns: nil or a MatchData instance with symbol keys:
|
124
|
+
# :sed (the whole sed command), :sep (the separator), :pattern,
|
125
|
+
# :replacement, :flags
|
126
|
+
#
|
127
|
+
# If you want to combine this regex, call
|
128
|
+
# sed_regex.source => <string>
|
129
|
+
|
130
|
+
def self.sed_regex
|
131
|
+
%r{
|
132
|
+
^(?<before>.*)
|
133
|
+
\b
|
134
|
+
(?<sed>
|
135
|
+
s
|
136
|
+
(?<sep>[^A-z0-9\s])
|
137
|
+
(?<pattern>(?!\g<sep>)*.*)
|
138
|
+
\k<sep>
|
139
|
+
(?<replacement>(?!\g<sep>)*.*)
|
140
|
+
\k<sep>
|
141
|
+
(?<flags>[A-z]*)
|
142
|
+
)
|
143
|
+
(?<after>.*)$
|
144
|
+
}x
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|