duwanis-aide 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,82 @@
1
+ = Aide - What can Jabber do for you?
2
+
3
+ http://github.com/duwanis/aide
4
+
5
+ == DESCRIPTION:
6
+
7
+ Aide is a DSL for defining and running Jabber bots.
8
+ In order to have a good grasp on how to use Aide, you'll want to familiarize
9
+ yourself with two classes: Aide::Dsl (used to actually read the Aide DSL), and
10
+ Aide::ActionContext (blocks defined using the DSL get executed in the scope of
11
+ an ActionContext). More thorough documentation will be coming soon...
12
+
13
+ == FEATURES/PROBLEMS:
14
+
15
+ It doesn't do anything yet. This is the main problem, really.
16
+
17
+ == EXAMPLE:
18
+
19
+ Check out example.aide in the source. If you install the gem
20
+ (see INSTALL below), you should be able to do the following to
21
+ see the results of parsing the file (or any other file matching
22
+ the DSL):
23
+ aide example.aide
24
+
25
+
26
+ == REQUIREMENTS:
27
+
28
+ Patience.
29
+
30
+
31
+ == INSTALL:
32
+
33
+ sudo gem install duwanis-aide --source=http://gems.github.com
34
+
35
+ == ROADMAP:
36
+
37
+ Here's the plan:
38
+ - 0.1 - Functionality in place to create a very simple bot. Can take in any
39
+ number of static commands, but nothing fancy.
40
+ - 0.2 - Help/describe functionality built in by default.
41
+ - 0.3 - Dynamic commands.
42
+ - 0.4 - Ability to subscribe to an XMPP Publisher (XEP-0060)
43
+ - 0.5 - Scheduled tasks (e.g. "execute this code every minute")
44
+ - 0.6 - Ability to act as an XMPP Publisher (XEP-0060)
45
+ - 0.7 - Command-line interface for publishing events (e.g. aide-publish [botname] [event])
46
+ - 0.8 - ?????
47
+ - 0.9 - Profit!
48
+ - 1.0 - Fully functional 1.0 release.
49
+
50
+ == HISTORY:
51
+
52
+ - 0.0.4 - Beginnings of xmpp4r integration.
53
+ - 0.0.2 - Bot class started, DSL is done in a sane way (no more creating kernel
54
+ methods, woo)
55
+ - 0.0.1 - Proof of concept. DSL is somewhat spec'd out, and you
56
+ are now capable of parsing files (albeit crudely).
57
+ - 0.0.0 - A new project is born!
58
+
59
+ == LICENSE:
60
+
61
+ (The MIT License)
62
+
63
+ Copyright (c) 2008 Tommy Morgan
64
+
65
+ Permission is hereby granted, free of charge, to any person obtaining
66
+ a copy of this software and associated documentation files (the
67
+ 'Software'), to deal in the Software without restriction, including
68
+ without limitation the rights to use, copy, modify, merge, publish,
69
+ distribute, sublicense, and/or sell copies of the Software, and to
70
+ permit persons to whom the Software is furnished to do so, subject to
71
+ the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be
74
+ included in all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
77
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
80
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
81
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
82
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'aide'
4
+ require 'daemons'
5
+
6
+ filepath = Pathname.new(ARGV[0]).cleanpath.to_s
7
+
8
+ bot = Aide::Bot.new
9
+ dsl = Aide::Dsl.new(bot, filepath)
10
+
11
+ #become a daemon
12
+ Daemons.daemonize
13
+
14
+ bot.login
@@ -0,0 +1,15 @@
1
+ # Just to make our requires at the top of the gem a little easier.
2
+ require 'pathname'
3
+ $:.unshift(Pathname(__FILE__).dirname.expand_path)
4
+
5
+ require 'rubygems'
6
+ require 'xmpp4r'
7
+ require 'logger'
8
+
9
+
10
+ require 'aide/logging'
11
+ require 'aide/errors'
12
+ require 'aide/actioncontext'
13
+ require 'aide/bot'
14
+ require 'aide/dsl'
15
+ require 'aide/version'
@@ -0,0 +1,49 @@
1
+ module Aide
2
+ ##
3
+ # This class is used to provide an execution context for processing messages.
4
+ # It houses all the attributes and helper methods that are to be exposed to
5
+ # the blocks that are passed in to Aide via the Aide::Dsl.with method.
6
+ # These contexts are created and destroyed on a per-message basis, so all
7
+ # attribute values refer to the message that is currently being processed.
8
+ #
9
+ class ActionContext
10
+ #String - The Jabber address from which the message was sent.
11
+ attr_accessor :from
12
+ #String - The Jabber address to which the message was sent.
13
+ attr_accessor :to
14
+ #String - The text of the message, minus the portion that was
15
+ #matched by the bot.
16
+ attr_accessor :message
17
+ #String - The full text of the message, including the portion that
18
+ #was matched by the bot.
19
+ attr_accessor :text
20
+ #Symbol - The type of the message (e.g. :chat, :group, etc.)
21
+ attr_accessor :type
22
+ #Aide::Bot - The bot instance that received and is acting on this
23
+ #message.
24
+ attr_accessor :bot
25
+
26
+ ##
27
+ # Sends a message back to the jabber id that sent the original message.
28
+ #=== Parameters:
29
+ # - *msg*: String. The text of the message to send.
30
+ #
31
+ def respond(msg)
32
+ send(msg, from)
33
+ end
34
+
35
+ ##
36
+ # Sends a message to the specified jabber id.
37
+ #=== Parameters:
38
+ # - *msg*: String. The text of the message to send.
39
+ # - *rec*: String. The jabber id to send the message to.
40
+ #
41
+ def send(msg, rec)
42
+ response = Jabber::Message.new(rec, msg)
43
+ response.type = :chat
44
+
45
+ bot.send(response)
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,198 @@
1
+ module Aide
2
+ ##
3
+ # The Bot class is the workhorse of aide. It is responsible for representing
4
+ # and executing the logic described by the dsl, and interacts with xmpp4r
5
+ # in order to send and receive messages via the jabber protocol.
6
+ #
7
+ class Bot
8
+ LOG = Aide::Logging.log
9
+
10
+ #Hash[String, Block] - the match strings to look for, and the code to execute when found.
11
+ attr_writer :actions
12
+
13
+ #String - the username to sign this bot in with.
14
+ attr_writer :username
15
+ #Password - the password for this bot to use.
16
+ attr_writer :password
17
+ #Array[String] - a list of jabber IDs to allow this bot to interact with. Precludes block_list.
18
+ attr_accessor :allow_list
19
+ #Array[String] - a list of jabber IDs to explicitly block. Ignored if allow_list is set.
20
+ attr_accessor :block_list
21
+
22
+ ##
23
+ #Creates a new Bot instance.
24
+ #
25
+ def initialize
26
+ @actions = {}
27
+ end
28
+
29
+ ##
30
+ #Logs this Bot instance in to the jabber network and starts it listening
31
+ #for messages.
32
+ #=== Preconditions
33
+ #This method assumes that the Bot has already been properly configured;
34
+ #specifically, that at least the username and password properties have
35
+ #been set.
36
+ #=== Postconditions
37
+ #- The Bot will be logged on,
38
+ #- Its presence will be set to 'chat,' and
39
+ #- It will be listening for messages from privileged users in its own
40
+ # process (see is_user_allowed?)
41
+ #=== Returns
42
+ #+nil+
43
+ #
44
+ def login
45
+ if @password == nil or @username == nil
46
+ raise Aide::Login_Error, "credentials missing"
47
+ end
48
+ @client = Jabber::Client.new(Jabber::JID.new(@username))
49
+ @client.connect
50
+
51
+ #actual authentication with the host
52
+ begin
53
+ @client.auth(@password)
54
+ rescue Jabber::ClientAuthenticationFailure => af
55
+ raise Aide::Login_Error, "Authentication failure: #{af.message}", af.backtrace
56
+ end
57
+
58
+ @client.send(Jabber::Presence.new)
59
+
60
+ LOG.debug "forking the sub-process..."
61
+ @pid = fork do
62
+ Signal.trap("HUP") { LOG.info "I'm dying!"; exit }
63
+
64
+ @client.add_message_callback do |m|
65
+ if m.type == :chat and is_user_allowed? m.from.to_s.split('/')[0]
66
+ process_message m
67
+ end
68
+ end
69
+
70
+ loop { sleep 0.01 }
71
+ end
72
+ Process.detach(@pid)
73
+ LOG.debug "process forked with pid #{@pid}"
74
+
75
+ end
76
+
77
+ ##
78
+ #Sends the given message.
79
+ #=== Parameters
80
+ #- *message*: Jabber::Message. The message to send.
81
+ #
82
+ def send(message)
83
+ @client.send message
84
+ end
85
+
86
+ ##
87
+ #Examines the given message to see if any actions need to be performed
88
+ #on it. If this message matches any actions defined for this Bot, an
89
+ #Aide::ActionContext is created and used to evaluate the block associated
90
+ #with that action.
91
+ #=== Parameters
92
+ #- *message*: Jabber::Message. The message to process.
93
+ #
94
+ def process_message(message)
95
+ results = match(message.body)
96
+ return nil unless results #no need to do anything if there's no match
97
+
98
+ context = Aide::ActionContext.new
99
+ block = @actions[results[:action_name]]
100
+ context.message = results[:message]
101
+ context.from = message.from
102
+ context.to = message.to
103
+ context.bot = self
104
+ context.text = message.body
105
+ context.type = message.type
106
+ context.instance_eval(&block)
107
+
108
+ nil #no need to complicate things by returning the results of the block
109
+ end
110
+
111
+ ##
112
+ #Log the bot out of jabber and kill the process that is listening for
113
+ #messages.
114
+ #
115
+ def logout
116
+ @client.close
117
+
118
+ LOG.debug "sending pid #{@pid} the HUP signal"
119
+ Process.kill("HUP", @pid)
120
+ end
121
+
122
+ ##
123
+ #Given the text of a message, determines if that text matches any of the
124
+ #actions defined for this Bot.
125
+ #=== Parameters
126
+ #- *text*: String. The text of the message to check for matches.
127
+ #=== Returns
128
+ #Hash[:action_name, :message]
129
+ #Where :action_name corresponds to the name of the action this text
130
+ #matches, and :message corresponds to everything in +text+ after the match.
131
+ #Returns +nil+ if no match was found.
132
+ #
133
+ def match(text)
134
+ result = nil
135
+ return result unless text
136
+ #tokenizing the string makes it easy to check for false matches
137
+ #e.g. match("tokentastic") shouldn't successfully match "token"
138
+ tokens = text.split
139
+ @actions.each do |name, block|
140
+ ntokens = name.split
141
+ if tokens[0...ntokens.size] == ntokens
142
+ result = { :action_name => name, :message => text[(name.size + 1)..text.size] }
143
+ end
144
+ end
145
+
146
+ result
147
+ end
148
+
149
+ ##
150
+ #Checks a given username to see if it is explicitly allowed or blocked.
151
+ #=== Parameters
152
+ #- *username*: String. The username to check privileges for.
153
+ #=== Returns
154
+ #+boolean+ - the value of this boolean depends on the level of privileges
155
+ #set when the Bot was configured. If an allow_list is defined, then that
156
+ #list has highest precedence - +true+ is returned if the user is in that
157
+ #list, and +false+ is returned otherwise. If allow_list is not defined,
158
+ #but block_list is, then the inverse check is performed (+true+ is
159
+ #returned when the user is absent from the block_list, and vice-versa).
160
+ #If neither list is defined then the user is assumed to be authorized.
161
+ #
162
+ def is_user_allowed?(username)
163
+ return @allow_list.member?(username) if @allow_list
164
+ return !@block_list.member?(username) if @block_list
165
+ return true
166
+ end
167
+
168
+ ##
169
+ # Adds an action to the bot.
170
+ #=== Parameters:
171
+ #- *match*: String. The text to match for this action.
172
+ #- *block*: Proc. The code to execute when this action is fired.
173
+ #
174
+ def add_action(match, block)
175
+ @actions[match] = block
176
+ end
177
+
178
+ ##
179
+ # Allows for appending to the allow_list.
180
+ #=== Parameters:
181
+ #- *list*: Array[String]. The list of users to add to the allow_list.
182
+ #
183
+ def add_allow_list(list)
184
+ @allow_list ||= []
185
+ @allow_list += list
186
+ end
187
+
188
+ ##
189
+ # Allows for appending to the block_list.
190
+ #=== Parameters:
191
+ #- *list*: Array[String]. The list of users to add to the block_list.
192
+ #
193
+ def add_block_list(list)
194
+ @block_list ||= []
195
+ @block_list += list
196
+ end
197
+ end # Bot
198
+ end # Aide
@@ -0,0 +1,74 @@
1
+ module Aide
2
+ ##
3
+ #The Dsl class is used for parsing Aide dsl files. All of the functions made
4
+ #available by this class are available to any dsl file.
5
+ #TODO: This class needs better documentation.
6
+ #
7
+ class Dsl
8
+ ##
9
+ # Creates a new Dsl instance.
10
+ #=== Parameters:
11
+ #- *bot*: Aide::Bot. The bot this dsl will modify/set up.
12
+ #- *filename*: String. The file to parse, if any; defaults to nil.
13
+ #=== Yields:
14
+ #Yields self after the file is parsed.
15
+ #
16
+ def initialize(bot, filename = nil)
17
+ @bot = bot
18
+ load_file filename if filename
19
+ yield self if block_given?
20
+ end
21
+
22
+ ##
23
+ # Sets the username for the Bot that is being loaded.
24
+ #
25
+ def bot_username(text)
26
+ @bot.username=text
27
+ end
28
+
29
+ ##
30
+ # Sets the password for the Bot that is being loaded.
31
+ #
32
+ def bot_password(text)
33
+ @bot.password=text
34
+ end
35
+
36
+ ##
37
+ # Adds *people to the allow_list.
38
+ #
39
+ def allow(*people)
40
+ @bot.add_allow_list people
41
+ end
42
+
43
+ ##
44
+ # Adds *people to the block_list.
45
+ #
46
+ def block(*people)
47
+ @bot.add_block_list people
48
+ end
49
+
50
+ ##
51
+ # Defines an action for the bot.
52
+ #===Parameters:
53
+ #- *text*: String. The text to match on.
54
+ #- *&block*: Block. The code to execute when *text* is found (see Aide::ActionContext)
55
+ #
56
+ def with(text,&block)
57
+ @bot.add_action text, block
58
+ end
59
+
60
+ ##
61
+ # Reads in and parses a Dsl file.
62
+ #
63
+ def load_file(filename)
64
+ instance_eval(File.read(filename), filename)
65
+ end
66
+
67
+ ##
68
+ # Parses a block as though it were a Dsl file.
69
+ #
70
+ def load_block(&block)
71
+ instance_eval(&block)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,8 @@
1
+ module Aide
2
+ ##
3
+ #Login_Error represents a problem with the credentials
4
+ #given to an instance of Aide::Bot.
5
+ #
6
+ class Login_Error < StandardError
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ module Aide
2
+ class Logging
3
+ class << self
4
+ attr_accessor :log
5
+ end
6
+ end
7
+
8
+ def log
9
+ Aide::Logging.log
10
+ end
11
+
12
+ end
13
+
14
+ #set this log to whatever you want...
15
+ Aide::Logging.log = Logger.new(STDOUT)
16
+ Aide::Logging.log.sev_threshold = Logger::DEBUG
@@ -0,0 +1,9 @@
1
+ module Aide #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 4
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duwanis-aide
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Tommy Morgan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-09 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: xmpp4r
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.3.0
23
+ version:
24
+ description: Aide provides a DSL for quickly and easily building Jabber (XMPP) bots.
25
+ email: tommy.morgan@gmail.com
26
+ executables:
27
+ - aide
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - README
34
+ - bin/aide
35
+ - lib/aide.rb
36
+ - lib/aide/actioncontext.rb
37
+ - lib/aide/bot.rb
38
+ - lib/aide/dsl.rb
39
+ - lib/aide/logging.rb
40
+ - lib/aide/errors.rb
41
+ - lib/aide/version.rb
42
+ has_rdoc: true
43
+ homepage: http://github.com/duwanis/aide
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --main
47
+ - README
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.2.0
66
+ signing_key:
67
+ specification_version: 2
68
+ summary: A DSL for defining and running Jabber bots.
69
+ test_files: []
70
+