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 +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('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
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
|
+
|