palbo-lijab 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|