1 #import "JubCLIChatUI.h"
2 #import "JubChatClient.h"
3 #import "JubCLIColor.h"
4 #import "JubCLICommand.h"
7 BEGINCLICOMMAND(JubCLIReplyCommand, @":r", nil,
8 @"Sets the sender of the last incomming message as the default recipient")
10 if (_ui.lastIn == nil) {
11 [of_stdout writeLine: @"No message has been received yet"];
15 _ui.sink = (JubCLIChatUI*)[_ui.client chatForContact: _ui.lastIn];
16 [of_stdout writeFormat: @"Set sink to %@\n",
17 [_ui.lastIn.rosterItem.JID bareJID]];
21 BEGINCLICOMMAND(JubCLISetSinkCommand, @":s", @"<who>",
22 @"Selects <who> as the default recipient")
24 if ([parameters count] != 1) {
25 [of_stdout writeLine: @"Syntax: ':s <who>'"];
29 OFString *param = parameters[0];
30 XMPPContact *contact = _ui.client.contactManager.contacts[param];
33 [of_stdout writeFormat: @"Contact '%@' not found in your "
38 _ui.sink = (JubCLIChatUI*)[_ui.client chatForContact: contact];
40 [of_stdout writeFormat: @"Set sink to %@\n", param];
44 BEGINCLICOMMAND(JubCLIMessageCommand, @":m", @"<who> <message>",
45 @"Sends a single message to <who>")
47 if ([parameters count] < 2) {
48 [of_stdout writeLine: @"Syntax: ':m <who> <message>'"];
52 XMPPContact *contact =
53 _ui.client.contactManager.contacts[parameters[0]];
56 [of_stdout writeFormat: @"Contact %@ not found in your "
57 @"roster\n", parameters[0]];
62 (JubCLIChatUI*)[_ui.client chatForContact: contact];
65 [parameters arrayByRemovingObject: [parameters firstObject]];
67 [chat send: [message componentsJoinedByString: @" "]];
71 BEGINCLICOMMAND(JubCLIPresenceCommand, @":t", @"<status> [<message>]",
72 @"Changes your presence")
74 if ([parameters count] < 1) {
76 @"Syntax: ':t <status> [<message>]'"];
80 XMPPPresence *presence;
81 OFString *show = parameters[0];
83 if (![@[ @"available", @"away", @"dnd", @"xa", @"chat", @"unavailable" ]
84 containsObject: show]) {
85 [of_stdout writeLine: @"<status> must be one of:"
86 @"available, away, dnd, xa, chat, unavailable"];
90 if ([show isEqual: @"unavailable"])
91 presence = [XMPPPresence presenceWithType: show];
93 presence = [XMPPPresence presence];
95 if (![@[ @"available", @"unavailable" ] containsObject: show])
98 if ([parameters count] == 2) {
99 [_ui.client.connection sendStanza: presence];
104 [parameters arrayByRemovingObject: [parameters firstObject]];
105 presence.status = [message componentsJoinedByString: @" "];
107 [_ui.client.connection sendStanza: presence];
111 BEGINCLICOMMAND(JubCLIRosterCommand, @":roster", nil, @"Shows your roster")
113 OFDictionary *contacts = _ui.client.contactManager.contacts;
114 for (OFString *key in contacts) {
115 XMPPContact *contact = contacts[key];
116 OFString *name = contact.rosterItem.name;
117 XMPPPresence *presence =
118 [[[contact.presences allObjects] sortedArray] firstObject];
121 [of_stdout writeFormat: @"%@ <%@> (", name, key];
123 [of_stdout writeFormat: @"%@ (", key];
126 [of_stdout writeFormat: COL_OFFLINE(@"offline")];
127 else if ([presence.show isEqual: @"chat"])
128 [of_stdout writeFormat: COL_CHAT(@"free for chat")];
129 else if ([presence.show isEqual: @"away"])
130 [of_stdout writeFormat: COL_AWAY(@"away")];
131 else if ([presence.show isEqual: @"xa"])
132 [of_stdout writeFormat: COL_XA(@"extended away")];
133 else if ([presence.show isEqual: @"dnd"])
134 [of_stdout writeFormat: COL_DND(@"do not disturb")];
136 [of_stdout writeFormat: COL_ONLINE(@"online")];
138 [of_stdout writeString: @")\n"];
143 @implementation JubCLIUI
144 @synthesize client = _client;
145 @synthesize lastIn = _lastIn;
146 @synthesize sink = _sink;
148 - initWithClient: (JubChatClient*)client
153 _commands = [OFMutableDictionary new];
154 _client = [client retain];
155 _contactManager = client.contactManager;
156 [_contactManager addDelegate: self];
158 [self addCommand: [[[JubCLIReplyCommand alloc]
159 initWithCLIUI: self] autorelease]];
161 [self addCommand: [[[JubCLISetSinkCommand alloc]
162 initWithCLIUI: self] autorelease]];
164 [self addCommand: [[[JubCLIMessageCommand alloc]
165 initWithCLIUI: self] autorelease]];
167 [self addCommand: [[[JubCLIPresenceCommand alloc]
168 initWithCLIUI: self] autorelease]];
170 [self addCommand: [[[JubCLIRosterCommand alloc]
171 initWithCLIUI: self] autorelease]];
182 [_contactManager removeDelegate: self];
188 - (void)startUIThread
190 [of_stdin asyncReadLineWithTarget: self
191 selector: @selector(Jub_userInputWithStream:
195 - (void)client: (JubChatClient*)client
196 didChangePresence: (XMPPPresence*)presence
202 return [JubCLIChatUI class];
205 - (void)addCommand: (id<JubCLICommand>)command
207 [_commands setObject: command
208 forKey: command.command];
211 - (BOOL)Jub_userInputWithStream: (OFStream*)stream
212 line: (OFString*)line
213 exception: (OFException*)exception
216 [OFApplication terminate];
218 if ([line length] == 0)
221 if ([line characterAtIndex: 0] != ':') {
223 [of_stdout writeLine: @"No default sink selected, "
224 @"type `:h` for help"];
231 line = [line stringByDeletingTrailingWhitespaces];
233 OFArray *input= [line componentsSeparatedByString: @" "];
235 if ([input[0] isEqual: @":h"]) {
236 __block size_t longest = 0;
238 [_commands enumerateKeysAndObjectsUsingBlock:
239 ^(OFString *key, id<JubCLICommand> command, BOOL *stop) {
240 size_t length = [command.command length] +
241 (command.params == nil ? 0 :
242 (1 + [command.params length]));
244 if (length > longest)
248 for (OFString *key in [[_commands allKeys] sortedArray]) {
249 id<JubCLICommand> command = _commands[key];
250 size_t length = [command.command length] +
251 (command.params == nil ? 0 :
252 (1 + [command.params length]));
254 if (command.params == nil)
255 [of_stdout writeFormat: @"`%@`",
258 [of_stdout writeFormat: @"`%@ %@`",
259 command.command, command.params];
261 // This is NOT distributive due to integer arithmetic
262 size_t padding = (longest + 2)/8 - (length + 2)/8;
264 for (size_t i = 0; i <= padding; i++)
265 [of_stdout writeString: @"\t"];
267 [of_stdout writeFormat: @"- %@\n", command.help];
273 id<JubCLICommand> command = _commands[input[0]];
276 [command callWithParameters:
277 [input arrayByRemovingObject: [input firstObject]]];
282 [of_stdout writeLine: @"Invalid command, type `:h` for help"];
287 - (void)contact: (XMPPContact*)contact
288 didSendMessage: (XMPPMessage*)message
290 if (message.body == nil || ![message.type isEqual: @"chat"])
296 - (void)contact: (XMPPContact*)contact
297 didSendPresence: (XMPPPresence*)presence
299 [of_stdout writeFormat: BOLD("%@") @" is now in state ", presence.from];
301 if ([presence.type isEqual: @"unavailable"])
302 [of_stdout writeFormat: COL_OFFLINE(@"offline")];
303 else if ([presence.show isEqual: @"chat"])
304 [of_stdout writeFormat: COL_CHAT(@"free for chat")];
305 else if ([presence.show isEqual: @"away"])
306 [of_stdout writeFormat: COL_AWAY(@"away")];
307 else if ([presence.show isEqual: @"xa"])
308 [of_stdout writeFormat: COL_XA(@"extended away")];
309 else if ([presence.show isEqual: @"dnd"])
310 [of_stdout writeFormat: COL_DND(@"do not disturb")];
312 [of_stdout writeFormat: COL_ONLINE(@"online")];
314 if (presence.status != nil)
315 [of_stdout writeFormat: @": %@", presence.status];
317 [of_stdout writeString: @"\n"];