Auto 4.0.0.alpha.1-x86-mingw32
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/.yardopts +7 -0
- data/Gemfile +19 -0
- data/LICENSE.md +31 -0
- data/README.md +109 -0
- data/Rakefile +41 -0
- data/bin/auto +110 -0
- data/bin/auto-conf +45 -0
- data/conf/example.json +100 -0
- data/conf/example.yml +125 -0
- data/docs/Contributing.md +77 -0
- data/docs/Events.md +103 -0
- data/docs/Todo.md +21 -0
- data/docs/Upgrade.md +16 -0
- data/ext/dsl_base.c +49 -0
- data/ext/libauto/auto.h +20 -0
- data/ext/libauto/extconf.rb +16 -0
- data/ext/libauto/libauto.c +29 -0
- data/ext/libauto/libauto.h +28 -0
- data/ext/libauto/logger.c +177 -0
- data/ext/libauto/logger.h +44 -0
- data/lib/auto.rb +43 -0
- data/lib/auto/api.rb +7 -0
- data/lib/auto/api/events.rb +166 -0
- data/lib/auto/api/object.rb +29 -0
- data/lib/auto/api/plugin.rb +155 -0
- data/lib/auto/api/timers.rb +93 -0
- data/lib/auto/bot.rb +338 -0
- data/lib/auto/config.rb +181 -0
- data/lib/auto/configure.rb +410 -0
- data/lib/auto/configure/shell.rb +154 -0
- data/lib/auto/dsl/base.rb +74 -0
- data/lib/auto/dsl/irc.rb +13 -0
- data/lib/auto/irc.rb +8 -0
- data/lib/auto/irc/common.rb +63 -0
- data/lib/auto/irc/library.rb +89 -0
- data/lib/auto/irc/object/channel.rb +21 -0
- data/lib/auto/irc/object/entity.rb +90 -0
- data/lib/auto/irc/object/message.rb +99 -0
- data/lib/auto/irc/object/user.rb +139 -0
- data/lib/auto/irc/protocol.rb +164 -0
- data/lib/auto/irc/protocol/numerics.rb +60 -0
- data/lib/auto/irc/sasl/diffie_hellman.rb +36 -0
- data/lib/auto/irc/sasl/mech.rb +15 -0
- data/lib/auto/irc/sasl/mech/dh_blowfish.rb +83 -0
- data/lib/auto/irc/sasl/mech/plain.rb +39 -0
- data/lib/auto/irc/server.rb +301 -0
- data/lib/auto/irc/state/channel_manager.rb +6 -0
- data/lib/auto/irc/state/support.rb +142 -0
- data/lib/auto/irc/state/user_manager.rb +6 -0
- data/lib/auto/irc/std/commands.rb +99 -0
- data/lib/auto/irc/std/numerics.rb +216 -0
- data/lib/auto/rubyext/integer.rb +25 -0
- data/lib/auto/rubyext/string.rb +10 -0
- data/lib/auto/version.rb +18 -0
- data/lib/libauto.so +0 -0
- data/spec/api_events_spec.rb +68 -0
- data/spec/config_json_spec.rb +116 -0
- data/spec/config_other_spec.rb +29 -0
- data/spec/config_yaml_spec.rb +136 -0
- data/spec/helper.rb +19 -0
- data/spec/irc_object_entity_spec.rb +51 -0
- data/spec/logger_spec.rb +30 -0
- data/spec/plugin_base_spec.rb +35 -0
- data/spec/timers_spec.rb +42 -0
- metadata +238 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
# Namespace: Auto
|
5
|
+
module Auto
|
6
|
+
|
7
|
+
# Namespace: API
|
8
|
+
module API
|
9
|
+
|
10
|
+
# A superclass for {Auto::API::Timers} and {Auto::API::Events}.
|
11
|
+
class Object
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# Get a random character.
|
16
|
+
#
|
17
|
+
# @return [String] A random character.
|
18
|
+
def get_rand_char
|
19
|
+
chrs = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".split(//)
|
20
|
+
chrs[rand(chrs.length)]
|
21
|
+
end
|
22
|
+
|
23
|
+
end # class Object
|
24
|
+
|
25
|
+
end # module API
|
26
|
+
|
27
|
+
end # module Auto
|
28
|
+
|
29
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'ostruct'
|
5
|
+
require 'libauto'
|
6
|
+
require 'auto/bot'
|
7
|
+
|
8
|
+
module Auto
|
9
|
+
|
10
|
+
module API
|
11
|
+
|
12
|
+
# A basic superclass for plugins.
|
13
|
+
#
|
14
|
+
# @api Auto
|
15
|
+
# @since 4.0.0
|
16
|
+
# @author noxgirl
|
17
|
+
# @author swarley
|
18
|
+
#
|
19
|
+
# @!attribute [r] name
|
20
|
+
# @return [String] Name of the plugin.
|
21
|
+
#
|
22
|
+
# @!attribute [r] summary
|
23
|
+
# @return [String] Summary of the plugin.
|
24
|
+
#
|
25
|
+
# @!attribute [r] version
|
26
|
+
# @return [String] Version of the plugin.
|
27
|
+
#
|
28
|
+
# @!attribute [r] library
|
29
|
+
# @return [String] The library upon which the plugin is based.
|
30
|
+
#
|
31
|
+
# @!attribute [r] author
|
32
|
+
# @return [String] Author of the plugin.
|
33
|
+
#
|
34
|
+
# @!attribute [r] auto
|
35
|
+
# @return [String] Version of Auto required by the plugin.
|
36
|
+
class Plugin
|
37
|
+
|
38
|
+
attr_reader :name, :summary, :version, :library, :author, :auto
|
39
|
+
|
40
|
+
# Configure the plugin.
|
41
|
+
#
|
42
|
+
# @yieldparam [OpenStruct] conf Configuration structure.
|
43
|
+
#
|
44
|
+
# - +conf.name+: Name of the plugin (String).
|
45
|
+
# - +conf.summary+: Summary of the plugin (String).
|
46
|
+
# - +conf.version+: Version of the plugin (String).
|
47
|
+
# - +conf.library+: Library upon which the plugin is based. (String).
|
48
|
+
# - +conf.author+: Author of the plugin (String).
|
49
|
+
# - +conf.auto+: Required version of Auto (String). Should be in the format of
|
50
|
+
# +'~> version'+ or +'>= version'+. +~>+ means at least +version+ but no later
|
51
|
+
# than the minor version (e.g. +'~> 4.0'+ will allow +4.0.2+ but not +4.1.0+).
|
52
|
+
# +>=+ means at at least +version+.
|
53
|
+
#
|
54
|
+
# Additionally, it should be noted that +conf.library+ should be either of the
|
55
|
+
# core libraries, *or* 'multi' if it uses multiple libraries.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# configure do |c|
|
59
|
+
# c.name = 'MagicPlugin'
|
60
|
+
# c.summary = 'A magical extension.'
|
61
|
+
# c.version = '1.00'
|
62
|
+
# c.library = 'irc'
|
63
|
+
# c.author = 'noxgirl'
|
64
|
+
# c.auto = '~> 4.0'
|
65
|
+
# end
|
66
|
+
def configure
|
67
|
+
|
68
|
+
# Prepare an open structure.
|
69
|
+
conf = OpenStruct.new
|
70
|
+
|
71
|
+
# Yield it to the configuration block.
|
72
|
+
yield conf if block_given?
|
73
|
+
|
74
|
+
# Check for sufficient configuration.
|
75
|
+
[:name,:summary,:version,:library,:author,:auto].each do |s|
|
76
|
+
if conf.send(s).nil?
|
77
|
+
raise PluginError, "Plugin #{self.inspect} provided insufficient configuration (#{s} is nil)."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@name = conf.name
|
82
|
+
@summary = conf.summary
|
83
|
+
@version = conf.version
|
84
|
+
@library = conf.library
|
85
|
+
@author = conf.author
|
86
|
+
|
87
|
+
# Check for compatibility.
|
88
|
+
if conf.auto =~ /^(~>\s*)((?:[\dabcr])(?:\.[\dabcr]))+$/ # ~>
|
89
|
+
|
90
|
+
ver = $2
|
91
|
+
if Gem::Version.new(ver.dup) >= Gem::Version.new(Auto::VERSION.dup) # must be later than or equal to current
|
92
|
+
|
93
|
+
# Split current version and plugin-demanded version by '.'.
|
94
|
+
verarr = Auto::VERSION.split(/\./)
|
95
|
+
pverarr = ver.split(/\./)
|
96
|
+
|
97
|
+
# Must be no later than the current minor version
|
98
|
+
unless verarr[1] <= pverarr[1]
|
99
|
+
raise PluginError, "Plugin #@name v#@version demands Auto #{conf.auto}; current version is #{Auto::VERSION}. Incompatible! Aborting load!"
|
100
|
+
end
|
101
|
+
|
102
|
+
@auto = conf.auto
|
103
|
+
|
104
|
+
else
|
105
|
+
raise PluginError, "Plugin #@name v#@version demands Auto #{conf.auto}; current version is #{Auto::VERSION}. Incompatible! Aborting load!"
|
106
|
+
end # if ver >=
|
107
|
+
|
108
|
+
elsif conf.auto =~ /^(>=\s*)((?:[\dabcr])(?:\.[\dabcr]))+$/ # >=
|
109
|
+
|
110
|
+
ver = $2
|
111
|
+
unless ver >= Auto::VERSION # must be later than or equal to current
|
112
|
+
raise PluginError, "Plugin #@name v#@version demands Auto #{conf.auto}; current version is #{Auto::VERSION}. Incompatible! Aborting load!"
|
113
|
+
end
|
114
|
+
|
115
|
+
@auto = conf.auto
|
116
|
+
|
117
|
+
else
|
118
|
+
raise PluginError, "Plugin #@name v#@version cannot be checked for compatibility. Aborting load!"
|
119
|
+
end # compat check
|
120
|
+
|
121
|
+
# If we've made it this far, it's sufficiently compatible with the API.
|
122
|
+
# Now, we need to extend this plugin with our domain-specific language (DSL).
|
123
|
+
self.extend Auto::DSL::Base # this is the base
|
124
|
+
case @library
|
125
|
+
|
126
|
+
when 'irc'
|
127
|
+
self.extend Auto::DSL::IRC
|
128
|
+
when 'multi'
|
129
|
+
# the specifications for multilib DSL functionality are yet undecided
|
130
|
+
else
|
131
|
+
# If it's a library we don't comprehend, exception time, it is.
|
132
|
+
raise PluginError, "Plugin #@name v#@version demands '#{@library}' library, which I do not understand. Aborting load!"
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end # def configure
|
137
|
+
|
138
|
+
#########
|
139
|
+
protected
|
140
|
+
#########
|
141
|
+
|
142
|
+
# Inheritance event.
|
143
|
+
#
|
144
|
+
# @param [Class] subklass The subclass which has inherited {self}.
|
145
|
+
def self.inherited(subklass)
|
146
|
+
$m.debug("[plugin] Auto::API::Plugin inherited by #{subklass}")
|
147
|
+
end
|
148
|
+
|
149
|
+
end # class Plugin
|
150
|
+
|
151
|
+
end # module API
|
152
|
+
|
153
|
+
end # module Auto
|
154
|
+
|
155
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
# Entering namespace: Auto
|
7
|
+
module Auto
|
8
|
+
|
9
|
+
# Entering namespace: API
|
10
|
+
module API
|
11
|
+
|
12
|
+
# A simple class which provides the API a fundamental timer system, based on
|
13
|
+
# threading.
|
14
|
+
#
|
15
|
+
# @api Auto
|
16
|
+
# @since 4.0.0
|
17
|
+
# @author noxgirl
|
18
|
+
#
|
19
|
+
# @!attribute [r] timers
|
20
|
+
# @return [Hash{String => Thread}] List of threads.
|
21
|
+
#
|
22
|
+
# @see Auto::API::Helper::Timers
|
23
|
+
class Timers < Auto::API::Object
|
24
|
+
|
25
|
+
attr_reader :timers
|
26
|
+
|
27
|
+
# Create a new instance of Auto::API::Timers.
|
28
|
+
def initialize
|
29
|
+
@timers = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
# Spawn a new timer.
|
33
|
+
#
|
34
|
+
# @param [Integer] time Number of seconds before this is executed, or in between executions.
|
35
|
+
# @param [Symbol] type Either +:once+ to execute a timer once and destroy it, or +:every+
|
36
|
+
# to repeat.
|
37
|
+
# @param [Array<>] args An array of arguments which to pass to the block when executed. (splat)
|
38
|
+
#
|
39
|
+
# @yield [...] The arguments which were provided in the timer's creation.
|
40
|
+
#
|
41
|
+
# @return [String] A unique identification string representing the timer. Useful if you wish
|
42
|
+
# to terminate it in the future.
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# => timer = timers.spawn(15, :once, 'cow') { |animal| puts animal }
|
46
|
+
# # after 15 seconds...
|
47
|
+
# 'cow'
|
48
|
+
#
|
49
|
+
# @see Auto::API::Helper::Timers#clock_do
|
50
|
+
def spawn(time, type, *args, &cb)
|
51
|
+
|
52
|
+
# Generate a unique ID for this timer.
|
53
|
+
id = ''
|
54
|
+
10.times { id += get_rand_char }
|
55
|
+
while @timers.has_key? id
|
56
|
+
id = ''
|
57
|
+
10.times { id += get_rand_char }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Create a new thread containing the timer.
|
61
|
+
if type == :once
|
62
|
+
@timers[id] = Thread.new { sleep time; cb.call(*args) }
|
63
|
+
elsif type == :every
|
64
|
+
@timers[id] = Thread.new { loop { sleep time; cb.call(*args) } }
|
65
|
+
else
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
id
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Delete a timer.
|
74
|
+
#
|
75
|
+
# @param [String] id The unique identification string of the timer, as provided
|
76
|
+
# by {#spawn}.
|
77
|
+
#
|
78
|
+
# @see Auto::API::Helper::Timers#clock_stop
|
79
|
+
def del(id)
|
80
|
+
# Does the timer exist?
|
81
|
+
if @timers.has_key? id
|
82
|
+
@timers[id].kill
|
83
|
+
@timers.delete id
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end # class Timers
|
88
|
+
|
89
|
+
end # module API
|
90
|
+
|
91
|
+
end # module Auto
|
92
|
+
|
93
|
+
# vim: set ts=4 sts=2 sw=2 et:
|
data/lib/auto/bot.rb
ADDED
@@ -0,0 +1,338 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'colored'
|
5
|
+
require 'sequel'
|
6
|
+
|
7
|
+
require 'auto/config'
|
8
|
+
|
9
|
+
require 'auto/api'
|
10
|
+
|
11
|
+
# Namespace: Auto
|
12
|
+
module Auto
|
13
|
+
|
14
|
+
# This is the central class of Auto, providing all core functionality.
|
15
|
+
#
|
16
|
+
# It should be additionally noted that for each loaded core library, a readable
|
17
|
+
# instance attribute of the library's name will exist, typically pointing to
|
18
|
+
# an instance of its respective Library class. (e.g. @irc = <Auto::IRC::Library>)
|
19
|
+
#
|
20
|
+
# @!attribute [r] opts
|
21
|
+
# @return [Slop] The options object.
|
22
|
+
#
|
23
|
+
#
|
24
|
+
# @!attribute [r] log
|
25
|
+
# @return [Auto::Logger] The logging instance.
|
26
|
+
#
|
27
|
+
# @!attribute [r] conf
|
28
|
+
# @return [Auto::Config] The configuration instance.
|
29
|
+
#
|
30
|
+
# @!attribute [r] events
|
31
|
+
# @return [Auto::API::Events] The event system instance.
|
32
|
+
#
|
33
|
+
# @!attribute [r] clock
|
34
|
+
# @return [Auto::API::Timers] The timer system instance.
|
35
|
+
#
|
36
|
+
# @!attribute [r] db
|
37
|
+
# @return [Sequel::SQLite::Database] If the database is SQLite (note: all
|
38
|
+
# adapted databases are subclasses of Sequel::Database).
|
39
|
+
# @return [Sequel::MySQL::Database] If the database is MySQL (note: all
|
40
|
+
# adapted databases are subclasses of Sequel::Database).
|
41
|
+
# @return [Sequel::Postgres::Database] If the database is PostgreSQL (note: all
|
42
|
+
# adapted databases are subclasses of Sequel::Database).
|
43
|
+
#
|
44
|
+
# @!attribute [r] libs
|
45
|
+
# @return [Array<String>] List of loaded core libraries.
|
46
|
+
#
|
47
|
+
# @!attribute [r] netloop
|
48
|
+
# @return [Thread] The thread in which #main_loop is running.
|
49
|
+
#
|
50
|
+
# @!attribute [r] sockets
|
51
|
+
# @return [Array<Object>] A list of socket objects.
|
52
|
+
class Bot
|
53
|
+
|
54
|
+
attr_reader :opts, :log, :conf, :events, :clock, :db, :libs, :netloop, :sockets
|
55
|
+
|
56
|
+
# Create a new instance of Auto.
|
57
|
+
#
|
58
|
+
# @param [Hash{String => Object}] opts A hash of options.
|
59
|
+
def initialize opts
|
60
|
+
# Save options.
|
61
|
+
@opts = opts
|
62
|
+
|
63
|
+
# Move to ~/.config/autobot if we're a gem.
|
64
|
+
if Auto.gem?
|
65
|
+
Dir.mkdir File.join(Dir.home, '.config') if !Dir.exists? File.join(Dir.home, '.config')
|
66
|
+
Dir.mkdir File.join(Dir.home, '.config', 'autobot') if !Dir.exists? File.join(Dir.home, '.config', 'autobot')
|
67
|
+
Dir.chdir File.join(Dir.home, '.config', 'autobot')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Initialize this instance.
|
72
|
+
def init
|
73
|
+
|
74
|
+
# Before anything else, start logging.
|
75
|
+
puts '* Starting logging...'.bold
|
76
|
+
@log = Auto::Logger.new
|
77
|
+
@log.info("Logging started at #{Time.now}")
|
78
|
+
|
79
|
+
# Load configuration
|
80
|
+
load_config
|
81
|
+
|
82
|
+
# Initialize the central event system
|
83
|
+
puts '* Starting the central event system...'.bold
|
84
|
+
@log.info("Starting the central event system...")
|
85
|
+
@events = Auto::API::Events.new
|
86
|
+
|
87
|
+
# Start the timer system.
|
88
|
+
puts '* Starting the timer system...'.bold
|
89
|
+
@log.info("Starting the timer system...")
|
90
|
+
@clock = Auto::API::Timers.new
|
91
|
+
|
92
|
+
# Prepare for sockets.
|
93
|
+
@sockets = []
|
94
|
+
|
95
|
+
# Initialize the database
|
96
|
+
load_database
|
97
|
+
|
98
|
+
# Load core libraries.
|
99
|
+
load_libraries
|
100
|
+
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
104
|
+
# Start the bot.
|
105
|
+
def start
|
106
|
+
|
107
|
+
# Call the start event.
|
108
|
+
@events.call :start
|
109
|
+
|
110
|
+
# Throw the program into the main loop.
|
111
|
+
@events.threads.each { |thr| thr.join } # block until we're ready to go
|
112
|
+
debug("Producing a thread and entering the main loop...") if @opts.verbose?
|
113
|
+
@netloop = Thread.new { main_loop }
|
114
|
+
@netloop.join
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
# Main loop.
|
119
|
+
def main_loop
|
120
|
+
loop do
|
121
|
+
# Build a list of sockets.
|
122
|
+
sockets = []
|
123
|
+
assoc_objects = {}
|
124
|
+
@sockets.each do |o|
|
125
|
+
unless o.socket.nil? or o.socket.closed?
|
126
|
+
sockets << o.socket
|
127
|
+
assoc_objects[o.socket] = o
|
128
|
+
end
|
129
|
+
end
|
130
|
+
next if sockets.empty?
|
131
|
+
|
132
|
+
# Call #select.
|
133
|
+
ready_read, _, _ = IO.select(sockets, [], [], nil)
|
134
|
+
|
135
|
+
# Iterate through sockets ready for reading.
|
136
|
+
ready_read.each do |socket|
|
137
|
+
@events.call :net_receive, assoc_objects[socket]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Produce an error message.
|
143
|
+
#
|
144
|
+
# @param [String] msg The message.
|
145
|
+
# @param [true, false] fatal Whether this error is fatal (will kill the program).
|
146
|
+
# @param [Array<String>] bt Backtrace.
|
147
|
+
def error msg, fatal = false, bt = nil
|
148
|
+
# Print it to STDERR.
|
149
|
+
STDERR.puts "ERROR: #{msg}".red
|
150
|
+
unless bt.nil?
|
151
|
+
STDERR.puts "Backtrace:"
|
152
|
+
STDERR.puts bt
|
153
|
+
end
|
154
|
+
|
155
|
+
# Log it.
|
156
|
+
@log.error(msg)
|
157
|
+
|
158
|
+
if fatal
|
159
|
+
#@netloop.kill if @netloop.active
|
160
|
+
exit 1
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Produce a warning message.
|
165
|
+
#
|
166
|
+
# @param [String] msg The message.
|
167
|
+
def warn msg
|
168
|
+
# Log it.
|
169
|
+
@log.warning(msg)
|
170
|
+
|
171
|
+
# Foreground it.
|
172
|
+
foreground("Warning: #{msg}".red, false)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Produce information.
|
176
|
+
#
|
177
|
+
# @param [String] msg The message.
|
178
|
+
def info msg
|
179
|
+
@log.info(msg)
|
180
|
+
foreground(">>> #{msg}".green, false)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Produce a message for foreground mode.
|
184
|
+
#
|
185
|
+
# @param [String] msg The message.
|
186
|
+
# @param [true, false] log Whether to log it as well as print to STDOUT.
|
187
|
+
def foreground msg, log = true
|
188
|
+
if @opts.foreground?
|
189
|
+
puts "[F] #{msg}"
|
190
|
+
@log.info("[F] #{msg}") if log
|
191
|
+
else
|
192
|
+
if @opts.debug?
|
193
|
+
debug(msg, log)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Produce a debug message.
|
199
|
+
#
|
200
|
+
# @param [String] msg The message.
|
201
|
+
# @param [true, false] log Whether to log it as well as print to STDOUT.
|
202
|
+
def debug msg, log = false
|
203
|
+
if @opts.debug?
|
204
|
+
puts "[D] #{msg}".blue
|
205
|
+
@log.debug(msg) if log
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Terminate the bot.
|
210
|
+
#
|
211
|
+
# @param [String] reason The reason for termination.
|
212
|
+
def terminate reason = 'Terminating'
|
213
|
+
info("Auto is terminating owing to thus: #{reason}")
|
214
|
+
|
215
|
+
# Call :die
|
216
|
+
@events.call :die, reason
|
217
|
+
|
218
|
+
# Close the database.
|
219
|
+
@db.disconnect
|
220
|
+
|
221
|
+
# When dying, allow about three seconds for hooks to execute before
|
222
|
+
# fully terminating.
|
223
|
+
sleep 3
|
224
|
+
|
225
|
+
# Delete auto.pid
|
226
|
+
unless @opts.debug? or @opts.foreground?
|
227
|
+
File.delete('auto.pid')
|
228
|
+
end
|
229
|
+
|
230
|
+
exit 0
|
231
|
+
end
|
232
|
+
|
233
|
+
#######
|
234
|
+
private
|
235
|
+
#######
|
236
|
+
|
237
|
+
# Load the configuration.
|
238
|
+
def load_config
|
239
|
+
|
240
|
+
# Try to find the file
|
241
|
+
# conf/ is given precedence over ~/.config/autobot/
|
242
|
+
# unless we're installed as a gem, in which case conf/ is ignored
|
243
|
+
confpath = nil
|
244
|
+
if @opts.json?
|
245
|
+
if File.exists? File.join(%w[conf auto.json]) and !Auto.gem?
|
246
|
+
confpath = File.join(%w[conf auto.json])
|
247
|
+
elsif File.exists? File.join(Dir.home, '.config', 'autobot', 'auto.json')
|
248
|
+
confpath = File.join(Dir.home, '.config', 'autobot', 'auto.json')
|
249
|
+
end
|
250
|
+
else
|
251
|
+
if File.exists? File.join(%w[conf auto.yml]) and !Auto.gem?
|
252
|
+
confpath = File.join(%w[conf auto.yml])
|
253
|
+
elsif File.exists? File.join(Dir.home, '.config', 'autobot', 'auto.yml')
|
254
|
+
confpath = File.join(Dir.home, '.config', 'autobot', 'auto.yml')
|
255
|
+
end
|
256
|
+
end
|
257
|
+
confpath = @opts[:conf] if @opts.conf? # --conf=FILE has supreme precedence
|
258
|
+
error('Could not find a configuration file', true) if confpath.nil?
|
259
|
+
|
260
|
+
# Process it.
|
261
|
+
puts "* Reading the configuration file #{confpath}...".bold
|
262
|
+
@log.info("Reading the configuration file #{confpath}...")
|
263
|
+
@conf = Auto::Config.new(File.expand_path(confpath))
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
# Load Auto libraries.
|
268
|
+
def load_libraries
|
269
|
+
|
270
|
+
puts '* Loading core libraries..'.bold
|
271
|
+
@log.info("Loading core libraries...")
|
272
|
+
@libs = []
|
273
|
+
|
274
|
+
# Iterate through each configured library.
|
275
|
+
@conf['libraries'].each do |lib|
|
276
|
+
lib.dc!
|
277
|
+
|
278
|
+
if @libs.include? lib
|
279
|
+
# Don't load a library more than once!
|
280
|
+
error("Cannot load library twice (#{lib})! Please fix your configuration.")
|
281
|
+
next
|
282
|
+
end
|
283
|
+
|
284
|
+
begin
|
285
|
+
# here is where magic occurs to load a library
|
286
|
+
require "auto/#{lib}"
|
287
|
+
instance_variable_set "@#{lib}".to_sym, Object.const_get("LIBRARY_#{lib.uc}")
|
288
|
+
define_singleton_method(lib.to_sym) { self.instance_variable_get("@#{__method__}".to_sym) }
|
289
|
+
@libs.push lib
|
290
|
+
rescue => e
|
291
|
+
error "Failed to load core library '#{lib}': #{e}", true, e.backtrace
|
292
|
+
end
|
293
|
+
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
# Load database.
|
299
|
+
def load_database
|
300
|
+
|
301
|
+
puts '* Initializing database...'.bold
|
302
|
+
@log.info('Initializing database...')
|
303
|
+
@db = nil
|
304
|
+
|
305
|
+
case @conf['database']['type'] # check the database type in the config
|
306
|
+
|
307
|
+
when 'sqlite' # it's SQLite
|
308
|
+
|
309
|
+
name = @conf['database']['name'] || 'auto.db'
|
310
|
+
@db = Sequel.sqlite(name)
|
311
|
+
|
312
|
+
when 'mysql', 'postgres' # for MySQL and Postgres
|
313
|
+
|
314
|
+
%[username password hostname name].each do |d|
|
315
|
+
unless @conf['database'].include? d
|
316
|
+
raise DatabaseError, "Insufficient configuration. For MySQL and PostgreSQL, we need the username, password, hostname, and name directives."
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
adapter = @conf['database']['type'].to_sym
|
321
|
+
|
322
|
+
@db = Sequel.connect(:adapter => adapter,
|
323
|
+
:host => @conf['database']['hostname'],
|
324
|
+
:database => @conf['database']['name'],
|
325
|
+
:user => @conf['database']['username'],
|
326
|
+
:password => @conf['database']['passname'])
|
327
|
+
|
328
|
+
else
|
329
|
+
raise DatabaseError, "Unrecognized database type: #{@conf['database']['type']}"
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
end # class Bot
|
335
|
+
|
336
|
+
end # module Auto
|
337
|
+
|
338
|
+
# vim: set ts=4 sts=2 sw=2 et:
|