]> cgit.babelmonkeys.de Git - jubjub.git/blob - src/gui/cli/JubCLIUI.m
e44a3f31fc621387e04d9d705afd7c0844c58c25
[jubjub.git] / src / gui / cli / JubCLIUI.m
1 #import "JubCLIChatUI.h"
2 #import "JubChatClient.h"
3 #import "JubCLIColor.h"
4 #import "JubCLICommand.h"
5 #import "JubCLIUI.h"
6
7 BEGINCLICOMMAND(JubCLIReplyCommand, @":r", nil,
8     @"Sets the sender of the last incomming message as the default recipient")
9 {
10         if (_ui.lastIn == nil) {
11                 [of_stdout writeLine: @"No message has been received yet"];
12                 return;
13         }
14
15         _ui.sink = (JubCLIChatUI*)[_ui.client chatForContact: _ui.lastIn];
16         [of_stdout writeFormat: @"Set sink to %@\n",
17             [_ui.lastIn.rosterItem.JID bareJID]];
18 }
19 ENDCLICOMMAND
20
21 BEGINCLICOMMAND(JubCLISetSinkCommand, @":s", @"<who>",
22     @"Selects <who> as the default recipient")
23 {
24         if ([parameters count] != 1) {
25                 [of_stdout writeLine: @"Syntax: ':s <who>'"];
26                 return;
27         }
28
29         OFString *param = parameters[0];
30         XMPPContact *contact = _ui.client.contactManager.contacts[param];
31
32         if (contact == nil) {
33                 [of_stdout writeFormat: @"Contact '%@' not found in your "
34                     @"roster\n", param];
35                 return;
36         }
37
38         _ui.sink = (JubCLIChatUI*)[_ui.client chatForContact: contact];
39
40         [of_stdout writeFormat: @"Set sink to %@\n", param];
41 }
42 ENDCLICOMMAND
43
44 BEGINCLICOMMAND(JubCLIMessageCommand, @":m", @"<who> <message>",
45     @"Sends a single message to <who>")
46 {
47         if ([parameters count] < 2) {
48                 [of_stdout writeLine: @"Syntax: ':m <who> <message>'"];
49                 return;
50         }
51
52         XMPPContact *contact =
53             _ui.client.contactManager.contacts[parameters[0]];
54
55         if (contact == nil) {
56                 [of_stdout writeFormat: @"Contact %@ not found in your "
57                     @"roster\n", parameters[0]];
58                 return;
59         }
60
61         JubCLIChatUI *chat =
62             (JubCLIChatUI*)[_ui.client chatForContact: contact];
63
64         OFArray *message =
65             [parameters arrayByRemovingObject: [parameters firstObject]];
66
67         [chat send: [message componentsJoinedByString: @" "]];
68 }
69 ENDCLICOMMAND
70
71 BEGINCLICOMMAND(JubCLIPresenceCommand, @":t", @"<status> [<message>]",
72     @"Changes your presence")
73 {
74         if ([parameters count] < 1) {
75                 [of_stdout writeLine:
76                     @"Syntax: ':t <status> [<message>]'"];
77                 return;
78         }
79
80         XMPPPresence *presence;
81         OFString *show = parameters[0];
82
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"];
87                 return;
88         }
89
90         if ([show isEqual: @"unavailable"])
91                 presence = [XMPPPresence presenceWithType: show];
92         else
93                 presence = [XMPPPresence presence];
94
95         if (![@[ @"available", @"unavailable" ] containsObject: show])
96                 presence.show = show;
97
98         if ([parameters count] == 2) {
99                 [_ui.client.connection sendStanza: presence];
100                 return;
101         }
102
103         OFArray *message =
104             [parameters arrayByRemovingObject: [parameters firstObject]];
105         presence.status = [message componentsJoinedByString: @" "];
106
107         [_ui.client.connection sendStanza: presence];
108 }
109 ENDCLICOMMAND
110
111 BEGINCLICOMMAND(JubCLIRosterCommand, @":roster", nil, @"Shows your roster")
112 {
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];
119
120                 if (name != nil)
121                         [of_stdout writeFormat: @"%@ <%@> (", name, key];
122                 else
123                         [of_stdout writeFormat: @"%@ (", key];
124
125                 if (presence == nil)
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")];
135                 else
136                         [of_stdout writeFormat: COL_ONLINE(@"online")];
137
138                 [of_stdout writeString: @")\n"];
139         }
140 }
141 ENDCLICOMMAND
142
143 @implementation JubCLIUI
144 @synthesize client = _client;
145 @synthesize lastIn = _lastIn;
146 @synthesize sink = _sink;
147
148 - initWithClient: (JubChatClient*)client
149 {
150         self = [super init];
151
152         @try {
153                 _commands =  [OFMutableDictionary new];
154                 _client = [client retain];
155                 _contactManager = client.contactManager;
156                 [_contactManager addDelegate: self];
157
158                 [self addCommand: [[[JubCLIReplyCommand alloc]
159                                        initWithCLIUI: self] autorelease]];
160
161                 [self addCommand: [[[JubCLISetSinkCommand alloc]
162                                        initWithCLIUI: self] autorelease]];
163
164                 [self addCommand: [[[JubCLIMessageCommand alloc]
165                                        initWithCLIUI: self] autorelease]];
166
167                 [self addCommand: [[[JubCLIPresenceCommand alloc]
168                                        initWithCLIUI: self] autorelease]];
169
170                 [self addCommand: [[[JubCLIRosterCommand alloc]
171                                        initWithCLIUI: self] autorelease]];
172         } @catch (id e) {
173                 [self release];
174                 @throw e;
175         }
176
177         return self;
178 }
179
180 - (void)dealloc
181 {
182         [_contactManager removeDelegate: self];
183         [_client release];
184         [_commands release];
185         [super dealloc];
186 }
187
188 - (void)startUIThread
189 {
190         [of_stdin asyncReadLineWithTarget: self
191                                  selector: @selector(Jub_userInputWithStream:
192                                                      line:exception:)];
193 }
194
195 -      (void)client: (JubChatClient*)client
196   didChangePresence: (XMPPPresence*)presence
197 {
198 }
199
200 - (Class)chatUIClass
201 {
202         return [JubCLIChatUI class];
203 }
204
205 - (void)addCommand: (id<JubCLICommand>)command
206 {
207         [_commands setObject: command
208                       forKey: command.command];
209 }
210
211 - (BOOL)Jub_userInputWithStream: (OFStream*)stream
212                            line: (OFString*)line
213                       exception: (OFException*)exception
214 {
215         if (line == nil)
216                 [OFApplication terminate];
217
218         if ([line length] == 0)
219                 return YES;
220
221         if ([line characterAtIndex: 0] != ':') {
222                 if (_sink == nil)
223                         [of_stdout writeLine: @"No default sink selected, "
224                             @"type `:h` for help"];
225
226                 [_sink send: line];
227
228                 return YES;
229         }
230
231         line = [line stringByDeletingTrailingWhitespaces];
232
233         OFArray *input= [line componentsSeparatedByString: @" "];
234
235         if ([input[0] isEqual: @":h"]) {
236                 __block size_t longest = 0;
237
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]));
243
244                         if (length > longest)
245                                 longest = length;
246                 }];
247
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]));
253
254                         if (command.params == nil)
255                                 [of_stdout writeFormat: @"`%@`",
256                                     command.command];
257                         else
258                                 [of_stdout writeFormat: @"`%@ %@`",
259                                     command.command, command.params];
260
261                         // This is NOT distributive due to integer arithmetic
262                         size_t padding = (longest + 2)/8 - (length + 2)/8;
263
264                         for (size_t i = 0; i <= padding; i++)
265                                 [of_stdout writeString: @"\t"];
266
267                         [of_stdout writeFormat: @"- %@\n", command.help];
268                 };
269
270                 return YES;
271         }
272
273         id<JubCLICommand> command = _commands[input[0]];
274
275         if (command) {
276                 [command callWithParameters:
277                     [input arrayByRemovingObject: [input firstObject]]];
278
279                 return YES;
280         }
281
282         [of_stdout writeLine: @"Invalid command, type `:h` for help"];
283
284         return YES;
285 }
286
287 -  (void)contact: (XMPPContact*)contact
288   didSendMessage: (XMPPMessage*)message
289 {
290         if (message.body == nil || ![message.type isEqual: @"chat"])
291                 return;
292
293         _lastIn =  contact;
294 }
295
296 -   (void)contact: (XMPPContact*)contact
297   didSendPresence: (XMPPPresence*)presence
298 {
299         [of_stdout writeFormat: BOLD("%@") @" is now in state ", presence.from];
300
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")];
311         else
312                 [of_stdout writeFormat: COL_ONLINE(@"online")];
313
314         if (presence.status != nil)
315                 [of_stdout writeFormat: @": %@", presence.status];
316
317         [of_stdout writeString: @"\n"];
318 }
319 @end