yong-purplegw_ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +2 -0
- data/Manifest.txt +7 -0
- data/README.txt +38 -0
- data/Rakefile +24 -0
- data/examples/purplegw_example.rb +83 -0
- data/ext/extconf.rb +6 -0
- data/ext/purple_ruby.c +411 -0
- metadata +71 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
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('purplegw_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
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-purplegw_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
|
+
|