palbo-lijab 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/bin/lijab +9 -0
- data/ext/extconf.rb +20 -0
- data/ext/readline_extra.c +172 -0
- data/lib/lijab/commands/contacts.rb +101 -0
- data/lib/lijab/commands/options.rb +49 -0
- data/lib/lijab/commands/simple.rb +132 -0
- data/lib/lijab/commands/status.rb +63 -0
- data/lib/lijab/commands/subscription.rb +78 -0
- data/lib/lijab/commands.rb +139 -0
- data/lib/lijab/config.rb +174 -0
- data/lib/lijab/contacts.rb +318 -0
- data/lib/lijab/history.rb +122 -0
- data/lib/lijab/hooks.rb +109 -0
- data/lib/lijab/input.rb +234 -0
- data/lib/lijab/main.rb +246 -0
- data/lib/lijab/out.rb +173 -0
- data/lib/lijab/term/ansi.rb +20 -0
- data/lib/lijab/version.rb +4 -0
- data/lib/lijab/xmpp4r/message.rb +45 -0
- data/lib/lijab.rb +7 -0
- data/lib/readline/extra.rb +7 -0
- metadata +102 -0
data/bin/lijab
ADDED
data/ext/extconf.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Loads mkmf which is used to make makefiles for Ruby extensions
|
2
|
+
require 'mkmf'
|
3
|
+
|
4
|
+
dir_config("readline")
|
5
|
+
|
6
|
+
$headers = ["stdio.h", "readline/readline.h"]
|
7
|
+
|
8
|
+
exit unless have_library("readline", "readline")
|
9
|
+
|
10
|
+
$headers.each { |h| exit unless have_header(h) }
|
11
|
+
|
12
|
+
%w{"rl_line_buffer"
|
13
|
+
"rl_insert_text"
|
14
|
+
"rl_parse_and_bind"
|
15
|
+
"rl_redisplay"}.each { |f| exit unless have_func(f, $headers) }
|
16
|
+
|
17
|
+
%w{"rl_pre_input_hook"
|
18
|
+
"rl_getc_function"}.each { |f| exit unless have_var(f, $headers) }
|
19
|
+
|
20
|
+
create_makefile("readline_extra")
|
@@ -0,0 +1,172 @@
|
|
1
|
+
|
2
|
+
#include <stdio.h>
|
3
|
+
|
4
|
+
#include <readline/readline.h>
|
5
|
+
|
6
|
+
#include "ruby.h"
|
7
|
+
#include "rubyio.h"
|
8
|
+
|
9
|
+
#define PRE_INPUT_PROC "pre_input_proc"
|
10
|
+
static ID pre_input_proc;
|
11
|
+
|
12
|
+
#define CHAR_INPUT_PROC "char_input_proc"
|
13
|
+
static ID char_input_proc;
|
14
|
+
|
15
|
+
|
16
|
+
VALUE mReadlineExtra = Qnil;
|
17
|
+
|
18
|
+
void Init_readline_extra();
|
19
|
+
|
20
|
+
static VALUE readline_extra_s_get_line_buffer(VALUE self);
|
21
|
+
static VALUE readline_extra_s_set_line_buffer(VALUE self, VALUE str);
|
22
|
+
static VALUE readline_extra_insert_text(VALUE self, VALUE text);
|
23
|
+
static VALUE readline_extra_parse_and_bind(VALUE self, VALUE text);
|
24
|
+
static VALUE readline_extra_redisplay(VALUE self);
|
25
|
+
|
26
|
+
static VALUE readline_extra_s_set_pre_input_proc(VALUE self, VALUE proc);
|
27
|
+
static VALUE readline_extra_s_get_pre_input_proc(VALUE self);
|
28
|
+
static int readline_extra_on_pre_input_hook(void);
|
29
|
+
|
30
|
+
int readline_extra_getc(FILE *stream);
|
31
|
+
static VALUE readline_extra_s_set_char_input_proc(VALUE self, VALUE proc);
|
32
|
+
static VALUE readline_extra_s_get_char_input_proc(VALUE self);
|
33
|
+
static int readline_extra_on_char_input_hook(int c);
|
34
|
+
|
35
|
+
void Init_readline_extra()
|
36
|
+
{
|
37
|
+
mReadlineExtra = rb_define_module("Readline");
|
38
|
+
|
39
|
+
pre_input_proc = rb_intern(PRE_INPUT_PROC);
|
40
|
+
rl_pre_input_hook = readline_extra_on_pre_input_hook;
|
41
|
+
|
42
|
+
rb_define_singleton_method(mReadlineExtra, "pre_input_proc=", readline_extra_s_set_pre_input_proc, 1);
|
43
|
+
rb_define_singleton_method(mReadlineExtra, "pre_input_proc", readline_extra_s_get_pre_input_proc, 0);
|
44
|
+
|
45
|
+
rl_getc_function = readline_extra_getc;
|
46
|
+
char_input_proc = rb_intern(CHAR_INPUT_PROC);
|
47
|
+
|
48
|
+
rb_define_singleton_method(mReadlineExtra, "char_input_proc=", readline_extra_s_set_char_input_proc, 1);
|
49
|
+
rb_define_singleton_method(mReadlineExtra, "char_input_proc", readline_extra_s_get_char_input_proc, 0);
|
50
|
+
|
51
|
+
rb_define_module_function(mReadlineExtra, "line_buffer", readline_extra_s_get_line_buffer, 0);
|
52
|
+
rb_define_module_function(mReadlineExtra, "line_buffer=", readline_extra_s_set_line_buffer, 1);
|
53
|
+
rb_define_module_function(mReadlineExtra, "insert_text", readline_extra_insert_text, 1);
|
54
|
+
rb_define_module_function(mReadlineExtra, "parse_and_bind", readline_extra_parse_and_bind, 1);
|
55
|
+
rb_define_module_function(mReadlineExtra, "redisplay", readline_extra_redisplay, 0);
|
56
|
+
|
57
|
+
}
|
58
|
+
|
59
|
+
static VALUE readline_extra_s_get_line_buffer(VALUE self)
|
60
|
+
{
|
61
|
+
if (rl_line_buffer)
|
62
|
+
return rb_tainted_str_new2(rl_line_buffer);
|
63
|
+
else
|
64
|
+
return rb_tainted_str_new2("");
|
65
|
+
}
|
66
|
+
|
67
|
+
static VALUE readline_extra_s_set_line_buffer(VALUE self, VALUE str)
|
68
|
+
{
|
69
|
+
rl_replace_line(RSTRING(str)->ptr, 1);
|
70
|
+
rl_point = rl_end;
|
71
|
+
|
72
|
+
return readline_extra_s_get_line_buffer(self);
|
73
|
+
}
|
74
|
+
|
75
|
+
static VALUE readline_extra_insert_text(VALUE self, VALUE text)
|
76
|
+
{
|
77
|
+
rl_insert_text(RSTRING(text)->ptr);
|
78
|
+
return self;
|
79
|
+
}
|
80
|
+
|
81
|
+
// doesn't seem to work
|
82
|
+
static VALUE readline_extra_parse_and_bind(VALUE self, VALUE text)
|
83
|
+
{
|
84
|
+
rl_parse_and_bind(RSTRING(text)->ptr);
|
85
|
+
return self;
|
86
|
+
}
|
87
|
+
|
88
|
+
static VALUE readline_extra_redisplay(VALUE self)
|
89
|
+
{
|
90
|
+
rl_redisplay();
|
91
|
+
return self;
|
92
|
+
}
|
93
|
+
|
94
|
+
static int
|
95
|
+
readline_extra_on_pre_input_hook(void)
|
96
|
+
{
|
97
|
+
VALUE proc;
|
98
|
+
|
99
|
+
proc = rb_attr_get(mReadlineExtra, pre_input_proc);
|
100
|
+
|
101
|
+
if (NIL_P(proc))
|
102
|
+
return -1;
|
103
|
+
|
104
|
+
rb_funcall(proc, rb_intern("call"), 0);
|
105
|
+
|
106
|
+
return 0;
|
107
|
+
}
|
108
|
+
|
109
|
+
static VALUE
|
110
|
+
readline_extra_s_set_pre_input_proc(VALUE self, VALUE proc)
|
111
|
+
{
|
112
|
+
rb_secure(4);
|
113
|
+
|
114
|
+
if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call")))
|
115
|
+
rb_raise(rb_eArgError, "argument must respond to `call'");
|
116
|
+
|
117
|
+
return rb_ivar_set(mReadlineExtra, pre_input_proc, proc);
|
118
|
+
}
|
119
|
+
|
120
|
+
static VALUE
|
121
|
+
readline_extra_s_get_pre_input_proc(VALUE self)
|
122
|
+
{
|
123
|
+
rb_secure(4);
|
124
|
+
return rb_attr_get(mReadlineExtra, pre_input_proc);
|
125
|
+
}
|
126
|
+
|
127
|
+
int readline_extra_getc(FILE *stream)
|
128
|
+
{
|
129
|
+
int c;
|
130
|
+
c = rl_getc(stream);
|
131
|
+
return readline_extra_on_char_input_hook(c);
|
132
|
+
}
|
133
|
+
|
134
|
+
static int
|
135
|
+
readline_extra_on_char_input_hook(int c)
|
136
|
+
{
|
137
|
+
VALUE proc, ret;
|
138
|
+
|
139
|
+
proc = rb_attr_get(mReadlineExtra, char_input_proc);
|
140
|
+
|
141
|
+
if (NIL_P(proc))
|
142
|
+
return c;
|
143
|
+
|
144
|
+
ret = rb_funcall(proc, rb_intern("call"), 1, INT2FIX(c));
|
145
|
+
|
146
|
+
if(ret == Qnil)
|
147
|
+
return 0;
|
148
|
+
|
149
|
+
if(!FIXNUM_P(ret))
|
150
|
+
rb_raise(rb_eTypeError, "Readline::char_input_proc must return nil or a Fixnum");
|
151
|
+
|
152
|
+
return (int)FIX2INT(ret);
|
153
|
+
}
|
154
|
+
|
155
|
+
static VALUE
|
156
|
+
readline_extra_s_set_char_input_proc(VALUE self, VALUE proc)
|
157
|
+
{
|
158
|
+
rb_secure(4);
|
159
|
+
|
160
|
+
if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call")))
|
161
|
+
rb_raise(rb_eArgError, "argument must respond to `call'");
|
162
|
+
|
163
|
+
return rb_ivar_set(mReadlineExtra, char_input_proc, proc);
|
164
|
+
}
|
165
|
+
|
166
|
+
static VALUE
|
167
|
+
readline_extra_s_get_char_input_proc(VALUE self)
|
168
|
+
{
|
169
|
+
rb_secure(4);
|
170
|
+
return rb_attr_get(mReadlineExtra, char_input_proc);
|
171
|
+
}
|
172
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
module ContactsCommandMixin
|
6
|
+
SORTBY = ["status", "alpha"]
|
7
|
+
|
8
|
+
def completer(line)
|
9
|
+
sortby = line.split[1] || ""
|
10
|
+
if SORTBY.grep(sortby).empty?
|
11
|
+
SORTBY.grep(/^#{Regexp.escape(sortby)}/)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def group_contacts(contacts)
|
16
|
+
grouped = {}
|
17
|
+
contacts.each do |jid,contact|
|
18
|
+
groups = contact.roster_item.groups
|
19
|
+
if groups.empty?
|
20
|
+
(grouped["<no group>"] ||= []) << contact
|
21
|
+
else
|
22
|
+
groups.each { |g| (grouped[g] ||= []) << contact }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
grouped = grouped.sort_by { |g,c| g }
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_contacts(sort_by_status=false, online_only=false)
|
30
|
+
if sort_by_status
|
31
|
+
contacts = Main.contacts.sort { |a, b| -(a[1].presence <=> b[1].presence) }
|
32
|
+
else
|
33
|
+
contacts = Main.contacts.sort_by { |j,c| c.simple_name }
|
34
|
+
end
|
35
|
+
|
36
|
+
if Config.opts[:show_groups_in_contact_list]
|
37
|
+
grouped = group_contacts(contacts)
|
38
|
+
else
|
39
|
+
grouped = {nil => contacts.map { |j,c| c }}
|
40
|
+
end
|
41
|
+
|
42
|
+
s = []
|
43
|
+
grouped.each do |group, contactz|
|
44
|
+
if online_only
|
45
|
+
next unless contactz.any? { |c| c.online? }
|
46
|
+
end
|
47
|
+
|
48
|
+
s << " #{group} ".on_blue if group
|
49
|
+
contactz.each do |contact|
|
50
|
+
unless online_only && !contact.online?
|
51
|
+
main = contact.presence
|
52
|
+
s << "* #{contact.simple_name} #{main.pretty(true)} " \
|
53
|
+
"(#{main.priority || 0}) [#{main.from || contact.jid}]"
|
54
|
+
|
55
|
+
if online_only && contact.roster_item.presences.length > 1
|
56
|
+
contact.roster_item.presences.each do |p|
|
57
|
+
if p.from != main.from
|
58
|
+
s << " #{p.from} #{p.pretty(true)} (#{p.priority || 0})"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
Out::put(s.join("\n")) unless s.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
Command.define :contacts do
|
72
|
+
usage "/contacts [status|alpha]"
|
73
|
+
description "Show a list of all contacts. Sorted alphabetically or by status."
|
74
|
+
|
75
|
+
SORTBY = ["status", "alpha"]
|
76
|
+
|
77
|
+
def run(args)
|
78
|
+
print_contacts(args.split[0] == "status")
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
include ContactsCommandMixin
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
Command.define :who do
|
87
|
+
usage "/who [status|alpha]"
|
88
|
+
description "Show a list of online contacts. Sorted alphabetically or by status."
|
89
|
+
|
90
|
+
def run(args)
|
91
|
+
print_contacts(args.split[0] == "status", true)
|
92
|
+
end
|
93
|
+
|
94
|
+
class << self
|
95
|
+
include ContactsCommandMixin
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
Command.define :set do
|
6
|
+
usage "/set <option> [<value>]"
|
7
|
+
description "Modify the options. Print the current value if no <value> is given.\n" \
|
8
|
+
"See #{Config.files[:config]} for the available options."
|
9
|
+
def run(args)
|
10
|
+
option, value = args.split(nil, 2).strip
|
11
|
+
|
12
|
+
option = option.to_sym if option
|
13
|
+
|
14
|
+
if !(Config.opts[option].is_a?(String) ||
|
15
|
+
Config.opts[option].is_a?(Numeric) ||
|
16
|
+
[true, false, nil].include?(Config.opts[option])) &&
|
17
|
+
Config.opts.key?(option)
|
18
|
+
raise CommandError, %{can't change "#{option} with /set"}
|
19
|
+
elsif !Config.opts.key?(option)
|
20
|
+
raise CommandError, %{no such option "#{option}"}
|
21
|
+
end
|
22
|
+
|
23
|
+
if value && !value.empty?
|
24
|
+
begin
|
25
|
+
val = YAML.load(value)
|
26
|
+
#raise TypeError unless val.is_a?(Config.opts[option].class)
|
27
|
+
Config.opts[option] = val
|
28
|
+
rescue
|
29
|
+
Out::error("invalid value", false)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
Out::put(YAML.dump(Config.opts[option])[4..-1].chomp)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def completer(line)
|
38
|
+
option = line.split(nil, 2).strip[1] || ""
|
39
|
+
Config.opts.keys.find_all do |k|
|
40
|
+
k.to_s =~ /^#{Regexp.escape(option)}/ &&
|
41
|
+
(Config.opts[k].is_a?(String) ||
|
42
|
+
Config.opts[k].is_a?(Numeric) ||
|
43
|
+
[true, false, nil].include?(Config.opts[k]))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
Command.define :help do
|
6
|
+
usage "/help [<command> | commands]"
|
7
|
+
description "Get some help."
|
8
|
+
|
9
|
+
def run(args)
|
10
|
+
if args.empty?
|
11
|
+
s = %Q{
|
12
|
+
When in doubt, hit <tab>.
|
13
|
+
|
14
|
+
Some general hints:
|
15
|
+
|
16
|
+
Run "lijab -a <name>" to connect to the account named <name>.
|
17
|
+
|
18
|
+
Tab on an empty line will try to complete online contacts.
|
19
|
+
If there are no online contact matches for what you typed, offline contacts will also be
|
20
|
+
considered.
|
21
|
+
|
22
|
+
You can tab-complete specific resources of a contact by typing the contact name
|
23
|
+
followed by an @ character, e.g. somecontact@<tab> will complete all the available
|
24
|
+
resources for the contact and a message can be sent to that specific resource.
|
25
|
+
|
26
|
+
Config/logs folder is at #{Config.basedir}
|
27
|
+
|
28
|
+
Put your custom commands in #{Config.dirs[:commands]}
|
29
|
+
Check out the files in <install-path>/lib/lijab/commands/ for some examples.
|
30
|
+
|
31
|
+
Put your custom hooks in #{Config.dirs[:hooks]}
|
32
|
+
|
33
|
+
Send mails to quuxbaz@gmail.com to complain about the lack of documentation :-)
|
34
|
+
|
35
|
+
}.gsub!(/^ */, '')
|
36
|
+
Out::put(s)
|
37
|
+
else
|
38
|
+
if args == "commands"
|
39
|
+
Out::put
|
40
|
+
Commands::registered.each do |name, cmd|
|
41
|
+
Out::put(%{#{cmd.usage || "/#{name}"}}.magenta)
|
42
|
+
Out::put("#{cmd.description}\n\n")
|
43
|
+
end
|
44
|
+
else
|
45
|
+
cmd = Commands::get(args)
|
46
|
+
if cmd
|
47
|
+
s = "usage: #{cmd.usage}\n\n" if cmd.usage
|
48
|
+
s = "#{s}#{cmd.description}"
|
49
|
+
Out::put(s)
|
50
|
+
else
|
51
|
+
raise CommandError, %(No such command "#{args}")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def completer(line)
|
58
|
+
help_cmd, rest = line.split(" ", 2)
|
59
|
+
rest ||= ""
|
60
|
+
|
61
|
+
m = "commands" =~ /^#{Regexp.escape(rest)}/ ? ["commands"] : []
|
62
|
+
|
63
|
+
rest = "/#{rest}"
|
64
|
+
|
65
|
+
m += Commands::completer(rest).map { |c| c[1..-1] } if rest.split(" ", 2).length == 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Command.define :history do
|
70
|
+
usage "/history [<contact>] [<limit>]"
|
71
|
+
description "Show the message history with a <contact>, or all the contacts."
|
72
|
+
|
73
|
+
def run(args)
|
74
|
+
contact, limit = args.split(" ", 2).strip
|
75
|
+
limit ||= 10
|
76
|
+
|
77
|
+
if contact
|
78
|
+
raise CommandError, %(No contact named "#{contact}) unless Main.contacts.key?(contact)
|
79
|
+
m = Main.contacts[contact].history.last(limit.to_i)
|
80
|
+
else
|
81
|
+
m = HistoryHandler::last(limit.to_i)
|
82
|
+
end
|
83
|
+
Out::history(*m)
|
84
|
+
end
|
85
|
+
|
86
|
+
class << self
|
87
|
+
include ContactCompleterMixin
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
Command.define :multiline do
|
92
|
+
usage "/multiline <contact> [<first_line>]"
|
93
|
+
description "Enter multiline mode, meaning, send a multiline message to a contact.\n" \
|
94
|
+
"Ctrl-d in an empty line exits multiline mode and sends the message."
|
95
|
+
|
96
|
+
def run(args)
|
97
|
+
contact, first_line = args.split(" ", 2).strip
|
98
|
+
first_line = "#{contact}: #{first_line}"
|
99
|
+
InputHandler::multiline(true, first_line)
|
100
|
+
end
|
101
|
+
|
102
|
+
class << self
|
103
|
+
include ContactCompleterMixin
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
Command.define :quit do
|
108
|
+
usage "/quit"
|
109
|
+
description "Quit lijab"
|
110
|
+
|
111
|
+
def run(args)
|
112
|
+
Main.quit
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO: make a generic option changer?
|
117
|
+
Command.define :show_status_changes do
|
118
|
+
usage "/show_status_changes yes|no"
|
119
|
+
description "Enable/disable printing the contacts' status changes. Can get quite spammish."
|
120
|
+
|
121
|
+
def run(args)
|
122
|
+
if !args || args.empty?
|
123
|
+
Out::put(Config.opts[:show_status_changes] ? "yes" : "no")
|
124
|
+
else
|
125
|
+
Config.opts[:show_status_changes] = args.strip == "yes"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
Command.define :priority do
|
6
|
+
usage "/priority [<priority>]"
|
7
|
+
description "Change the jabber priority. Number must be between -127 and 127.\n" \
|
8
|
+
"Show current priority if no argument is given."
|
9
|
+
|
10
|
+
def run(args)
|
11
|
+
if args.strip.empty?
|
12
|
+
Out::put("Current priority is #{Main.presence.priority}")
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
p = Integer(args)
|
18
|
+
rescue ArgumentError
|
19
|
+
raise Commands::CommandError, %{"#{args}" is not a valid integer}
|
20
|
+
end
|
21
|
+
|
22
|
+
raise Commands::CommandError, "priority must be between -127 and 127" unless (-127..127).include?(p)
|
23
|
+
|
24
|
+
Main.set_priority(p)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Command.define :status do
|
29
|
+
usage "/status [available|away|chat|xa|dnd|invisible] [<message>]"
|
30
|
+
description "Set your status.\n" \
|
31
|
+
"If no status is given, keep the current and set the status message.\n" \
|
32
|
+
"If no message is given, keep the current status and clear the message.\n" \
|
33
|
+
"If no arguments are given, print the current status."
|
34
|
+
|
35
|
+
STATUSES = ["available", "away", "chat", "xa", "dnd", "invisible"]
|
36
|
+
|
37
|
+
def run(args)
|
38
|
+
status, message = args.split(" ", 2).strip
|
39
|
+
|
40
|
+
unless status
|
41
|
+
p = Main.presence
|
42
|
+
Out::put("#{Config.jid} (#{p.priority || 0}) #{p.pretty(true)}")
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
unless STATUSES.include?(status)
|
47
|
+
message = "#{status} #{message}".strip
|
48
|
+
status = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
Main.set_status(status && status.to_sym, message)
|
52
|
+
end
|
53
|
+
|
54
|
+
def completer(line)
|
55
|
+
status = line.split[1] || ""
|
56
|
+
if STATUSES.grep(status).empty?
|
57
|
+
STATUSES.grep(/^#{Regexp.escape(status)}/)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
module Commands
|
4
|
+
Command.define :add do
|
5
|
+
usage "/add <user@server>"
|
6
|
+
description "Add a user to your roster."
|
7
|
+
|
8
|
+
def run(args)
|
9
|
+
Main.contacts.add(args)
|
10
|
+
Out::put("subscription request sent to #{args}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Command.define :remove do
|
15
|
+
usage "/remove <user@server>"
|
16
|
+
description "Remove a user from your roster."
|
17
|
+
|
18
|
+
def run(args)
|
19
|
+
unless Main.contacts.remove(args)
|
20
|
+
raise CommandError, "no contact found for #{args}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
include ContactCompleterMixin
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO: <user@server | all>
|
30
|
+
Command.define :requests do
|
31
|
+
usage "/requests [accept|accept_and_add|decline <user@server>]"
|
32
|
+
description "Accept/decline a user's request to see your status.\n" \
|
33
|
+
"Print pending requests if no argument given." \
|
34
|
+
|
35
|
+
ACTIONS = ["accept", "accept_and_add", "decline"]
|
36
|
+
|
37
|
+
def run(args)
|
38
|
+
action, addr = args.split(nil, 2).strip
|
39
|
+
|
40
|
+
if action
|
41
|
+
unless ACTIONS.include?(action)
|
42
|
+
raise CommandError, "action must be accept, accept_and_add or decline"
|
43
|
+
end
|
44
|
+
raise CommandError, "need the user's address" unless addr and !addr.empty?
|
45
|
+
|
46
|
+
if ["accept", "accept_and_add"].include?(action)
|
47
|
+
success = Main.contacts.process_request(addr, :accept)
|
48
|
+
Main.contacts.add(addr) if success && action == "accept_and_add"
|
49
|
+
else
|
50
|
+
success = Main.contacts.process_request(addr, :decline)
|
51
|
+
end
|
52
|
+
|
53
|
+
raise CommandError, "no pending request from #{addr}" unless success
|
54
|
+
else
|
55
|
+
if Main.contacts.has_subscription_requests?
|
56
|
+
Out::put("pending requests from:")
|
57
|
+
Out::put(Main.contacts.subscription_requests.join("\n"))
|
58
|
+
else
|
59
|
+
Out::put("no pending requests")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def completer(line)
|
65
|
+
_, action, addr = line.split(nil, 3)
|
66
|
+
|
67
|
+
if !addr
|
68
|
+
ACTIONS.grep(/^#{Regexp.escape(action)}/)
|
69
|
+
elsif addr && ACTIONS.include?(action)
|
70
|
+
Main.contacts.subscription_requests.grep(/^#{Regexp.escape(addr)}/).map do |c|
|
71
|
+
"#{action} #{c}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|