syndi 0.1.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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +0 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +28 -0
  6. data/README.md +104 -0
  7. data/Rakefile +30 -0
  8. data/WINDOWS.md +64 -0
  9. data/bin/syndi +102 -0
  10. data/bin/syndi-conf +47 -0
  11. data/conf/example.yml +101 -0
  12. data/docs/Events.md +103 -0
  13. data/docs/Upgrade.md +16 -0
  14. data/ext/csyndi/events.c +50 -0
  15. data/ext/csyndi/extconf.rb +20 -0
  16. data/ext/csyndi/integer.c +53 -0
  17. data/ext/csyndi/libauto.c +37 -0
  18. data/ext/csyndi/logger.c +229 -0
  19. data/include/syndi.h +22 -0
  20. data/include/syndi/csyndi.h +38 -0
  21. data/include/syndi/events.h +19 -0
  22. data/include/syndi/integer.h +17 -0
  23. data/include/syndi/logger.h +56 -0
  24. data/lib/csyndi.so +0 -0
  25. data/lib/syndi.rb +137 -0
  26. data/lib/syndi/actress.rb +12 -0
  27. data/lib/syndi/api.rb +7 -0
  28. data/lib/syndi/api/object.rb +29 -0
  29. data/lib/syndi/bot.rb +266 -0
  30. data/lib/syndi/config.rb +113 -0
  31. data/lib/syndi/dsl/base.rb +74 -0
  32. data/lib/syndi/dsl/irc.rb +13 -0
  33. data/lib/syndi/events.rb +130 -0
  34. data/lib/syndi/irc.rb +8 -0
  35. data/lib/syndi/irc/common.rb +63 -0
  36. data/lib/syndi/irc/library.rb +89 -0
  37. data/lib/syndi/irc/object/channel.rb +21 -0
  38. data/lib/syndi/irc/object/entity.rb +90 -0
  39. data/lib/syndi/irc/object/message.rb +99 -0
  40. data/lib/syndi/irc/object/user.rb +139 -0
  41. data/lib/syndi/irc/protocol.rb +161 -0
  42. data/lib/syndi/irc/protocol/numerics.rb +60 -0
  43. data/lib/syndi/irc/sasl/diffie_hellman.rb +36 -0
  44. data/lib/syndi/irc/sasl/mech.rb +7 -0
  45. data/lib/syndi/irc/sasl/mech/dh_blowfish.rb +83 -0
  46. data/lib/syndi/irc/sasl/mech/plain.rb +39 -0
  47. data/lib/syndi/irc/server.rb +301 -0
  48. data/lib/syndi/irc/state/channel_manager.rb +6 -0
  49. data/lib/syndi/irc/state/support.rb +142 -0
  50. data/lib/syndi/irc/state/user_manager.rb +6 -0
  51. data/lib/syndi/irc/std/commands.rb +99 -0
  52. data/lib/syndi/irc/std/numerics.rb +216 -0
  53. data/lib/syndi/jewel.rb +5 -0
  54. data/lib/syndi/jewel/specification.rb +121 -0
  55. data/lib/syndi/jewel/util.rb +27 -0
  56. data/lib/syndi/rubyext/string.rb +10 -0
  57. data/lib/syndi/verbosity.rb +10 -0
  58. data/lib/syndi/version.rb +38 -0
  59. data/spec/helper.rb +37 -0
  60. data/spec/syndi/events_spec.rb +89 -0
  61. data/tasks/compile.rake +15 -0
  62. data/tasks/install.rake +10 -0
  63. data/tasks/package.rake +13 -0
  64. data/tasks/spec.rake +12 -0
  65. metadata +243 -0
data/include/syndi.h ADDED
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright (c) 2013, Autumn Perrault, et al.
3
+ * All rights reserved.
4
+ * This free software is distributed under the FreeBSD license (see LICENSE).
5
+ *
6
+ */
7
+
8
+ #ifndef SYNDI_H
9
+ #define SYNDI_H
10
+
11
+ #include <stdio.h>
12
+ #include "ruby.h"
13
+
14
+ /* include csyndi headers */
15
+ #include "syndi/csyndi.h"
16
+ #include "syndi/logger.h"
17
+ #include "syndi/events.h"
18
+ #include "syndi/integer.h"
19
+
20
+ #endif // SYNDI_H
21
+
22
+ /* vim: set ts=4 sts=4 sw=4 et cindent: */
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Copyright (c) 2013, Autumn Perrault, et al.
3
+ * All rights reserved.
4
+ * This free software is distributed under the FreeBSD license (see LICENSE).
5
+ *
6
+ */
7
+
8
+ #ifndef __LIBSYNDI_H__
9
+ #define __LIBSYNDI_H__
10
+
11
+ /* SYM(x) returns rb_intern(x) */
12
+ #define SYM(str) rb_intern(#str)
13
+
14
+ /* variable for the Syndi module */
15
+ extern VALUE mSyndi;
16
+
17
+ /* Init_csyndi prototype */
18
+ void Init_csyndi();
19
+
20
+ /* Syndi's exceptions */
21
+ extern VALUE eLogError;
22
+ extern VALUE eConfigError;
23
+ extern VALUE eDatabaseError;
24
+ extern VALUE ePluginError;
25
+
26
+ /* line ending */
27
+ #ifdef _WIN32
28
+ #define OS_LINE_TERM "\r\n"
29
+ #else
30
+ #define OS_LINE_TERM "\n"
31
+ #endif
32
+
33
+ /* fetch the Syndi directory */
34
+ #define SYNDI_DIR RSTRING_PTR(rb_funcall(mSyndi, SYM(dir), 0))
35
+
36
+ #endif
37
+
38
+ /* vim: set ts=4 sts=4 sw=4 et cindent: */
@@ -0,0 +1,19 @@
1
+ /*
2
+ * Copyright (c) 2013, Autumn Perrault, et al.
3
+ * All rights reserved.
4
+ * This free software is distributed under the FreeBSD license (see LICENSE).
5
+ *
6
+ */
7
+
8
+ #ifndef WIN32
9
+ #include <pthread.h>
10
+ #endif
11
+
12
+ extern VALUE cEvents;
13
+
14
+ static VALUE events_initialize(VALUE self);
15
+ static VALUE events_on(VALUE self, VALUE event, VALUE rbpriority, VALUE prc);
16
+
17
+ static void init_events();
18
+
19
+ /* vim: set ts=4 sts=4 sw=4 et cindent: */
@@ -0,0 +1,17 @@
1
+ /*
2
+ * Copyright (c) 2013, Autumn Perrault, et al.
3
+ * All rights reserved.
4
+ * This free software is distributed under the FreeBSD license (see LICENSE).
5
+ *
6
+ */
7
+
8
+ #ifndef __SYNDI_INTEGER_H__
9
+ #define __SYNDI_INTEGER_H__
10
+
11
+ void init_integer();
12
+ VALUE swar_bits_set(VALUE self);
13
+ VALUE modular_power(VALUE self, VALUE exponent, VALUE modulus);
14
+
15
+ #endif
16
+
17
+ /* vim: set ts=4 sts=4 sw=4 et cindent: */
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Copyright (c) 2013, Autumn Perrault, et al.
3
+ * All rights reserved.
4
+ * This free software is distributed under the FreeBSD license (see LICENSE).
5
+ *
6
+ */
7
+
8
+ #ifndef __SYNDI_LOGGER_H__
9
+ #define __SYNDI_LOGGER_H__
10
+
11
+ #include <errno.h>
12
+ #include <sys/stat.h>
13
+ #include <string.h>
14
+ #include <time.h>
15
+ #ifndef _WIN32
16
+ #include <unistd.h>
17
+ #endif
18
+
19
+ /* Set format based on target OS. */
20
+ #ifdef _WIN32
21
+ #define LOG_FILE_FORMAT "logs\\%Y%m%d.log"
22
+ #else
23
+ #define LOG_FILE_FORMAT "logs/%Y%m%d.log"
24
+ #endif
25
+
26
+ /* This is based on the desired output give or take a few characters. */
27
+ #define MAX_TIME_STRING_LENGTH 18
28
+
29
+ /* ("YYYY-MM-DD HH:MM:SS +ZZZZ") "YEAR-MONTH-DAY HOUR:MINUTES:SECONDS UTC_OFFSET" */
30
+ #define LOG_TIME_FORMAT_LENGTH 25
31
+
32
+ extern VALUE cLogger;
33
+ void init_syndi_logger();
34
+
35
+ /* Ruby-accessible methods */
36
+ VALUE logger_init(VALUE self);
37
+
38
+ VALUE logger_fatal(VALUE self, VALUE message);
39
+ VALUE logger_error(int argc, VALUE *argv, VALUE message);
40
+ VALUE logger_verbose(VALUE self, VALUE message, VALUE level);
41
+ VALUE logger_warning(VALUE self, VALUE message);
42
+ VALUE logger_info(VALUE self, VALUE message);
43
+
44
+ /* internal functions */
45
+ static void log_out2scrn(int type, const char *message, int level);
46
+ static void log_out2file(const char *type, const char *message);
47
+ static void log_dircheck();
48
+
49
+ /* symbolic constants for event types */
50
+ #define TYPE_FATAL 0
51
+ #define TYPE_ERROR 1
52
+ #define TYPE_WARNING 2
53
+ #define TYPE_INFO 3
54
+ #define TYPE_DEBUG 4
55
+
56
+ #endif
data/lib/csyndi.so ADDED
Binary file
data/lib/syndi.rb ADDED
@@ -0,0 +1,137 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'syndi/rubyext/string'
5
+ require 'syndi/version'
6
+ require 'fileutils'
7
+
8
+ module Syndi
9
+ extend self
10
+
11
+ # @return [Boolean] Whether we're installed as a gem.
12
+ def gem?
13
+ begin
14
+ # If we already checked, just return the result of that.
15
+ return @gem if defined? @gem
16
+
17
+ # Otherwise, check.
18
+ result = Gem.path.each do |gempath|
19
+ break true if __FILE__ =~ /^#{Regexp.escape gempath}/
20
+ end
21
+ @gem = (result == true ? true : false)
22
+ ensure
23
+ @gem ||= false
24
+ end
25
+ end
26
+
27
+ # @return [Boolean] Whether we're running on Microsoft Windows.
28
+ def windows?
29
+ begin
30
+ return @windows if defined? @windows
31
+ if ::RbConfig::CONFIG['host_os'] =~ /bccwin|djgpp|mswin|mingw|cygwin|wince/i
32
+ @windows = true
33
+ else
34
+ @windows = false
35
+ end
36
+ ensure
37
+ @windows ||= false
38
+ end
39
+ end
40
+
41
+ # This fixes coloring issues on Windows--or at least, the ones with which we
42
+ # need be concerned.
43
+ def windows_colored
44
+ colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white]
45
+ extras = [:clear, :bold, :underline, :reversed]
46
+
47
+ colors.each do |col|
48
+ String.send(:define_method, col, proc { self })
49
+ end
50
+ extras.each do |extr|
51
+ String.send(:define_method, extr, proc { self })
52
+ end
53
+ end
54
+
55
+ ###################
56
+ # central methods #
57
+ ###################
58
+
59
+ # Retrieve the application data directory.
60
+ #
61
+ # @return [String]
62
+ def dir
63
+ @app_dir ||= File.join ENV['HOME'], '.syndi'
64
+ end
65
+
66
+ # Set the application data directory.
67
+ def dir= directory
68
+ FileUtils.mkdir_p directory unless Dir.exists? directory
69
+ Dir.chdir directory
70
+ @app_dir = directory
71
+ end
72
+
73
+ # Initiate Syndi with command-line +options+.
74
+ #
75
+ # @param [Slop] options The command-line options.
76
+ def go options
77
+
78
+ end
79
+
80
+ # Logger access.
81
+ #
82
+ # @return [Syndi::Logger]
83
+ def log
84
+ @logger ||= Syndi::Logger.new
85
+ end
86
+
87
+ # Central event system access.
88
+ #
89
+ # @return [Syndi::Events]
90
+ def events
91
+ @event_manager ||= Syndi::Events.new
92
+ end
93
+
94
+ # Configuration access.
95
+ #
96
+ # @return [Syndi::Config]
97
+ def conf
98
+ @configuration ||= Syndi::Config.new
99
+ end
100
+
101
+ # Central Syndi Celluloid actor.
102
+ #
103
+ # @return [Syndi::Actress] Subclass of {Celluloid::Actor}.
104
+ def actress
105
+ @actress ||= Syndi::Actress.new
106
+ end
107
+
108
+ # Execute some code after the given interval.
109
+ #
110
+ # @param [Integer] interval
111
+ #
112
+ # @return [Celluloid::Timer]
113
+ def after interval, &prc
114
+ actress.after interval, &prc
115
+ end
116
+
117
+ # Execute some code every +interval+.
118
+ #
119
+ # @param [Integer] interval
120
+ #
121
+ # @return [Celluloid::Timer]
122
+ def every interval, &prc
123
+ actress.every interval, &prc
124
+ end
125
+
126
+ end
127
+
128
+ if Syndi.windows?
129
+ Syndi.windows_colored
130
+ else
131
+ require 'colored'
132
+ end
133
+
134
+ require 'csyndi'
135
+ %w[events actress config bot].each { |lib| require "syndi/#{lib}" }
136
+
137
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'celluloid'
5
+
6
+ module Syndi
7
+ class Actress
8
+ include Celluloid
9
+ end
10
+ end
11
+
12
+ # vim: set ts=4 sts=2 sw=2 et:
data/lib/syndi/api.rb ADDED
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'syndi/api/events'
5
+ require 'syndi/api/timers'
6
+
7
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -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 (see LICENSE).
3
+
4
+ # Namespace: Syndi
5
+ module Syndi
6
+
7
+ # Namespace: API
8
+ module API
9
+
10
+ # A superclass for {Syndi::API::Timers} and {Syndi::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 Syndi
28
+
29
+ # vim: set ts=4 sts=2 sw=2 et:
data/lib/syndi/bot.rb ADDED
@@ -0,0 +1,266 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'redis'
5
+
6
+ # Namespace: Syndi
7
+ module Syndi
8
+
9
+ # This is the central class of Syndi, providing all core functionality.
10
+ #
11
+ # It should be additionally noted that for each loaded core library, a readable
12
+ # instance attribute of the library's name will exist, typically pointing to
13
+ # an instance of its respective Library class. (e.g. @irc = <Syndi::IRC::Library>)
14
+ #
15
+ # @!attribute [r] opts
16
+ # @return [Slop] The options object.
17
+ #
18
+ #
19
+ # @!attribute [r] log
20
+ # @return [Syndi::Logger] The logging instance.
21
+ #
22
+ # @!attribute [r] conf
23
+ # @return [Syndi::Config] The configuration instance.
24
+ #
25
+ # @!attribute [r] events
26
+ # @return [Syndi::API::Events] The event system instance.
27
+ #
28
+ # @!attribute [r] clock
29
+ # @return [Syndi::API::Timers] The timer system instance.
30
+ #
31
+ # @!attribute [r] db
32
+ # @return [Redis] The Redis database.
33
+ #
34
+ # @!attribute [r] libs
35
+ # @return [Array<String>] List of loaded core libraries.
36
+ #
37
+ # @!attribute [r] netloop
38
+ # @return [Thread] The thread in which #main_loop is running.
39
+ #
40
+ # @!attribute [r] sockets
41
+ # @return [Array<Object>] A list of socket objects.
42
+ class Bot
43
+
44
+ attr_reader :opts, :log, :conf, :events, :clock, :db, :libs,
45
+ :netloop, :sockets
46
+
47
+ # Create a new instance of Syndi.
48
+ #
49
+ # @param [Hash{String => Object}] opts A hash of options.
50
+ def initialize opts
51
+ # Save options.
52
+ @opts = opts
53
+ end
54
+
55
+ # Initialize this instance.
56
+ def init
57
+
58
+ # Load configuration
59
+ load_config
60
+
61
+ # Initialize the central event system
62
+ @events = Syndi::API::Events.new
63
+
64
+ # Start the timer system.
65
+ @clock = Syndi::API::Timers.new
66
+
67
+ # Prepare for sockets.
68
+ @sockets = []
69
+
70
+ # Initialize the database
71
+ @db = load_database
72
+
73
+ # Load core libraries.
74
+ load_libraries
75
+
76
+ true
77
+ end
78
+
79
+ # Start the bot.
80
+ def start
81
+
82
+ # Call the start event.
83
+ @events.call :start
84
+
85
+ # Throw the program into the main loop.
86
+ @events.threads.each { |thr| thr.join } # block until we're ready to go
87
+ $log.verbose("Producing a thread and entering the main loop...", VUSEFUL) do
88
+ @netloop = Thread.new { main_loop }
89
+ @netloop.join
90
+ end
91
+
92
+ end
93
+
94
+ # Daemonize the bot.
95
+ def daemonize
96
+ $log.info "Forking into the background. . . ."
97
+
98
+ # Direct all incoming data on STDIN and outgoing data on STDOUT/STDERR to /dev/null.
99
+ $stdin = File.open '/dev/null'
100
+ $stdout = $stderr = File.open '/dev/null', 'w'
101
+
102
+ # Fork and retrieve the PID.
103
+ pid = fork
104
+
105
+ # Save it to syndi.pid.
106
+ unless pid.nil?
107
+ File.open('syndi.pid', 'w') { |io| io.puts pid }
108
+ exit 0
109
+ end
110
+ end
111
+
112
+ # Main loop.
113
+ def main_loop
114
+ loop do
115
+ # Build a list of sockets.
116
+ sockets = []
117
+ assoc_objects = {}
118
+ @sockets.each do |o|
119
+ unless o.socket.nil? or o.socket.closed?
120
+ sockets << o.socket
121
+ assoc_objects[o.socket] = o
122
+ end
123
+ end
124
+ next if sockets.empty?
125
+
126
+ # Call #select.
127
+ ready_read, _, _ = IO.select(sockets, [], [], nil)
128
+
129
+ # Iterate through sockets ready for reading.
130
+ ready_read.each do |socket|
131
+ @events.call :net_receive, assoc_objects[socket]
132
+ end
133
+ end
134
+ end
135
+
136
+ # Terminate the bot.
137
+ #
138
+ # @param [String] reason The reason for termination.
139
+ def terminate reason = 'Terminating'
140
+ info "Syndi is terminating owing to thus: #{reason}"
141
+
142
+ # Call :die
143
+ @events.call :die, reason
144
+
145
+ # Close the database.
146
+ @db.disconnect
147
+
148
+ # When dying, allow about three seconds for hooks to execute before
149
+ # fully terminating.
150
+ sleep 3
151
+
152
+ # Delete syndi.pid
153
+ File.delete 'syndi.pid' unless @opts.foreground?
154
+
155
+ exit 0
156
+ end
157
+
158
+ #######
159
+ private
160
+ #######
161
+
162
+ # Load the configuration.
163
+ def load_config
164
+
165
+ # Try to find the file
166
+ # if we're a gem, we'll try ~/.syndi/syndi.yml
167
+ # else we'll try ./conf/syndi.yml
168
+ confpath = nil
169
+ if Syndi.gem?
170
+ confpath = File.join(SYNDI_DIR, 'syndi.yml')
171
+ else
172
+ confpath = File.join('conf', 'syndi.yml')
173
+ end
174
+ confpath = @opts[:conf] if @opts.conf? # --conf=FILE has supreme precedence
175
+
176
+ $log.info "Reading the configuration file #{confpath}..."
177
+ @conf = Syndi::Config.new File.expand_path(confpath)
178
+
179
+ end
180
+
181
+ # Load Syndi libraries.
182
+ def load_libraries
183
+
184
+ $log.info 'Loading core libraries...'
185
+ @libs = []
186
+
187
+ # Iterate through each configured library.
188
+ @conf['libraries'].each do |lib|
189
+ lib.dc!
190
+
191
+ if @libs.include? lib
192
+ # Don't load a library more than once!
193
+ $log.error "Cannot load library twice (#{lib})! Please fix your configuration."
194
+ next
195
+ end
196
+
197
+ begin
198
+ load_library lib
199
+ @libs.push lib
200
+ rescue => e
201
+ $log.error_bt "Failed to load core library '#{lib}': #{e}", e.backtrace
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
208
+ # Load a core library.
209
+ def load_library lib
210
+ # here is where magic occurs to load a library
211
+ require "syndi/#{lib}"
212
+ instance_variable_set "@#{lib}".to_sym, Object.const_get("LIBRARY_#{lib.uc}")
213
+ define_singleton_method(lib.to_sym) { self.instance_variable_get("@#{__method__}".to_sym) }
214
+ end
215
+
216
+ # Load the Redis database.
217
+ def load_database
218
+
219
+ driver = @conf['database']['driver'] || 'redis'
220
+
221
+ case driver
222
+ when 'redis'
223
+ load_db_redis
224
+ when 'flatfile'
225
+ load_db_flatfile
226
+ end
227
+
228
+ end
229
+
230
+ # Initializes Redis.
231
+ def load_db_redis
232
+
233
+ config = Hash.new
234
+ if host = @conf['database']['address']
235
+ config[:host] = host
236
+ end
237
+ if port = @conf['database']['port']
238
+ config[:port] = port
239
+ end
240
+ if path = @conf['database']['path']
241
+ config[:path] = path
242
+ end
243
+
244
+ redis = Redis.new config
245
+
246
+ if passwd = @conf['database']['password']
247
+ redis.auth passwd
248
+ end
249
+ if id = @conf['database']['number']
250
+ redis.select id
251
+ end
252
+
253
+ redis
254
+
255
+ end
256
+
257
+ # Initializes Flatfile.
258
+ def load_db_flatfile
259
+ FileKV.new 'syndi.db'
260
+ end
261
+
262
+ end # class Bot
263
+
264
+ end # module Syndi
265
+
266
+ # vim: set ts=4 sts=2 sw=2 et: