zelf0gale-aide 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +82 -0
- data/bin/aide +15 -0
- data/lib/aide.rb +13 -0
- data/lib/aide/actioncontext.rb +49 -0
- data/lib/aide/bot.rb +190 -0
- data/lib/aide/dsl.rb +74 -0
- data/lib/aide/errors.rb +8 -0
- data/lib/aide/version.rb +9 -0
- metadata +70 -0
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.
|
data/bin/aide
ADDED
@@ -0,0 +1,15 @@
|
|
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_thread = bot.login
|
15
|
+
bot_thread.join
|
data/lib/aide.rb
ADDED
@@ -0,0 +1,13 @@
|
|
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
|
+
require 'aide/errors'
|
10
|
+
require 'aide/actioncontext'
|
11
|
+
require 'aide/bot'
|
12
|
+
require 'aide/dsl'
|
13
|
+
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
|
data/lib/aide/bot.rb
ADDED
@@ -0,0 +1,190 @@
|
|
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
|
+
|
9
|
+
#Hash[String, Block] - the match strings to look for, and the code to execute when found.
|
10
|
+
attr_writer :actions
|
11
|
+
|
12
|
+
#String - the username to sign this bot in with.
|
13
|
+
attr_writer :username
|
14
|
+
#Password - the password for this bot to use.
|
15
|
+
attr_writer :password
|
16
|
+
#Array[String] - a list of jabber IDs to allow this bot to interact with. Precludes block_list.
|
17
|
+
attr_accessor :allow_list
|
18
|
+
#Array[String] - a list of jabber IDs to explicitly block. Ignored if allow_list is set.
|
19
|
+
attr_accessor :block_list
|
20
|
+
|
21
|
+
##
|
22
|
+
#Creates a new Bot instance.
|
23
|
+
#
|
24
|
+
def initialize
|
25
|
+
@actions = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
#Logs this Bot instance in to the jabber network and starts it listening
|
30
|
+
#for messages.
|
31
|
+
#=== Preconditions
|
32
|
+
#This method assumes that the Bot has already been properly configured;
|
33
|
+
#specifically, that at least the username and password properties have
|
34
|
+
#been set.
|
35
|
+
#=== Postconditions
|
36
|
+
#- The Bot will be logged on,
|
37
|
+
#- Its presence will be set to 'chat,' and
|
38
|
+
#- It will be listening for messages from privileged users in its own
|
39
|
+
# process (see is_user_allowed?)
|
40
|
+
#=== Returns
|
41
|
+
# The thread that the bot is running in. Join it if you need to.
|
42
|
+
#
|
43
|
+
def login
|
44
|
+
if @password == nil or @username == nil
|
45
|
+
raise Aide::Login_Error, "credentials missing"
|
46
|
+
end
|
47
|
+
@client = Jabber::Client.new(Jabber::JID.new(@username))
|
48
|
+
@client.connect
|
49
|
+
|
50
|
+
#actual authentication with the host
|
51
|
+
begin
|
52
|
+
@client.auth(@password)
|
53
|
+
rescue Jabber::ClientAuthenticationFailure => af
|
54
|
+
raise Aide::Login_Error, "Authentication failure: #{af.message}", af.backtrace
|
55
|
+
end
|
56
|
+
|
57
|
+
@client.send(Jabber::Presence.new)
|
58
|
+
|
59
|
+
@thread = Thread.new do
|
60
|
+
@client.add_message_callback do |m|
|
61
|
+
if m.type == :chat and is_user_allowed? m.from.to_s.split('/')[0]
|
62
|
+
process_message m
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
loop { sleep 0.01 }
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
#Sends the given message.
|
73
|
+
#=== Parameters
|
74
|
+
#- *message*: Jabber::Message. The message to send.
|
75
|
+
#
|
76
|
+
def send(message)
|
77
|
+
@client.send message
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
#Examines the given message to see if any actions need to be performed
|
82
|
+
#on it. If this message matches any actions defined for this Bot, an
|
83
|
+
#Aide::ActionContext is created and used to evaluate the block associated
|
84
|
+
#with that action.
|
85
|
+
#=== Parameters
|
86
|
+
#- *message*: Jabber::Message. The message to process.
|
87
|
+
#
|
88
|
+
def process_message(message)
|
89
|
+
results = match(message.body)
|
90
|
+
return nil unless results #no need to do anything if there's no match
|
91
|
+
|
92
|
+
context = Aide::ActionContext.new
|
93
|
+
block = @actions[results[:action_name]]
|
94
|
+
context.message = results[:message]
|
95
|
+
context.from = message.from
|
96
|
+
context.to = message.to
|
97
|
+
context.bot = self
|
98
|
+
context.text = message.body
|
99
|
+
context.type = message.type
|
100
|
+
context.instance_eval(&block)
|
101
|
+
|
102
|
+
nil #no need to complicate things by returning the results of the block
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
#Log the bot out of jabber and kill the process that is listening for
|
107
|
+
#messages.
|
108
|
+
#
|
109
|
+
def logout
|
110
|
+
@client.close
|
111
|
+
@thread.kill
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
#Given the text of a message, determines if that text matches any of the
|
116
|
+
#actions defined for this Bot.
|
117
|
+
#=== Parameters
|
118
|
+
#- *text*: String. The text of the message to check for matches.
|
119
|
+
#=== Returns
|
120
|
+
#Hash[:action_name, :message]
|
121
|
+
#Where :action_name corresponds to the name of the action this text
|
122
|
+
#matches, and :message corresponds to everything in +text+ after the match.
|
123
|
+
#Returns +nil+ if no match was found.
|
124
|
+
#
|
125
|
+
def match(text)
|
126
|
+
result = nil
|
127
|
+
return result unless text
|
128
|
+
#tokenizing the string makes it easy to check for false matches
|
129
|
+
#e.g. match("tokentastic") shouldn't successfully match "token"
|
130
|
+
tokens = text.split
|
131
|
+
@actions.each do |name, block|
|
132
|
+
ntokens = name.split
|
133
|
+
if tokens[0...ntokens.size] == ntokens
|
134
|
+
result = { :action_name => name, :message => text[(name.size + 1)..text.size] }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
#Checks a given username to see if it is explicitly allowed or blocked.
|
143
|
+
#=== Parameters
|
144
|
+
#- *username*: String. The username to check privileges for.
|
145
|
+
#=== Returns
|
146
|
+
#+boolean+ - the value of this boolean depends on the level of privileges
|
147
|
+
#set when the Bot was configured. If an allow_list is defined, then that
|
148
|
+
#list has highest precedence - +true+ is returned if the user is in that
|
149
|
+
#list, and +false+ is returned otherwise. If allow_list is not defined,
|
150
|
+
#but block_list is, then the inverse check is performed (+true+ is
|
151
|
+
#returned when the user is absent from the block_list, and vice-versa).
|
152
|
+
#If neither list is defined then the user is assumed to be authorized.
|
153
|
+
#
|
154
|
+
def is_user_allowed?(username)
|
155
|
+
return @allow_list.member?(username) if @allow_list
|
156
|
+
return !@block_list.member?(username) if @block_list
|
157
|
+
return true
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Adds an action to the bot.
|
162
|
+
#=== Parameters:
|
163
|
+
#- *match*: String. The text to match for this action.
|
164
|
+
#- *block*: Proc. The code to execute when this action is fired.
|
165
|
+
#
|
166
|
+
def add_action(match, block)
|
167
|
+
@actions[match] = block
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Allows for appending to the allow_list.
|
172
|
+
#=== Parameters:
|
173
|
+
#- *list*: Array[String]. The list of users to add to the allow_list.
|
174
|
+
#
|
175
|
+
def add_allow_list(list)
|
176
|
+
@allow_list ||= []
|
177
|
+
@allow_list += list
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Allows for appending to the block_list.
|
182
|
+
#=== Parameters:
|
183
|
+
#- *list*: Array[String]. The list of users to add to the block_list.
|
184
|
+
#
|
185
|
+
def add_block_list(list)
|
186
|
+
@block_list ||= []
|
187
|
+
@block_list += list
|
188
|
+
end
|
189
|
+
end # Bot
|
190
|
+
end # Aide
|
data/lib/aide/dsl.rb
ADDED
@@ -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
|
data/lib/aide/errors.rb
ADDED
data/lib/aide/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zelf0gale-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
|
+
|