yong-purple_ruby 0.1.0

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/History.txt ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0
2
+
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ ext/extconf.rb
2
+ ext/purple_ruby.c
3
+ examples/purplegw_example.rb
4
+ Manifest.txt
5
+ History.txt
6
+ README.txt
7
+ Rakefile
data/README.txt ADDED
@@ -0,0 +1,38 @@
1
+ == OVERVIEW
2
+
3
+ purple_ruby is a ruby gem to write servers that send and recive IM messages. It uses libpurple (http://developer.pidgin.im/wiki/WhatIsLibpurple) and therforce supports all protocols that Pidgin/Adium supports (MSN/Gtalk/Yahoo/AIM/ICQ etc).
4
+
5
+ Please check examples/purplegw_example.rb for details. Bascially you just tell it what to do when an IM was received, and there is an embedded tcp 'proxy' which allows you send IM messages.
6
+
7
+ Why not "ruburple"? I have used ruburple (http://rubyforge.org/projects/ruburple), but found it blocks a lot. libpurple needs to run its own event loop which interferes with ruby's green thread model. Ruburple's author has done lots of hard work to workaround the problem (http://rubyforge.org/pipermail/ruburple-development/2007-June/000005.html), but it does not work well.
8
+
9
+ == INSTALLATION
10
+
11
+ Linux (Ubuntu):
12
+ ---------------
13
+ apt-get install libpurple0 libpurple-dev
14
+ gem install yong-purple_ruby
15
+
16
+ OSX:
17
+ ----
18
+ sudo port -d selfupdate
19
+ sudo port install gnutls
20
+ (wait forever....)
21
+ sudo port install nss
22
+ (wait forever....)
23
+ wget http://downloads.sourceforge.net/pidgin/pidgin-2.5.5.tar.bz2
24
+ tar xvjf pidgin-2.5.5.tar.bz2
25
+ cd pidgin-2.5.5
26
+ ./configure --disable-gtkui --disable-screensaver --disable-consoleui --disable-sm --disable-perl --disable-tk --disable-tcl --disable-gstreamer --disable-schemas-install --disable-gestures --disable-cap --disable-gevolution --disable-gtkspell --disable-startup-notification --disable-avahi --disable-nm --disable-dbus --disable-meanwhile
27
+ make
28
+ (wait forever...)
29
+ sudo make install
30
+
31
+ edit your ~/.bash_profile and add this line
32
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
33
+
34
+ sudo gem install yong-purple_ruby
35
+
36
+
37
+
38
+
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+
4
+ EXT = "ext/ruburple_ext.#{Hoe::DLEXT}"
5
+
6
+ Hoe.new('purple_ruby', '0.1.0') do |p|
7
+ p.author = 'yong'
8
+ p.email = 'yong@intridea.com'
9
+ p.url = 'http://www.intridea.com'
10
+ p.summary = 'A ruby gem to write server that sends and recives IM messages'
11
+ p.description = 'A ruby gem to write server that sends and recives IM messages'
12
+
13
+ p.spec_extras[:extensions] = "ext/extconf.rb"
14
+ p.clean_globs << EXT << "ext/*.o" << "ext/Makefile"
15
+ end
16
+
17
+ task :test => EXT
18
+
19
+ file EXT => ["ext/extconf.rb", "ext/purple_ruby.c"] do
20
+ Dir.chdir "ext" do
21
+ ruby "extconf.rb"
22
+ sh "make"
23
+ end
24
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ #Example Usage:
3
+ #
4
+ #Start the daemon and receive IM:
5
+ #$ruby examples/purplegw_example.rb prpl-msn user@hotmail.com password prpl-jabber user@gmail.com password
6
+ #
7
+ #Send im:
8
+ #$ irb
9
+ #irb(main):001:0> require 'lib/purplegw_ruby'
10
+ #irb(main):007:0> PurpleGW.deliver 'prpl-jabber', 'friend@gmail.com', 'hello worlds!'
11
+ #
12
+
13
+ require 'hpricot'
14
+ require 'socket'
15
+ require File.expand_path(File.join(File.dirname(__FILE__), '../ext/purple_ruby'))
16
+
17
+ class PurpleGWExample
18
+ SERVER_IP = "127.0.0.1"
19
+ SERVER_PORT = 9876
20
+
21
+ def start configs
22
+ PurpleRuby.init false #use 'true' if you want to see the debug messages
23
+
24
+ puts "Available protocols:", PurpleRuby.list_protocols
25
+
26
+ accounts = {}
27
+ configs.each {|config|
28
+ account = PurpleRuby.login(config[:protocol], config[:username], config[:password])
29
+ accounts[config[:protocol]] = account
30
+ }
31
+
32
+ #handle incoming im messages
33
+ PurpleRuby.watch_incoming_im do |receiver, sender, message|
34
+ sender = sender[0...sender.index('/')] if sender.index('/') #discard anything after '/'
35
+ text = (Hpricot(message)).to_plain_text
36
+ puts "recv: #{receiver}, #{sender}, #{text}"
37
+ end
38
+
39
+ #TODO detect login failure
40
+ PurpleRuby.watch_signed_on_event do |acc|
41
+ puts "signed on: #{acc.username}"
42
+ end
43
+
44
+ #listen a tcp port, parse incoming data and send it out.
45
+ #We assume the incoming data is in the following format:
46
+ #<protocol> <user> <message>
47
+ PurpleRuby.watch_incoming_ipc(SERVER_IP, SERVER_PORT) do |data|
48
+ first_space = data.index(' ')
49
+ second_space = data.index(' ', first_space + 1)
50
+ protocol = data[0...first_space]
51
+ user = data[(first_space+1)...second_space]
52
+ message = data[(second_space+1)...-1]
53
+ puts "send: #{protocol}, #{user}, #{message}"
54
+ accounts[protocol].send_im(user, message)
55
+ end
56
+
57
+ trap("INT") {
58
+ #TODO ctrl-c can not be detected until a message is coming
59
+ puts 'Ctrl-C, quit...'
60
+ PurpleRuby.main_loop_stop
61
+ }
62
+
63
+ PurpleRuby.main_loop_run
64
+ end
65
+
66
+ def self.deliver(protocol, to_users, message)
67
+ to_users = [to_users] unless to_users.is_a?(Array)
68
+ to_users.each do |user|
69
+ t = TCPSocket.new(SERVER_IP, SERVER_PORT)
70
+ t.print "#{protocol} #{user} #{message}\n"
71
+ t.close
72
+ end
73
+ end
74
+ end
75
+
76
+ if ARGV.length >= 3
77
+ configs = []
78
+ configs << {:protocol => ARGV[0], :username => ARGV[1], :password => ARGV[2]}
79
+ configs << {:protocol => ARGV[3], :username => ARGV[4], :password => ARGV[5]} if ARGV.length >= 6
80
+ #add more accounts here if you like
81
+ PurpleGWExample.new.start configs
82
+ end
83
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+ $CFLAGS = "#{ENV['CFLAGS']} -Wall -O3 -g"
3
+ pkg_config 'purple'
4
+ pkg_config 'glib-2.0'
5
+ pkg_config 'gthread-2.0'
6
+ create_makefile('purple_ruby')
data/ext/purple_ruby.c ADDED
@@ -0,0 +1,411 @@
1
+ #include <libpurple/account.h>
2
+ #include <libpurple/conversation.h>
3
+ #include <libpurple/core.h>
4
+ #include <libpurple/debug.h>
5
+ #include <libpurple/cipher.h>
6
+ #include <libpurple/eventloop.h>
7
+ #include <libpurple/ft.h>
8
+ #include <libpurple/log.h>
9
+ #include <libpurple/notify.h>
10
+ #include <libpurple/prefs.h>
11
+ #include <libpurple/prpl.h>
12
+ #include <libpurple/pounce.h>
13
+ #include <libpurple/request.h>
14
+ #include <libpurple/savedstatuses.h>
15
+ #include <libpurple/sound.h>
16
+ #include <libpurple/status.h>
17
+ #include <libpurple/util.h>
18
+ #include <libpurple/whiteboard.h>
19
+ #include <libpurple/network.h>
20
+
21
+ #include <ruby.h>
22
+ #include <errno.h>
23
+ #include <stdarg.h>
24
+ #include <netinet/in.h>
25
+ #include <sys/socket.h>
26
+ #include <arpa/inet.h>
27
+ #include <fcntl.h>
28
+
29
+ #define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
30
+ #define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
31
+
32
+ typedef struct _PurpleGLibIOClosure {
33
+ PurpleInputFunction function;
34
+ guint result;
35
+ gpointer data;
36
+ } PurpleGLibIOClosure;
37
+
38
+ static void purple_glib_io_destroy(gpointer data)
39
+ {
40
+ g_free(data);
41
+ }
42
+
43
+ static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
44
+ {
45
+ PurpleGLibIOClosure *closure = data;
46
+ PurpleInputCondition purple_cond = 0;
47
+
48
+ if (condition & PURPLE_GLIB_READ_COND)
49
+ purple_cond |= PURPLE_INPUT_READ;
50
+ if (condition & PURPLE_GLIB_WRITE_COND)
51
+ purple_cond |= PURPLE_INPUT_WRITE;
52
+
53
+ closure->function(closure->data, g_io_channel_unix_get_fd(source),
54
+ purple_cond);
55
+
56
+ return TRUE;
57
+ }
58
+
59
+ static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
60
+ gpointer data)
61
+ {
62
+ PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1);
63
+ GIOChannel *channel;
64
+ GIOCondition cond = 0;
65
+
66
+ closure->function = function;
67
+ closure->data = data;
68
+
69
+ if (condition & PURPLE_INPUT_READ)
70
+ cond |= PURPLE_GLIB_READ_COND;
71
+ if (condition & PURPLE_INPUT_WRITE)
72
+ cond |= PURPLE_GLIB_WRITE_COND;
73
+
74
+ channel = g_io_channel_unix_new(fd);
75
+ closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
76
+ purple_glib_io_invoke, closure, purple_glib_io_destroy);
77
+
78
+ g_io_channel_unref(channel);
79
+ return closure->result;
80
+ }
81
+
82
+ static PurpleEventLoopUiOps glib_eventloops =
83
+ {
84
+ g_timeout_add,
85
+ g_source_remove,
86
+ glib_input_add,
87
+ g_source_remove,
88
+ NULL,
89
+ #if GLIB_CHECK_VERSION(2,14,0)
90
+ g_timeout_add_seconds,
91
+ #else
92
+ NULL,
93
+ #endif
94
+
95
+ /* padding */
96
+ NULL,
97
+ NULL,
98
+ NULL
99
+ };
100
+
101
+ static VALUE cPurpleRuby;
102
+ static VALUE cAccount;
103
+ static char* UI_ID = "purplegw";
104
+ static GMainLoop *main_loop;
105
+ static VALUE im_hanlder;
106
+ static VALUE signed_on_hanlder;
107
+ static GHashTable* hash_table;
108
+
109
+ static void write_conv(PurpleConversation *conv, const char *who, const char *alias,
110
+ const char *message, PurpleMessageFlags flags, time_t mtime)
111
+ {
112
+ VALUE *args = g_new(VALUE, 3);
113
+ args[0] = rb_str_new2(purple_account_get_username(purple_conversation_get_account(conv)));
114
+ args[1] = rb_str_new2(who);
115
+ args[2] = rb_str_new2(message);
116
+ rb_funcall2(im_hanlder, rb_intern("call"), 3, args);
117
+ }
118
+
119
+ static PurpleConversationUiOps conv_uiops =
120
+ {
121
+ NULL, /* create_conversation */
122
+ NULL, /* destroy_conversation */
123
+ NULL, /* write_chat */
124
+ NULL, /* write_im */
125
+ write_conv, /* write_conv */
126
+ NULL, /* chat_add_users */
127
+ NULL, /* chat_rename_user */
128
+ NULL, /* chat_remove_users */
129
+ NULL, /* chat_update_user */
130
+ NULL, /* present */
131
+ NULL, /* has_focus */
132
+ NULL, /* custom_smiley_add */
133
+ NULL, /* custom_smiley_write */
134
+ NULL, /* custom_smiley_close */
135
+ NULL, /* send_confirm */
136
+ NULL,
137
+ NULL,
138
+ NULL,
139
+ NULL
140
+ };
141
+
142
+ static void ui_init(void)
143
+ {
144
+ /**
145
+ * This should initialize the UI components for all the modules. Here we
146
+ * just initialize the UI for conversations.
147
+ */
148
+ purple_conversations_set_ui_ops(&conv_uiops);
149
+ }
150
+
151
+ static PurpleCoreUiOps core_uiops =
152
+ {
153
+ NULL,
154
+ NULL,
155
+ ui_init,
156
+ NULL,
157
+
158
+ /* padding */
159
+ NULL,
160
+ NULL,
161
+ NULL,
162
+ NULL
163
+ };
164
+
165
+ static VALUE init(VALUE self, VALUE debug)
166
+ {
167
+ hash_table = g_hash_table_new(NULL, NULL);
168
+
169
+ purple_debug_set_enabled((debug == Qnil || debug == Qfalse) ? FALSE : TRUE);
170
+ purple_core_set_ui_ops(&core_uiops);
171
+ purple_eventloop_set_ui_ops(&glib_eventloops);
172
+
173
+ if (!purple_core_init(UI_ID)) {
174
+ rb_raise(rb_eRuntimeError, "libpurple initialization failed");
175
+ }
176
+
177
+ /* Create and load the buddylist. */
178
+ purple_set_blist(purple_blist_new());
179
+ purple_blist_load();
180
+
181
+ /* Load the preferences. */
182
+ purple_prefs_load();
183
+
184
+ /* Load the pounces. */
185
+ purple_pounces_load();
186
+
187
+ return Qnil;
188
+ }
189
+
190
+ static VALUE watch_incoming_im(VALUE self)
191
+ {
192
+ im_hanlder = rb_block_proc();
193
+ return im_hanlder;
194
+ }
195
+
196
+ static void signed_on(PurpleConnection* connection)
197
+ {
198
+ VALUE *args = g_new(VALUE, 1);
199
+ args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, purple_connection_get_account(connection));
200
+ rb_funcall2((VALUE)signed_on_hanlder, rb_intern("call"), 1, args);
201
+ }
202
+
203
+ static VALUE watch_signed_on_event(VALUE self)
204
+ {
205
+ signed_on_hanlder = rb_block_proc();
206
+ int handle;
207
+ purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
208
+ PURPLE_CALLBACK(signed_on), NULL);
209
+ return signed_on_hanlder;
210
+ }
211
+
212
+ static void _read_socket_handler(gpointer data, int socket, PurpleInputCondition condition)
213
+ {
214
+ char message[4096] = {0};
215
+ int i = recv(socket, message, sizeof(message) - 1, 0);
216
+ if (i > 0) {
217
+ //printf("recv %d %d\n", socket, i);
218
+
219
+ VALUE str = (VALUE)g_hash_table_lookup(hash_table, (gpointer)socket);
220
+ if (NULL == str) rb_raise(rb_eRuntimeError, "can not find socket: %d", socket);
221
+ rb_str_append(str, rb_str_new2(message));
222
+ } else {
223
+ //printf("closed %d %d %s\n", socket, i, g_strerror(errno));
224
+
225
+ VALUE str = (VALUE)g_hash_table_lookup(hash_table, (gpointer)socket);
226
+ if (NULL == str) return;
227
+
228
+ close(socket);
229
+ purple_input_remove(socket);
230
+ g_hash_table_remove(hash_table, (gpointer)socket);
231
+
232
+ VALUE *args = g_new(VALUE, 1);
233
+ args[0] = str;
234
+ rb_funcall2((VALUE)data, rb_intern("call"), 1, args);
235
+ }
236
+ }
237
+
238
+ static void _accept_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
239
+ {
240
+ /* Check that it is a read condition */
241
+ if (condition != PURPLE_INPUT_READ)
242
+ return;
243
+
244
+ struct sockaddr_in their_addr; /* connector's address information */
245
+ socklen_t sin_size = sizeof(struct sockaddr);
246
+ int client_socket;
247
+ if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
248
+ return;
249
+ }
250
+
251
+ int flags = fcntl(client_socket, F_GETFL);
252
+ fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
253
+ #ifndef _WIN32
254
+ fcntl(client_socket, F_SETFD, FD_CLOEXEC);
255
+ #endif
256
+
257
+ //printf("new connection: %d\n", client_socket);
258
+
259
+ g_hash_table_insert(hash_table, (gpointer)client_socket, (gpointer)rb_str_new2(""));
260
+
261
+ purple_input_add(client_socket, PURPLE_INPUT_READ, _read_socket_handler, data);
262
+ }
263
+
264
+ static VALUE watch_incoming_ipc(VALUE self, VALUE serverip, VALUE port)
265
+ {
266
+ struct sockaddr_in my_addr;
267
+ int soc;
268
+
269
+ /* Open a listening socket for incoming conversations */
270
+ if ((soc = socket(PF_INET, SOCK_STREAM, 0)) < 0)
271
+ {
272
+ rb_raise(rb_eRuntimeError, "Cannot open socket: %s\n", g_strerror(errno));
273
+ return Qnil;
274
+ }
275
+
276
+ memset(&my_addr, 0, sizeof(struct sockaddr_in));
277
+ my_addr.sin_family = AF_INET;
278
+ my_addr.sin_addr.s_addr = inet_addr(RSTRING(serverip)->ptr);
279
+ my_addr.sin_port = htons(FIX2INT(port));
280
+ if (bind(soc, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
281
+ {
282
+ rb_raise(rb_eRuntimeError, "Unable to bind to port %d: %s\n", (int)FIX2INT(port), g_strerror(errno));
283
+ return Qnil;
284
+ }
285
+
286
+ /* Attempt to listen on the bound socket */
287
+ if (listen(soc, 10) != 0)
288
+ {
289
+ rb_raise(rb_eRuntimeError, "Cannot listen on socket: %s\n", g_strerror(errno));
290
+ return Qnil;
291
+ }
292
+
293
+ VALUE proc = rb_block_proc();
294
+
295
+ /* Open a watcher in the socket we have just opened */
296
+ purple_input_add(soc, PURPLE_INPUT_READ, _accept_socket_handler, (gpointer)proc);
297
+
298
+ return port;
299
+ }
300
+
301
+ static VALUE login(VALUE self, VALUE protocol, VALUE username, VALUE password)
302
+ {
303
+ PurpleAccount* account = purple_account_new(RSTRING(username)->ptr, RSTRING(protocol)->ptr);
304
+ purple_account_set_password(account, RSTRING(password)->ptr);
305
+ purple_account_set_enabled(account, UI_ID, TRUE);
306
+ PurpleSavedStatus *status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
307
+ purple_savedstatus_activate(status);
308
+
309
+ return Data_Wrap_Struct(cAccount, NULL, NULL, account);
310
+ }
311
+
312
+ static VALUE main_loop_run(VALUE self)
313
+ {
314
+ main_loop = g_main_loop_new(NULL, FALSE);
315
+ g_main_loop_run(main_loop);
316
+ return Qnil;
317
+ }
318
+
319
+ static VALUE main_loop_stop(VALUE self)
320
+ {
321
+ g_main_loop_quit(main_loop);
322
+ return Qnil;
323
+ }
324
+
325
+ static VALUE send_im(VALUE self, VALUE name, VALUE message)
326
+ {
327
+ PurpleAccount *account;
328
+ Data_Get_Struct(self, PurpleAccount, account);
329
+
330
+ if (purple_account_is_connected(account)) {
331
+ int i = serv_send_im(purple_account_get_connection(account), RSTRING(name)->ptr, RSTRING(message)->ptr, 0);
332
+ return INT2FIX(i);
333
+ } else {
334
+ return Qnil;
335
+ }
336
+ }
337
+
338
+ static VALUE username(VALUE self)
339
+ {
340
+ PurpleAccount *account;
341
+ Data_Get_Struct(self, PurpleAccount, account);
342
+ return rb_str_new2(purple_account_get_username(account));
343
+ }
344
+
345
+ static VALUE list_protocols(VALUE self)
346
+ {
347
+ VALUE array = rb_ary_new();
348
+
349
+ GList *iter = purple_plugins_get_protocols();
350
+ int i;
351
+ for (i = 0; iter; iter = iter->next) {
352
+ PurplePlugin *plugin = iter->data;
353
+ PurplePluginInfo *info = plugin->info;
354
+ if (info && info->name) {
355
+ char s[256];
356
+ snprintf(s, sizeof(s) -1, "%s %s", info->id, info->name);
357
+ rb_ary_push(array, rb_str_new2(s));
358
+ }
359
+ }
360
+
361
+ return array;
362
+ }
363
+
364
+ static VALUE add_buddy(VALUE self, VALUE buddy)
365
+ {
366
+ PurpleAccount *account;
367
+ Data_Get_Struct(self, PurpleAccount, account);
368
+
369
+ char* group = _("Buddies");
370
+ PurpleGroup* grp = purple_find_group(group);
371
+ if (!grp)
372
+ {
373
+ grp = purple_group_new(group);
374
+ purple_blist_add_group(grp, NULL);
375
+ }
376
+
377
+ PurpleBuddy* pb = purple_buddy_new(account, RSTRING(buddy)->ptr, NULL);
378
+ purple_blist_add_buddy(pb, NULL, grp, NULL);
379
+ purple_account_add_buddy(account, pb);
380
+ return Qtrue;
381
+ }
382
+
383
+ static VALUE has_buddy(VALUE self, VALUE buddy)
384
+ {
385
+ PurpleAccount *account;
386
+ Data_Get_Struct(self, PurpleAccount, account);
387
+ if (purple_find_buddy(account, RSTRING(buddy)->ptr) != NULL) {
388
+ return Qtrue;
389
+ } else {
390
+ return Qfalse;
391
+ }
392
+ }
393
+
394
+ void Init_purple_ruby()
395
+ {
396
+ cPurpleRuby = rb_define_class("PurpleRuby", rb_cObject);
397
+ rb_define_singleton_method(cPurpleRuby, "init", init, 1);
398
+ rb_define_singleton_method(cPurpleRuby, "list_protocols", list_protocols, 0);
399
+ rb_define_singleton_method(cPurpleRuby, "watch_signed_on_event", watch_signed_on_event, 0);
400
+ rb_define_singleton_method(cPurpleRuby, "watch_incoming_im", watch_incoming_im, 0);
401
+ rb_define_singleton_method(cPurpleRuby, "login", login, 3);
402
+ rb_define_singleton_method(cPurpleRuby, "watch_incoming_ipc", watch_incoming_ipc, 2);
403
+ rb_define_singleton_method(cPurpleRuby, "main_loop_run", main_loop_run, 0);
404
+ rb_define_singleton_method(cPurpleRuby, "main_loop_stop", main_loop_stop, 0);
405
+
406
+ cAccount = rb_define_class_under(cPurpleRuby, "Account", rb_cObject);
407
+ rb_define_method(cAccount, "send_im", send_im, 2);
408
+ rb_define_method(cAccount, "username", username, 0);
409
+ rb_define_method(cAccount, "add_buddy", add_buddy, 1);
410
+ rb_define_method(cAccount, "has_buddy?", has_buddy, 1);
411
+ }
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yong-purple_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - yong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.3
24
+ version:
25
+ description: A ruby gem to write server that sends and recives IM messages
26
+ email: yong@intridea.com
27
+ executables: []
28
+
29
+ extensions:
30
+ - ext/extconf.rb
31
+ extra_rdoc_files:
32
+ - Manifest.txt
33
+ - History.txt
34
+ - README.txt
35
+ files:
36
+ - ext/extconf.rb
37
+ - ext/purple_ruby.c
38
+ - examples/purplegw_example.rb
39
+ - Manifest.txt
40
+ - History.txt
41
+ - README.txt
42
+ - Rakefile
43
+ has_rdoc: true
44
+ homepage: http://www.intridea.com
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --main
48
+ - README.txt
49
+ require_paths:
50
+ - ext
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: purplegw_ruby
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: A ruby gem to write server that sends and recives IM messages
70
+ test_files: []
71
+