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
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
|
4
|
+
module Commands
|
5
|
+
@registered = {}
|
6
|
+
@overloaded = {}
|
7
|
+
|
8
|
+
module CommandMixin
|
9
|
+
def define_meta(*names)
|
10
|
+
class_eval do
|
11
|
+
names.each do |name|
|
12
|
+
define_method(name) do |*args|
|
13
|
+
if args.size == 0
|
14
|
+
instance_variable_get("@#{name}")
|
15
|
+
else
|
16
|
+
instance_variable_set("@#{name}", *args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ContactCompleterMixin
|
25
|
+
def completer(line)
|
26
|
+
_, contact = line.split(nil, 2)
|
27
|
+
Main.contacts.completer(contact, false)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class CommandError < RuntimeError
|
32
|
+
end
|
33
|
+
|
34
|
+
class Command
|
35
|
+
class << self
|
36
|
+
private :new
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.define(name, &block)
|
40
|
+
c = new
|
41
|
+
c.instance_eval(&block)
|
42
|
+
Commands::register(name.to_sym, c)
|
43
|
+
end
|
44
|
+
|
45
|
+
def completer(s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(args)
|
49
|
+
end
|
50
|
+
|
51
|
+
extend CommandMixin
|
52
|
+
define_meta :usage, :description
|
53
|
+
end
|
54
|
+
|
55
|
+
module_function
|
56
|
+
|
57
|
+
def init
|
58
|
+
files = Dir[File.join(File.dirname(File.expand_path(__FILE__)), 'commands', '*.rb')] + \
|
59
|
+
Dir[File.join(Config.dirs[:commands], '**', '*.rb')]
|
60
|
+
|
61
|
+
files.each { |f| load f }
|
62
|
+
Config.opts[:aliases].each do |a, c|
|
63
|
+
register_alias(a, c)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def register(name, cmd)
|
68
|
+
name = name.to_sym
|
69
|
+
@overloaded[name] = @registered[name] if @registered.key?(name)
|
70
|
+
@registered[name] = cmd
|
71
|
+
end
|
72
|
+
|
73
|
+
def register_alias(name, s)
|
74
|
+
alias_cmd, alias_args = s.split(" ", 2)
|
75
|
+
alias_cmd.strip!
|
76
|
+
alias_cmd = alias_cmd[1..-1] if alias_cmd[0] == ?/
|
77
|
+
name = name[1..-1] if name[0] == ?/
|
78
|
+
|
79
|
+
Command.define name.to_sym do
|
80
|
+
description %{Alias for "#{s}"}
|
81
|
+
@alias_cmd = alias_cmd
|
82
|
+
@alias_args = alias_args
|
83
|
+
@name = name
|
84
|
+
|
85
|
+
def run(args)
|
86
|
+
Commands::run(@alias_cmd, [@alias_args, args].join(" "), true)
|
87
|
+
end
|
88
|
+
|
89
|
+
def completer(line)
|
90
|
+
args = line.split(" ", 2)[1]
|
91
|
+
Commands::completer("/#{@alias_cmd} #{@alias_args} #{args}").map do |r|
|
92
|
+
r.gsub(/\/#{@alias_cmd}\s?/, "")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def get(name)
|
99
|
+
@registered[name.to_sym]
|
100
|
+
end
|
101
|
+
|
102
|
+
def registered?(name)
|
103
|
+
@registered.key?(name.to_sym)
|
104
|
+
end
|
105
|
+
|
106
|
+
def run(cmd, args="", is_alias=false)
|
107
|
+
cmd = cmd.strip.to_sym
|
108
|
+
command = @overloaded[cmd] if is_alias
|
109
|
+
command ||= @registered[cmd]
|
110
|
+
if command
|
111
|
+
begin
|
112
|
+
command.run(args.strip)
|
113
|
+
rescue CommandError => e
|
114
|
+
Out::error("#{cmd}: #{e}", false)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
Out::error("no such command: /#{cmd}", false)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def completer(line)
|
122
|
+
cmd, args = line[1..-1].split(" ", 2).strip
|
123
|
+
cmd ||= ""
|
124
|
+
|
125
|
+
matches = @registered.keys.find_all { |c| c.to_s.match(/^#{Regexp.escape(cmd)}/) }
|
126
|
+
|
127
|
+
if !cmd.empty? && (matches.length == 1 || args) && registered?(cmd)
|
128
|
+
(@registered[cmd.to_sym].completer(line) || []).map { |s| "/#{cmd} #{s}" }
|
129
|
+
else
|
130
|
+
matches.map { |k| "/#{k}" }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
attr_reader :registered
|
135
|
+
module_function :registered
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
data/lib/lijab/config.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
|
2
|
+
module Lijab
|
3
|
+
|
4
|
+
module Config
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def init(args)
|
8
|
+
@opts = {}
|
9
|
+
@dirs = {}
|
10
|
+
@files = {}
|
11
|
+
@accounts = []
|
12
|
+
@account = nil
|
13
|
+
|
14
|
+
setup_basedir(args[:basedir])
|
15
|
+
read_accounts(args[:account])
|
16
|
+
read_options()
|
17
|
+
|
18
|
+
@jid = Jabber::JID.new("#{@account[:jabberid]}")
|
19
|
+
@jid.resource ||= "lijab#{(0...5).map{rand(10).to_s}.join}"
|
20
|
+
@account[:server] ||= @jid.domain
|
21
|
+
|
22
|
+
create_account_dirs()
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_basedir(basedir)
|
26
|
+
# xdg? meh
|
27
|
+
#xdg = ENV["XDG_CONFIG_HOME"]
|
28
|
+
#xdg && File.join(xdg, "lijab") ||
|
29
|
+
|
30
|
+
@basedir = basedir ||
|
31
|
+
ENV["LIJABDIR"] ||
|
32
|
+
"~/.lijab"
|
33
|
+
@basedir = File.expand_path(@basedir)
|
34
|
+
|
35
|
+
unless File.directory?(@basedir)
|
36
|
+
puts "Creating #{@basedir} with the default configs"
|
37
|
+
end
|
38
|
+
|
39
|
+
%w{accounts commands hooks}.each do |d|
|
40
|
+
@dirs[d.to_sym] = path = File.join(@basedir, d)
|
41
|
+
FileUtils.mkdir_p(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
@files[:accounts] = path = File.join(@basedir, "accounts.yml")
|
45
|
+
File.open(path, 'w') { |f| f.puts(DEFAULT_ACCOUNTS_FILE) } unless File.file?(path)
|
46
|
+
|
47
|
+
@files[:config] = File.join(@basedir, "config.yml")
|
48
|
+
dump_config_file(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump_config_file(default=false, clobber=false)
|
52
|
+
if !File.file?(@files[:config]) || clobber
|
53
|
+
File.open(@files[:config], 'w') do |f|
|
54
|
+
DEFAULT_OPTIONS.each do |a|
|
55
|
+
if a[2]
|
56
|
+
f.puts
|
57
|
+
a[2].each { |l| f.puts("# #{l}") }
|
58
|
+
end
|
59
|
+
v = default ? a[1] : @opts[a[0]]
|
60
|
+
f.puts(YAML.dump({a[0] => v})[5..-1].chomp)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def read_accounts(account)
|
67
|
+
File.open(@files[:accounts]) do |f|
|
68
|
+
YAML.load_documents(f) { |a| @accounts << a }
|
69
|
+
end
|
70
|
+
|
71
|
+
errors = []
|
72
|
+
errors << "need at least one account!" if @accounts.empty?
|
73
|
+
|
74
|
+
@accounts.each do |a|
|
75
|
+
a[:port] ||= a[:use_ssl] ? 5223 : 5222
|
76
|
+
|
77
|
+
errors << "account #{a} needs a name" unless a.key?(:name)
|
78
|
+
errors << "account #{a[:name] || a} needs a jabberid" unless a.key?(:jabberid)
|
79
|
+
end
|
80
|
+
|
81
|
+
@account = account ? @accounts.find { |a| a[:name] == account} : @accounts[0]
|
82
|
+
|
83
|
+
errors << "no account with name #{account} in #{@accounts_file}" if account && !@account
|
84
|
+
|
85
|
+
errors.each do |e|
|
86
|
+
STDERR.puts("#{File.basename($0)}: error: #{e}")
|
87
|
+
end
|
88
|
+
|
89
|
+
exit(1) unless errors.empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_options
|
93
|
+
# FIXME: error check / validate
|
94
|
+
|
95
|
+
@opts = Hash[*DEFAULT_OPTIONS.collect { |a| [a[0], a[1]] }.flatten]
|
96
|
+
@opts.merge!(YAML.load_file(@files[:config]))
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_account_dirs
|
100
|
+
@accounts.each do |a|
|
101
|
+
a[:dir] = File.join(@dirs[:accounts], @jid.strip.to_s)
|
102
|
+
a[:log_dir] = File.join(a[:dir], "logs")
|
103
|
+
a[:typed] = File.join(a[:dir], "typed_history")
|
104
|
+
|
105
|
+
[:dir, :log_dir].each { |s| FileUtils.mkdir_p(a[s]) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
DEFAULT_OPTIONS = [
|
110
|
+
[:datetime_format, "%H:%M:%S", ["Time formatting (leave empty to disable timestamps)"]],
|
111
|
+
[:history_datetime_format, "%Y-%b-%d %H:%M:%S"],
|
112
|
+
[:autocomplete_online_first, true,
|
113
|
+
["When completing contacts try to find matches for online contacts, and if none",
|
114
|
+
"is found try to find matches on all of them. Otherwise always match every",
|
115
|
+
"contact."]],
|
116
|
+
[:show_status_changes, true, ["Show changes in contacts' status"]],
|
117
|
+
[:show_groups_in_contact_list, false, ["Group contacts in contact list"]],
|
118
|
+
[:ctrl_c_quits, false,
|
119
|
+
["ctrl+c quits the program if enabled, otherwise ctrl+c ignores whatever is",
|
120
|
+
"typed and you get a clean prompt, and ctrl+d on a clean line exits lijab,",
|
121
|
+
"terminal style."]],
|
122
|
+
[:terminal_bell_on_message, true,
|
123
|
+
["Ring the terminal bell on incoming message.",
|
124
|
+
"Useful for setting the urgent hint on the terminal window:",
|
125
|
+
"Set as so in your ~/.Xdefaults, might have to run xrdb -merge ~/.Xdefaults afterwards",
|
126
|
+
"XTerm*bellIsUrgent: true",
|
127
|
+
"or",
|
128
|
+
"URxvt*urgentOnBell: true",
|
129
|
+
"or just look it up on your terminal's man page, don't be lazy."]],
|
130
|
+
[:status_priorities,
|
131
|
+
{:chat => 55, :available => 50, :away => 40, :xa => 30, :dnd => 20},
|
132
|
+
["Default priority for each status"]],
|
133
|
+
[:aliases, {"/h" => "/history", "/exit" => "/quit"},
|
134
|
+
["Command aliases.",
|
135
|
+
"<command_alias> : <existing_command>",
|
136
|
+
"Commands can be overloaded.",
|
137
|
+
"For instance /who could be redefined like so to sort by status by default.",
|
138
|
+
"/who : /who status"]]
|
139
|
+
]
|
140
|
+
|
141
|
+
DEFAULT_ACCOUNTS_FILE = %Q{
|
142
|
+
# Accounts go here. Separate each one with ---
|
143
|
+
# First one is the default.
|
144
|
+
|
145
|
+
#---
|
146
|
+
#:name : an_account # the account name
|
147
|
+
#:jabberid : fisk@example.com/lijab # the resource is optional
|
148
|
+
#:password : frosk # optional, will prompt if not present
|
149
|
+
#:server : localhost # optional, will use the jid domain if not present
|
150
|
+
#:port : 5222 # optional
|
151
|
+
#:use_ssl : no # deprecated in jabber, but might help sometimes
|
152
|
+
#:log : yes # yes|no ; default no
|
153
|
+
|
154
|
+
#---
|
155
|
+
#:name : another_account
|
156
|
+
#:jabberid : another_user@example.com/lijab
|
157
|
+
|
158
|
+
#---
|
159
|
+
#:name : gmail_account
|
160
|
+
#:jabberid : blah@gmail.com/lijab
|
161
|
+
#:server : talk.google.com
|
162
|
+
## might wanna try use_ssl if the normal settings don't work (e.g. in ubuntu afaik)
|
163
|
+
##:port : 5223
|
164
|
+
##:use_ssl : yes
|
165
|
+
#:log : yes
|
166
|
+
|
167
|
+
}.gsub!(/^ */, '')
|
168
|
+
|
169
|
+
attr_reader :jid, :account, :basedir, :dirs, :files, :opts
|
170
|
+
module_function :jid, :account, :basedir, :dirs, :files, :opts
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
@@ -0,0 +1,318 @@
|
|
1
|
+
|
2
|
+
class Jabber::Presence
|
3
|
+
PRETTY = Hash.new(["", []]).update(
|
4
|
+
{ :available => ["available", [:green, :bold]],
|
5
|
+
:away => ["away", [:magenta]],
|
6
|
+
:chat => ["chatty", [:green]],
|
7
|
+
:dnd => ["busy", [:red, :bold]],
|
8
|
+
:xa => ["not available", [:red]],
|
9
|
+
:offline => ["offline", [:blue]]
|
10
|
+
})
|
11
|
+
|
12
|
+
def pretty_show
|
13
|
+
case type()
|
14
|
+
when nil
|
15
|
+
show() || :available
|
16
|
+
when :unavailable
|
17
|
+
:offline
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def pretty(colorize=false)
|
22
|
+
sh = pretty_show()
|
23
|
+
|
24
|
+
s, colors = PRETTY[sh]
|
25
|
+
s = s.colored(*colors) if colorize
|
26
|
+
message = status() && !status().empty? ? " [#{status()}]" : ""
|
27
|
+
"#{s}#{message}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Lijab
|
32
|
+
module Contacts
|
33
|
+
|
34
|
+
class Contact
|
35
|
+
attr_accessor :simple_name, :history
|
36
|
+
attr_writer :color
|
37
|
+
attr_reader :roster_item
|
38
|
+
|
39
|
+
COLORS = [:red, :blue, :yellow, :green, :magenta, :cyan].sort_by { rand() }
|
40
|
+
@@cur_color = 0
|
41
|
+
|
42
|
+
def initialize(simple_name, roster_item)
|
43
|
+
@simple_name = simple_name
|
44
|
+
@roster_item = roster_item
|
45
|
+
@resource_jid = @roster_item.jid
|
46
|
+
@history = HistoryHandler::get(jid())
|
47
|
+
end
|
48
|
+
|
49
|
+
def presence(jid=nil)
|
50
|
+
if jid
|
51
|
+
p = @roster_item.presences.select { |p| p.jid == jid }.first
|
52
|
+
else
|
53
|
+
p = @roster_item.presences.inject(nil) do |max, presence|
|
54
|
+
!max.nil? && max.priority.to_i > presence.priority.to_i ? max : presence
|
55
|
+
end
|
56
|
+
end
|
57
|
+
p || Jabber::Presence.new.set_type(:unavailable)
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_message(msg)
|
61
|
+
@thread = msg.thread
|
62
|
+
|
63
|
+
if msg.body && !msg.body.empty?
|
64
|
+
@resource_jid = msg.from
|
65
|
+
Out::message_in(@simple_name, msg.body, [color(), :bold])
|
66
|
+
@history.log(msg.body, :from)
|
67
|
+
end
|
68
|
+
|
69
|
+
if msg.chat_state
|
70
|
+
s = ""
|
71
|
+
case msg.chat_state
|
72
|
+
when :composing
|
73
|
+
s = "is typing"
|
74
|
+
when :active
|
75
|
+
Out::clear_infoline
|
76
|
+
when :gone
|
77
|
+
s = "went away"
|
78
|
+
when :paused
|
79
|
+
s = "paused typing"
|
80
|
+
end
|
81
|
+
Out::infoline("* #{@simple_name} #{s}".red) unless s.empty?
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def send_message(msg, jid=nil)
|
86
|
+
if msg.kind_of?(Jabber::Message)
|
87
|
+
msg.thread = @thread unless msg.thread
|
88
|
+
message = msg
|
89
|
+
elsif msg.kind_of?(String) && !msg.empty?
|
90
|
+
# TODO: send chat_state only in the first message
|
91
|
+
if jid
|
92
|
+
@resource_jid = jid
|
93
|
+
else
|
94
|
+
jid = @resource_jid
|
95
|
+
end
|
96
|
+
Out::message_out(@simple_name, msg, color())
|
97
|
+
message = Jabber::Message.new(jid, msg).set_type(:chat) \
|
98
|
+
.set_chat_state(:active) \
|
99
|
+
.set_thread(@thread)
|
100
|
+
|
101
|
+
@chat_state_timer.kill if @chat_state_timer && @chat_state_timer.alive?
|
102
|
+
end
|
103
|
+
|
104
|
+
message = HooksHandler::handle_pre_send_message(self, message)
|
105
|
+
return unless message
|
106
|
+
|
107
|
+
Main.client.send(message)
|
108
|
+
|
109
|
+
HooksHandler::handle_post_send_message(self, message)
|
110
|
+
|
111
|
+
@history.log(message.body, :to)
|
112
|
+
@chat_state = :active
|
113
|
+
end
|
114
|
+
|
115
|
+
def send_chat_state(state)
|
116
|
+
return if state == @chat_state
|
117
|
+
msg = Jabber::Message.new(jid(), nil).set_type(:chat).set_chat_state(state)
|
118
|
+
Main.client.send(msg)
|
119
|
+
@chat_state = state
|
120
|
+
end
|
121
|
+
|
122
|
+
def typed_stuff
|
123
|
+
send_chat_state(:composing)
|
124
|
+
|
125
|
+
@chat_state_timer.kill if @chat_state_timer && @chat_state_timer.alive?
|
126
|
+
@chat_state_timer = Thread.new do
|
127
|
+
sleep(3); return if !Main.connected
|
128
|
+
send_chat_state(:paused)
|
129
|
+
sleep(10); return if !Main.connected
|
130
|
+
send_chat_state(:active)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def presence_changed(old_p, new_p)
|
135
|
+
@resource_jid = @roster_item.jid if old_p && old_p.from == @resource_jid
|
136
|
+
end
|
137
|
+
|
138
|
+
def jid
|
139
|
+
@roster_item.jid
|
140
|
+
end
|
141
|
+
|
142
|
+
def online?
|
143
|
+
@roster_item.online?
|
144
|
+
end
|
145
|
+
|
146
|
+
def color
|
147
|
+
@color = COLORS[(@@cur_color = (@@cur_color + 1) % COLORS.length)] unless @color
|
148
|
+
@color
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_s;
|
152
|
+
@simple_name
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class Contacts < Hash
|
157
|
+
attr_reader :roster, :subscription_requests
|
158
|
+
|
159
|
+
def initialize(roster)
|
160
|
+
super()
|
161
|
+
@roster = roster
|
162
|
+
|
163
|
+
# why does everything always has to be so hackish?
|
164
|
+
self_ri = Jabber::Roster::Helper::RosterItem.new(Main.client)
|
165
|
+
self_ri.jid = Config.jid.strip
|
166
|
+
@roster.items[self_ri.jid] = self_ri
|
167
|
+
|
168
|
+
@roster.add_presence_callback(&method(:handle_presence))
|
169
|
+
@roster.add_subscription_callback(&method(:handle_subscription))
|
170
|
+
@roster.add_subscription_request_callback(&method(:handle_subscription))
|
171
|
+
@roster.wait_for_roster
|
172
|
+
|
173
|
+
@subscription_requests = {}
|
174
|
+
@short = {}
|
175
|
+
|
176
|
+
@roster.items.each do |jid, item|
|
177
|
+
add(jid, Contact.new(jid.node, item))
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def [](k)
|
182
|
+
return @short[k] if @short.key?(k)
|
183
|
+
|
184
|
+
k = Jabber::JID.new(k) unless k.is_a?(Jabber::JID)
|
185
|
+
|
186
|
+
super(k) || super(k.strip)
|
187
|
+
end
|
188
|
+
|
189
|
+
def key?(k)
|
190
|
+
return true if @short.key?(k)
|
191
|
+
|
192
|
+
k = Jabber::JID.new(k) unless k.is_a?(Jabber::JID)
|
193
|
+
|
194
|
+
super(k) || super(k.strip)
|
195
|
+
end
|
196
|
+
|
197
|
+
def add(jid, contact=nil)
|
198
|
+
if contact
|
199
|
+
self[jid] = contact
|
200
|
+
if @short.key?(jid.node)
|
201
|
+
self[@short[jid.node].jid].simple_name = jid.strip.to_s
|
202
|
+
@short[@short[jid.node].jid.strip.to_s] = @short.delete(jid.node)
|
203
|
+
@short[jid.strip.to_s] = self[jid]
|
204
|
+
else
|
205
|
+
@short[jid.node] = self[jid]
|
206
|
+
end
|
207
|
+
else
|
208
|
+
jid = Jabber::JID.new(jid) unless jid.is_a?(Jabber::JID)
|
209
|
+
jid.strip!
|
210
|
+
|
211
|
+
@roster.add(jid, nil, true)
|
212
|
+
end
|
213
|
+
|
214
|
+
contact || self[jid]
|
215
|
+
end
|
216
|
+
|
217
|
+
def remove(jid)
|
218
|
+
return false unless key?(jid)
|
219
|
+
|
220
|
+
contact = self[jid]
|
221
|
+
contact.roster_item.remove()
|
222
|
+
@short.delete(contact.simple_name)
|
223
|
+
self.delete(contact.jid)
|
224
|
+
|
225
|
+
true
|
226
|
+
end
|
227
|
+
|
228
|
+
def process_request(jid, action)
|
229
|
+
jid = Jabber::JID.new(jid) unless jid.is_a?(Jabber::JID)
|
230
|
+
jid.strip!
|
231
|
+
if @subscription_requests.include?(jid)
|
232
|
+
|
233
|
+
@subscription_requests.delete(jid)
|
234
|
+
case action
|
235
|
+
when :accept
|
236
|
+
Main.contacts.roster.accept_subscription(jid)
|
237
|
+
when :decline
|
238
|
+
Main.contacts.roster.decline_subscription(jid)
|
239
|
+
end
|
240
|
+
|
241
|
+
true
|
242
|
+
else
|
243
|
+
false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def has_subscription_requests?
|
248
|
+
!@subscription_requests.empty?
|
249
|
+
end
|
250
|
+
|
251
|
+
def subscription_requests
|
252
|
+
@subscription_requests.keys.map { |jid| jid.to_s }
|
253
|
+
end
|
254
|
+
|
255
|
+
def completer(line, end_with_colon=true)
|
256
|
+
if line.include?(?@)
|
257
|
+
matches = @roster.items.values.collect { |ri| ri.presences }.flatten.select do |p|
|
258
|
+
p.from.to_s =~ /^#{Regexp.escape(line)}/
|
259
|
+
end.map { |p| p.from.to_s }
|
260
|
+
else
|
261
|
+
if Config.opts[:autocomplete_online_first]
|
262
|
+
matches = @short.keys.find_all do |name|
|
263
|
+
@short[name].online? && name =~ /^#{Regexp.escape(line)}/
|
264
|
+
end
|
265
|
+
end
|
266
|
+
if matches.empty? || !Config.opts[:autocomplete_online_first]
|
267
|
+
matches = @short.keys.find_all { |name| name =~ /^#{Regexp.escape(line)}/ }
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end_with_colon && matches.length == 1 ? "#{matches.first}:" : matches
|
271
|
+
end
|
272
|
+
|
273
|
+
def handle_non_contact_message(msg)
|
274
|
+
# TODO: improve this, maybe show the contact differentiated in /contacts
|
275
|
+
|
276
|
+
jid = msg.from.strip
|
277
|
+
|
278
|
+
ri = Jabber::Roster::Helper::RosterItem.new(Main.client)
|
279
|
+
ri.jid = jid
|
280
|
+
|
281
|
+
self[jid] = Contact.new(jid.to_s, ri)
|
282
|
+
self[jid].handle_message(msg)
|
283
|
+
#add(jid, Contact.new(jid.to_s, ri)).handle_message(msg)
|
284
|
+
end
|
285
|
+
|
286
|
+
def handle_presence(roster_item, old_p, new_p)
|
287
|
+
contact = self[new_p.from]
|
288
|
+
if Config.opts[:show_status_changes]
|
289
|
+
type = new_p.type
|
290
|
+
if type == nil || type == :unavailable && (!contact || !contact.online?)
|
291
|
+
Out::presence(new_p.from.to_s, new_p)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
if contact
|
296
|
+
contact.roster_item.presences.delete_if { |p| p.type == :unavailable } rescue nil
|
297
|
+
contact.presence_changed(old_p, new_p)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def handle_subscription(roster_item, presence)
|
302
|
+
show = true
|
303
|
+
if presence.type == :subscribe
|
304
|
+
show = !@subscription_requests.key?(presence.from.strip)
|
305
|
+
@subscription_requests[presence.from.strip] = presence
|
306
|
+
elsif presence.type == :subscribed
|
307
|
+
jid = presence.from.strip
|
308
|
+
|
309
|
+
@roster.add(jid)
|
310
|
+
add(jid, Contact.new(jid.node, @roster[jid]))
|
311
|
+
#p = Jabber::Presence.new.set_type(:probe)
|
312
|
+
end
|
313
|
+
|
314
|
+
Out::subscription(presence.from.to_s, presence.type, presence.status) if show
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|