rumpy 0.9.8
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/README.rdoc +53 -0
- data/lib/rumpy.rb +195 -0
- metadata +101 -0
data/README.rdoc
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
== Welcome to Rumpy
|
2
|
+
|
3
|
+
Rumpy is some kind of framework to make up your own jabber bot quickly.
|
4
|
+
It uses {ActiveRecord}[https://github.com/rails/rails/tree/master/activerecord] and {XMPP4R}[http://home.gna.org/xmpp4r/].
|
5
|
+
|
6
|
+
Our goal is 'DO NOT REINVENT THE WHEEL'.
|
7
|
+
|
8
|
+
== Features
|
9
|
+
|
10
|
+
* Forget about xmpp-related things. Just set your login & password.
|
11
|
+
* Forget about database-related things. Just set your database preferences.
|
12
|
+
* Write logic using ActiveRecord and callback functions.
|
13
|
+
|
14
|
+
== Getting started
|
15
|
+
|
16
|
+
* Rumpy uses 3 configs:
|
17
|
+
database.yml :: Your bot's database preferences.
|
18
|
+
lang.yml :: Your bot's responces. Append to existing keys more answers and use them like @lang['someanswer]. **There must be at least 3 keys : hello /used when somebody add bot/, stranger /when somebody trying talk to bot without authorization/, authorized /when bot get authorized/.
|
19
|
+
xmpp.yml :: Your bot's account settings.
|
20
|
+
Look at Examples section to see this configs.
|
21
|
+
* Implement your ActiveRecord models in one directory. You have to implement at least one model, for users.
|
22
|
+
* Prepare your database.
|
23
|
+
* Start writing your class:
|
24
|
+
* Mix in Rumpy::Bot module:
|
25
|
+
include Rumpy::Bot
|
26
|
+
* Define 3 instance variables:
|
27
|
+
@models_path :: Path to directory, containing all ruby files with models.
|
28
|
+
@config_path :: Path to directory, containing all ruby config files
|
29
|
+
@main_model :: Symbol, that stands for main model. If your main model is, e.g. User, set @main_model to :user
|
30
|
+
@pid_file :: Optional variable, sets location of the file to which pid of detached process will be saved. If not set, it equals NameOfYourBotClass.downcase + '.pid'.
|
31
|
+
@errfile :: Optional variable, sets location of the file for errors. If not set, standard error stream will be used.
|
32
|
+
* Write 3 methods:
|
33
|
+
backend_func() -> [[receiver, message]*] :: This optional method is running all the time in the loop. Returns array of pairs [receiver, message].
|
34
|
+
parser_func(msg) -> pars_result :: This method parses any incoming message and returs results.
|
35
|
+
do_func(usermodel, pars_results) -> msg :: This method uses results from parser_func, doing some stuff with model of user, from whom message was received. Returns message to be send to this user
|
36
|
+
* Run bot:
|
37
|
+
You can run your bot without detaching:
|
38
|
+
Rumpy.run YourBotClassName
|
39
|
+
Or with detaching:
|
40
|
+
To start your bot:
|
41
|
+
Rumpy.start YourBotClassName
|
42
|
+
To stop it:
|
43
|
+
Rumpy.stop YourBotClassName
|
44
|
+
|
45
|
+
== Example
|
46
|
+
|
47
|
+
Look at {CuteBot}[https://github.com/MPogoda/CuteBot], {yatodo}[https://github.com/MPogoda/yatodo], {Noty}[https://github.com/Ximik/Noty].
|
48
|
+
|
49
|
+
Feel free to contact us about any questions related to Rumpy.
|
50
|
+
|
51
|
+
== License
|
52
|
+
|
53
|
+
Rumpy is released under the MIT license.
|
data/lib/rumpy.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'xmpp4r/client'
|
3
|
+
require 'xmpp4r/roster'
|
4
|
+
require 'active_record'
|
5
|
+
require 'active_record/validations'
|
6
|
+
|
7
|
+
module Rumpy
|
8
|
+
|
9
|
+
def self.start(botclass)
|
10
|
+
bot = botclass.new
|
11
|
+
pf = pid_file bot
|
12
|
+
return false if File.exist? pf
|
13
|
+
pid = fork do
|
14
|
+
bot.start
|
15
|
+
end
|
16
|
+
Process.detach pid
|
17
|
+
File.open(pf, 'w') do |file|
|
18
|
+
file.puts pid
|
19
|
+
end
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.stop(botclass)
|
24
|
+
pf = pid_file botclass.new
|
25
|
+
return false unless File.exist? pf
|
26
|
+
begin
|
27
|
+
File.open(pf) do |file|
|
28
|
+
Process.kill :TERM, file.gets.strip.to_i
|
29
|
+
end
|
30
|
+
ensure
|
31
|
+
File.unlink pf
|
32
|
+
end
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.run(botclass)
|
37
|
+
botclass.new.start
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.pid_file(bot)
|
41
|
+
pid_file = bot.pid_file
|
42
|
+
pid_file = bot.class.to_s.downcase + '.pid' if pid_file.nil?
|
43
|
+
pid_file
|
44
|
+
end
|
45
|
+
|
46
|
+
module Bot
|
47
|
+
attr_reader :pid_file
|
48
|
+
|
49
|
+
def start
|
50
|
+
@err_file = if @err_file then
|
51
|
+
File.open(@err_file, 'w')
|
52
|
+
else
|
53
|
+
$stderr
|
54
|
+
end
|
55
|
+
Signal.trap :TERM do |signo|
|
56
|
+
@err_file.close
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
init
|
61
|
+
connect
|
62
|
+
clear_users
|
63
|
+
start_subscription_callback
|
64
|
+
start_message_callback
|
65
|
+
@client.send Jabber::Presence.new
|
66
|
+
Thread.new do
|
67
|
+
begin
|
68
|
+
loop do
|
69
|
+
backend_func().each do |result|
|
70
|
+
send_msg *result
|
71
|
+
end
|
72
|
+
end
|
73
|
+
rescue => e
|
74
|
+
$err_file.puts e.inspect
|
75
|
+
$err_file.puts e.backtrace
|
76
|
+
end
|
77
|
+
end if self.respond_to? :backend_func
|
78
|
+
Thread.stop
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def init
|
84
|
+
|
85
|
+
xmppconfig = YAML::load_file @config_path + '/xmpp.yml'
|
86
|
+
@lang = YAML::load_file @config_path + '/lang.yml'
|
87
|
+
@jid = Jabber::JID.new xmppconfig['jid']
|
88
|
+
@password = xmppconfig['password']
|
89
|
+
@client = Jabber::Client.new @jid
|
90
|
+
|
91
|
+
if @models_path then
|
92
|
+
dbconfig = YAML::load_file @config_path + '/database.yml'
|
93
|
+
ActiveRecord::Base.establish_connection dbconfig
|
94
|
+
Dir[@models_path].each do |file|
|
95
|
+
self.class.require file
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@main_model = Object.const_get @main_model.to_s.capitalize
|
100
|
+
def @main_model.find_by_jid(jid)
|
101
|
+
super jid.strip.to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
@mutexes = Hash.new do |h, k|
|
105
|
+
h[k] = Mutex.new
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def connect
|
110
|
+
@client.connect
|
111
|
+
@client.auth @password
|
112
|
+
@roster = Jabber::Roster::Helper.new @client
|
113
|
+
@roster.wait_for_roster
|
114
|
+
end
|
115
|
+
|
116
|
+
def clear_users
|
117
|
+
@main_model.find_each do |user|
|
118
|
+
items = @roster.find user.jid
|
119
|
+
user.destroy if items.count != 1
|
120
|
+
end
|
121
|
+
@roster.items.each do |jid, item|
|
122
|
+
user = @main_model.find_by_jid jid
|
123
|
+
if user.nil? then
|
124
|
+
item.remove
|
125
|
+
next
|
126
|
+
elsif item.subscription != :both then
|
127
|
+
item.remove
|
128
|
+
user.destroy
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def start_subscription_callback
|
134
|
+
@roster.add_subscription_request_callback do |item, presence|
|
135
|
+
jid = presence.from
|
136
|
+
@roster.accept_subscription jid
|
137
|
+
@client.send Jabber::Presence.new.set_type(:subscribe).set_to(jid)
|
138
|
+
send_msg jid, @lang['hello']
|
139
|
+
end
|
140
|
+
@roster.add_subscription_callback do |item, presence|
|
141
|
+
case presence.type
|
142
|
+
when :unsubscribed, :unsubscribe
|
143
|
+
item.remove
|
144
|
+
remove_jid item.jid
|
145
|
+
when :subscribed
|
146
|
+
add_jid item.jid
|
147
|
+
send_msg item.jid, @lang['authorized']
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def start_message_callback
|
153
|
+
@client.add_message_callback do |msg|
|
154
|
+
begin
|
155
|
+
if msg.type != :error and msg.body and msg.from then
|
156
|
+
if user = @main_model.find_by_jid(msg.from) then
|
157
|
+
pars_results = parser_func msg.body
|
158
|
+
|
159
|
+
message = ""
|
160
|
+
@mutexes[user.jid].synchronize do
|
161
|
+
message = do_func user, pars_results
|
162
|
+
end
|
163
|
+
send_msg msg.from, message
|
164
|
+
else
|
165
|
+
send_msg msg.from, @lang['stranger']
|
166
|
+
items = @roster.find msg.from.strip.to_s
|
167
|
+
items.first.last.remove unless items.empty?
|
168
|
+
end
|
169
|
+
end
|
170
|
+
rescue => e
|
171
|
+
@err_file.puts e.inspect
|
172
|
+
@err_file.puts e.backtrace
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def send_msg(destination, text)
|
178
|
+
return if destination.nil? or text.nil?
|
179
|
+
msg = Jabber::Message.new destination, text
|
180
|
+
msg.type = :chat
|
181
|
+
@client.send msg
|
182
|
+
end
|
183
|
+
|
184
|
+
def add_jid(jid)
|
185
|
+
user = @main_model.new
|
186
|
+
user.jid = jid.strip.to_s
|
187
|
+
user.save
|
188
|
+
end
|
189
|
+
|
190
|
+
def remove_jid(jid)
|
191
|
+
user = @main_model.find_by_jid jid
|
192
|
+
user.destroy unless user.nil?
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rumpy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 43
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
- 8
|
10
|
+
version: 0.9.8
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tsokurov A.G.
|
14
|
+
- Pogoda M.V.
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-06-17 00:00:00 +03:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: activerecord
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">"
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
31
|
+
segments:
|
32
|
+
- 3
|
33
|
+
- 0
|
34
|
+
version: "3.0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: xmpp4r
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 1
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 5
|
49
|
+
version: "0.5"
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
description: Rumpy is some kind of framework to make up your own jabber bot quickly.
|
53
|
+
email:
|
54
|
+
- mpogoda@lavabit.com
|
55
|
+
- me@ximik.net
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.rdoc
|
62
|
+
files:
|
63
|
+
- lib/rumpy.rb
|
64
|
+
- README.rdoc
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: https://github.com/Ximik/Rumpy
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --main
|
72
|
+
- README.rdoc
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: Rumpy == jabber bot framework
|
100
|
+
test_files: []
|
101
|
+
|