comparison irccdctl/cli.cpp @ 527:a88796ed040a

Irccdctl: switch to Boost.Asio, closes #697
author David Demelier <markand@malikania.fr>
date Thu, 16 Nov 2017 22:45:12 +0100
parents e03521cf207b
children 9daccaeedcce
comparison
equal deleted inserted replaced
526:5df38b4fbc55 527:a88796ed040a
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */ 17 */
18 18
19 #include <cassert> 19 #include <boost/system/system_error.hpp>
20 #include <iostream> 20
21 #include <sstream> 21 #include <irccd/errors.hpp>
22 22 #include <irccd/json_util.hpp>
23 #include <boost/timer/timer.hpp> 23 #include <irccd/options.hpp>
24 24 #include <irccd/string_util.hpp>
25 #include <json.hpp> 25
26 #include <irccd/ctl/controller.hpp>
26 27
27 #include "cli.hpp" 28 #include "cli.hpp"
28 #include "irccdctl.hpp"
29 #include "json_util.hpp"
30 #include "logger.hpp"
31 #include "options.hpp"
32 #include "net_util.hpp"
33 #include "string_util.hpp"
34
35 using namespace std::string_literals;
36 29
37 namespace irccd { 30 namespace irccd {
38 31
39 /* 32 namespace ctl {
40 * Cli. 33
41 * ------------------------------------------------------------------ 34 void cli::recv_response(ctl::controller& ctl, nlohmann::json req, handler_t handler)
42 */ 35 {
43 36 ctl.recv([&ctl, req, handler, this] (auto code, auto message) {
44 void Cli::check(const nlohmann::json &response) 37 if (code)
45 { 38 throw boost::system::system_error(code);
46 if (!json_util::get_bool(response, "status", false)) { 39
47 auto error = json_util::get_string(response, "error"); 40 auto c = json_util::to_string(message["command"]);
48 41
49 if (error.empty()) 42 if (c != req["command"].get<std::string>()) {
50 throw std::runtime_error("command failed with an unknown error"); 43 recv_response(ctl, std::move(req), std::move(handler));
51 44 return;
52 throw std::runtime_error(error); 45 }
53 } 46
54 } 47 if (message["error"].is_number_integer())
55 48 throw boost::system::system_error(static_cast<network_error>(message["error"].template get<int>()));
56 nlohmann::json Cli::request(Irccdctl &irccdctl, nlohmann::json args) 49 if (message["error"].is_string())
57 { 50 throw std::runtime_error(message["error"].template get<std::string>());
58 auto msg = nlohmann::json(); 51
59 52 if (handler)
60 if (!args.is_object()) 53 handler(std::move(message));
61 args = nlohmann::json::object(); 54 });
62 55 }
63 args.push_back({"command", m_name}); 56
64 irccdctl.client().request(args); 57 void cli::request(ctl::controller& ctl, nlohmann::json req, handler_t handler)
65 58 {
66 auto id = irccdctl.client().onMessage.connect([&] (auto input) { 59 ctl.send(req, [&ctl, req, handler, this] (auto code, auto) {
67 msg = std::move(input); 60 if (code)
68 }); 61 throw boost::system::system_error(code);
69 62
70 try { 63 recv_response(ctl, std::move(req), std::move(handler));
71 boost::timer::cpu_timer timer; 64 });
72 65 }
73 while (irccdctl.client().isConnected() && !msg.is_object() && timer.elapsed().wall / 1000000LL < 3000) 66
74 net_util::poll(3000 - timer.elapsed().wall / 1000000LL, irccdctl); 67 /*
75 } catch (const std::exception &) { 68 * plugin_info_cli.
76 irccdctl.client().onMessage.disconnect(id); 69 * ------------------------------------------------------------------
77 throw; 70 */
78 } 71
79 72 void plugin_config_cli::set(ctl::controller& ctl, const std::vector<std::string>&args)
80 irccdctl.client().onMessage.disconnect(id); 73 {
81 74 request(ctl, {
82 if (!msg.is_object())
83 throw std::runtime_error("no response received");
84 if (json_util::get_string(msg, "command") != m_name)
85 throw std::runtime_error("unexpected command result received");
86
87 check(msg);
88
89 return msg;
90 }
91
92 void Cli::call(Irccdctl &irccdctl, nlohmann::json args)
93 {
94 check(request(irccdctl, args));
95 }
96
97 namespace cli {
98
99 /*
100 * PluginConfigCli.
101 * ------------------------------------------------------------------
102 */
103
104 void PluginConfigCli::set(Irccdctl &irccdctl, const std::vector<std::string> &args)
105 {
106 check(request(irccdctl, nlohmann::json::object({
107 { "plugin", args[0] }, 75 { "plugin", args[0] },
108 { "variable", args[1] }, 76 { "variable", args[1] },
109 { "value", args[2] } 77 { "value", args[2] }
110 }))); 78 });
111 } 79 }
112 80
113 void PluginConfigCli::get(Irccdctl &irccdctl, const std::vector<std::string> &args) 81 void plugin_config_cli::get(ctl::controller& ctl, const std::vector<std::string>& args)
114 { 82 {
115 auto result = request(irccdctl, nlohmann::json::object({ 83 auto json = nlohmann::json::object({
116 { "plugin", args[0] }, 84 { "plugin", args[0] },
117 { "variable", args[1] } 85 { "variable", args[1] }
118 })); 86 });
119 87
120 check(result); 88 request(ctl, std::move(json), [args] (auto result) {
121 89 if (result["variables"].is_object())
122 if (result["variables"].is_object()) 90 std::cout << json_util::pretty(result["variables"][args[1]]) << std::endl;
123 std::cout << json_util::pretty(result["variables"][args[1]]) << std::endl; 91 });
124 } 92 }
125 93
126 void PluginConfigCli::getall(Irccdctl &irccdctl, const std::vector<std::string> &args) 94 void plugin_config_cli::getall(ctl::controller& ctl, const std::vector<std::string> &args)
127 { 95 {
128 auto result = request(irccdctl, nlohmann::json::object({{ "plugin", args[0] }})); 96 request(ctl, {{ "plugin", args[0] }}, [] (auto result) {
129 97 auto variables = result["variables"];
130 check(result); 98
131 99 for (auto v = variables.begin(); v != variables.end(); ++v)
132 auto variables = result["variables"]; 100 std::cout << std::setw(16) << std::left << v.key() << " : " << json_util::pretty(v.value()) << std::endl;
133 101 });
134 for (auto v = variables.begin(); v != variables.end(); ++v) 102 }
135 std::cout << std::setw(16) << std::left << v.key() << " : " << json_util::pretty(v.value()) << std::endl; 103
136 } 104 std::string plugin_config_cli::name() const
137 105 {
138 PluginConfigCli::PluginConfigCli() 106 return "plugin-config";
139 : Cli("plugin-config", 107 }
140 "configure a plugin", 108
141 "plugin-config plugin [variable] [value]", 109 void plugin_config_cli::exec(ctl::controller& ctl, const std::vector<std::string> &args)
142 "Get or set a plugin configuration variable.\n\n"
143 "If both variable and value are provided, sets the plugin configuration "
144 "to the\nrespective variable name and value.\n\n"
145 "If only variable is specified, shows its current value. Otherwise, list "
146 "all\nvariables and their values.\n\n"
147 "Examples:\n"
148 "\tirccdctl plugin-config ask")
149 {
150 }
151
152 void PluginConfigCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
153 { 110 {
154 switch (args.size()) { 111 switch (args.size()) {
155 case 3: 112 case 3:
156 set(irccdctl, args); 113 set(ctl, args);
157 break; 114 break;
158 case 2: 115 case 2:
159 get(irccdctl, args); 116 get(ctl, args);
160 break; 117 break;
161 case 1: 118 case 1:
162 getall(irccdctl, args); 119 getall(ctl, args);
163 break; 120 break;
164 default: 121 default:
165 throw std::invalid_argument("plugin-config requires at least 1 argument"); 122 throw std::invalid_argument("plugin-config requires at least 1 argument");
166 } 123 }
167 } 124 }
168 125
169 /* 126 /*
170 * PluginInfoCli. 127 * plugin_info_cli.
171 * ------------------------------------------------------------------ 128 * ------------------------------------------------------------------
172 */ 129 */
173 130
174 PluginInfoCli::PluginInfoCli() 131 std::string plugin_info_cli::name() const
175 : Cli("plugin-info", 132 {
176 "get plugin information", 133 return "plugin-info";
177 "plugin-info plugin", 134 }
178 "Get plugin information.\n\n" 135
179 "Example:\n" 136 void plugin_info_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
180 "\tirccdctl plugin-info ask"
181 )
182 {
183 }
184
185 void PluginInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
186 { 137 {
187 if (args.size() < 1) 138 if (args.size() < 1)
188 throw std::invalid_argument("plugin-info requires 1 argument"); 139 throw std::invalid_argument("plugin-info requires 1 argument");
189 140
190 auto result = request(irccdctl, {{ "plugin", args[0] }}); 141 request(ctl, {{ "plugin", args[0] }}, [] (auto result) {
191 142 std::cout << std::boolalpha;
192 std::cout << std::boolalpha; 143 std::cout << "Author : " << json_util::get_string(result, "author") << std::endl;
193 std::cout << "Author : " << json_util::get_string(result, "author") << std::endl; 144 std::cout << "License : " << json_util::get_string(result, "license") << std::endl;
194 std::cout << "License : " << json_util::get_string(result, "license") << std::endl; 145 std::cout << "Summary : " << json_util::get_string(result, "summary") << std::endl;
195 std::cout << "Summary : " << json_util::get_string(result, "summary") << std::endl; 146 std::cout << "Version : " << json_util::get_string(result, "version") << std::endl;
196 std::cout << "Version : " << json_util::get_string(result, "version") << std::endl; 147 });
197 } 148 }
198 149
199 /* 150 /*
200 * PluginListCli. 151 * plugin_list_cli.
201 * ------------------------------------------------------------------ 152 * ------------------------------------------------------------------
202 */ 153 */
203 154
204 PluginListCli::PluginListCli() 155 std::string plugin_list_cli::name() const
205 : Cli("plugin-list", 156 {
206 "list loaded plugins", 157 return "plugin-list";
207 "plugin-list", 158 }
208 "Get the list of all loaded plugins.\n\n" 159
209 "Example:\n" 160 void plugin_list_cli::exec(ctl::controller& ctl, const std::vector<std::string>&)
210 "\tirccdctl plugin-list") 161 {
211 { 162 request(ctl, {{ "command", "plugin-list" }}, [] (auto result) {
212 } 163 for (const auto &value : result["list"])
213 164 if (value.is_string())
214 void PluginListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) 165 std::cout << value.template get<std::string>() << std::endl;
215 { 166 });
216 auto result = request(irccdctl); 167 }
217 168
218 for (const auto &value : result["list"]) 169 /*
219 if (value.is_string()) 170 * plugin_load_cli.
220 std::cout << value.get<std::string>() << std::endl; 171 * ------------------------------------------------------------------
221 } 172 */
222 173
223 /* 174 std::string plugin_load_cli::name() const
224 * PluginLoadCli. 175 {
225 * ------------------------------------------------------------------ 176 return "plugin-load";
226 */ 177 }
227 178
228 PluginLoadCli::PluginLoadCli() 179 void plugin_load_cli::exec(ctl::controller& ctl, const std::vector<std::string> &args)
229 : Cli("plugin-load",
230 "load a plugin",
231 "plugin-load logger",
232 "Load a plugin into the irccd instance.\n\n"
233 "Example:\n"
234 "\tirccdctl plugin-load logger")
235 {
236 }
237
238 void PluginLoadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
239 { 180 {
240 if (args.size() < 1) 181 if (args.size() < 1)
241 throw std::invalid_argument("plugin-load requires 1 argument"); 182 throw std::invalid_argument("plugin-load requires 1 argument");
242 183
243 check(request(irccdctl, {{"plugin", args[0]}})); 184 request(ctl, {{ "plugin", args[0] }});
244 } 185 }
245 186
246 /* 187 /*
247 * PluginReloadCli. 188 * plugin_reload_cli.
248 * ------------------------------------------------------------------ 189 * ------------------------------------------------------------------
249 */ 190 */
250 191
251 PluginReloadCli::PluginReloadCli() 192 std::string plugin_reload_cli::name() const
252 : Cli("plugin-reload", 193 {
253 "reload a plugin", 194 return "plugin-reload";
254 "plugin-reload plugin", 195 }
255 "Reload a plugin by calling the appropriate onReload event, the plugin is not\n" 196
256 "unloaded and must be already loaded.\n\n" 197 void plugin_reload_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
257 "Example:\n"
258 "\tirccdctl plugin-reload logger")
259 {
260 }
261
262 void PluginReloadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
263 { 198 {
264 if (args.size() < 1) 199 if (args.size() < 1)
265 throw std::invalid_argument("plugin-reload requires 1 argument"); 200 throw std::invalid_argument("plugin-reload requires 1 argument");
266 201
267 check(request(irccdctl, {{ "plugin", args[0] }})); 202 request(ctl, {{ "plugin", args[0] }});
268 } 203 }
269 204
270 /* 205 /*
271 * PluginUnloadCli. 206 * plugin_unload_cli.
272 * ------------------------------------------------------------------ 207 * ------------------------------------------------------------------
273 */ 208 */
274 209
275 PluginUnloadCli::PluginUnloadCli() 210 std::string plugin_unload_cli::name() const
276 : Cli("plugin-unload", 211 {
277 "unload a plugin", 212 return "plugin-unload";
278 "plugin-unload plugin", 213 }
279 "Unload a loaded plugin from the irccd instance.\n\n" 214
280 "Example:\n" 215 void plugin_unload_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
281 "\tirccdctl plugin-unload logger")
282 {
283 }
284
285 void PluginUnloadCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
286 { 216 {
287 if (args.size() < 1) 217 if (args.size() < 1)
288 throw std::invalid_argument("plugin-unload requires 1 argument"); 218 throw std::invalid_argument("plugin-unload requires 1 argument");
289 219
290 check(request(irccdctl, {{ "plugin", args[0] }})); 220 request(ctl, {{ "plugin", args[0] }});
291 } 221 }
292 222
293 /* 223 /*
294 * ServerChannelCli. 224 * server_channel_cli.
295 * ------------------------------------------------------------------ 225 * ------------------------------------------------------------------
296 */ 226 */
297 227
298 ServerChannelMode::ServerChannelMode() 228 std::string server_channel_mode_cli::name() const
299 : Cli("server-cmode", 229 {
300 "change channel mode", 230 return "server-cmode";
301 "server-cmode server channel mode", 231 }
302 "Change the mode of the specified channel.\n\n" 232
303 "Example:\n" 233 void server_channel_mode_cli::exec(ctl::controller& ctl, const std::vector<std::string> &args)
304 "\tirccdctl server-cmode freenode #staff +t")
305 {
306 }
307
308 void ServerChannelMode::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
309 { 234 {
310 if (args.size() < 3) 235 if (args.size() < 3)
311 throw std::invalid_argument("server-cmode requires 3 arguments"); 236 throw std::invalid_argument("server-cmode requires 3 arguments");
312 237
313 check(request(irccdctl, { 238 request(ctl, {
314 { "command", "server-cmode" }, 239 { "command", "server-cmode" },
315 { "server", args[0] }, 240 { "server", args[0] },
316 { "channel", args[1] }, 241 { "channel", args[1] },
317 { "mode", args[2] } 242 { "mode", args[2] }
318 })); 243 });
319 } 244 }
320 245
321 /* 246 /*
322 * ServerChannelNoticeCli. 247 * server_channel_notice_cli.
323 * ------------------------------------------------------------------ 248 * ------------------------------------------------------------------
324 */ 249 */
325 250
326 ServerChannelNoticeCli::ServerChannelNoticeCli() 251 std::string server_channel_notice_cli::name() const
327 : Cli("server-cnotice", 252 {
328 "send a channel notice", 253 return "server-cnotice";
329 "server-cnotice server channel message", 254 }
330 "Send a notice to a public channel. This is a notice that everyone on the channel\n" 255
331 "will receive.\n\n" 256 void server_channel_notice_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
332 "Example:\n"
333 "\tirccdctl server-cnotice freenode #staff \"Don't flood!\"")
334 {
335 }
336
337 void ServerChannelNoticeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
338 { 257 {
339 if (args.size() < 3) 258 if (args.size() < 3)
340 throw std::invalid_argument("server-cnotice requires 3 arguments"); 259 throw std::invalid_argument("server-cnotice requires 3 arguments");
341 260
342 check(request(irccdctl, { 261 request(ctl, {
343 { "command", "server-cnotice" }, 262 { "command", "server-cnotice" },
344 { "server", args[0] }, 263 { "server", args[0] },
345 { "channel", args[1] }, 264 { "channel", args[1] },
346 { "message", args[2] } 265 { "message", args[2] }
347 })); 266 });
348 } 267 }
349 268
350 /* 269 /*
351 * ServerConnectCli. 270 * server_connect_cli.
352 * ------------------------------------------------------------------ 271 * ------------------------------------------------------------------
353 */ 272 */
354 273
355 namespace { 274 namespace {
356 275
374 return option::read(args, options); 293 return option::read(args, options);
375 } 294 }
376 295
377 } // !namespace 296 } // !namespace
378 297
379 ServerConnectCli::ServerConnectCli() 298 std::string server_connect_cli::name() const
380 : Cli("server-connect", 299 {
381 "add a server", 300 return "server-connect";
382 "server-connect [options] id host [port]", 301 }
383 "Connect to a new IRC server.\n\n" 302
384 "Available options:\n" 303 void server_connect_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
385 " -c, --command\t\tspecify the command char\n"
386 " -n, --nickname\tspecify a nickname\n"
387 " -r, --realname\tspecify a real name\n"
388 " -S, --ssl-verify\tverify SSL\n"
389 " -s, --ssl\t\tconnect using SSL\n"
390 " -u, --username\tspecify a user name\n\n"
391 "Example:\n"
392 "\tirccdctl server-connect -n jean example irc.example.org\n"
393 "\tirccdctl server-connect --ssl example irc.example.org 6697")
394 {
395 }
396
397 void ServerConnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
398 { 304 {
399 std::vector<std::string> copy(args); 305 std::vector<std::string> copy(args);
400 306
401 option::result result = parse(copy); 307 option::result result = parse(copy);
402 option::result::const_iterator it; 308 option::result::const_iterator it;
425 if ((it = result.find("-r")) != result.end() || (it = result.find("--realname")) != result.end()) 331 if ((it = result.find("-r")) != result.end() || (it = result.find("--realname")) != result.end())
426 object["realname"] = it->second; 332 object["realname"] = it->second;
427 if ((it = result.find("-u")) != result.end() || (it = result.find("--username")) != result.end()) 333 if ((it = result.find("-u")) != result.end() || (it = result.find("--username")) != result.end())
428 object["username"] = it->second; 334 object["username"] = it->second;
429 335
430 check(request(irccdctl, object)); 336 request(ctl, object);
431 } 337 }
432 338
433 /* 339 /*
434 * ServerDisconnectCli. 340 * server_disconnect_cli.
435 * ------------------------------------------------------------------ 341 * ------------------------------------------------------------------
436 */ 342 */
437 343
438 ServerDisconnectCli::ServerDisconnectCli() 344 std::string server_disconnect_cli::name() const
439 : Cli("server-disconnect", 345 {
440 "disconnect server", 346 return "server-disconnect";
441 "server-disconnect [server]", 347 }
442 "Disconnect from a server.\n\n" 348
443 "If server is not specified, irccd disconnects all servers.\n\n" 349 void server_disconnect_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
444 "Example:\n"
445 "\tirccdctl server-disconnect localhost")
446 {
447 }
448
449 void ServerDisconnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
450 { 350 {
451 auto object = nlohmann::json::object({ 351 auto object = nlohmann::json::object({
452 { "command", "server-disconnect" } 352 { "command", "server-disconnect" }
453 }); 353 });
454 354
455 if (args.size() > 0) 355 if (args.size() > 0)
456 object["server"] = args[0]; 356 object["server"] = args[0];
457 357
458 check(request(irccdctl, object)); 358 request(ctl, object);
459 } 359 }
460 360
461 /* 361 /*
462 * ServerInfoCli. 362 * server_info_cli.
463 * ------------------------------------------------------------------ 363 * ------------------------------------------------------------------
464 */ 364 */
465 365
466 ServerInfoCli::ServerInfoCli() 366 std::string server_info_cli::name() const
467 : Cli("server-info", 367 {
468 "get server information", 368 return "server-info";
469 "server-info server", 369 }
470 "Get information about a server.\n\n" 370
471 "Example:\n" 371 void server_info_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
472 "\tirccdctl server-info freenode")
473 {
474 }
475
476 void ServerInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
477 { 372 {
478 if (args.size() < 1) 373 if (args.size() < 1)
479 throw std::invalid_argument("server-info requires 1 argument"); 374 throw std::invalid_argument("server-info requires 1 argument");
480 375
481 auto result = request(irccdctl, { 376 auto json = nlohmann::json::object({
482 { "command", "server-info" }, 377 { "command", "server-info" },
483 { "server", args[0] } 378 { "server", args[0] }
484 }); 379 });
485 380
486 check(result); 381 request(ctl, std::move(json), [] (auto result) {
487 382 std::cout << std::boolalpha;
488 std::cout << std::boolalpha; 383 std::cout << "Name : " << json_util::pretty(result["name"]) << std::endl;
489 std::cout << "Name : " << json_util::pretty(result["name"]) << std::endl; 384 std::cout << "Host : " << json_util::pretty(result["host"]) << std::endl;
490 std::cout << "Host : " << json_util::pretty(result["host"]) << std::endl; 385 std::cout << "Port : " << json_util::pretty(result["port"]) << std::endl;
491 std::cout << "Port : " << json_util::pretty(result["port"]) << std::endl; 386 std::cout << "Ipv6 : " << json_util::pretty(result["ipv6"]) << std::endl;
492 std::cout << "Ipv6 : " << json_util::pretty(result["ipv6"]) << std::endl; 387 std::cout << "SSL : " << json_util::pretty(result["ssl"]) << std::endl;
493 std::cout << "SSL : " << json_util::pretty(result["ssl"]) << std::endl; 388 std::cout << "SSL verified : " << json_util::pretty(result["sslVerify"]) << std::endl;
494 std::cout << "SSL verified : " << json_util::pretty(result["sslVerify"]) << std::endl; 389 std::cout << "Channels : ";
495 std::cout << "Channels : "; 390
496 391 for (const auto &v : result["channels"])
497 for (const auto &v : result["channels"]) 392 if (v.is_string())
498 if (v.is_string()) 393 std::cout << v.template get<std::string>() << " ";
499 std::cout << v.get<std::string>() << " "; 394
500 395 std::cout << std::endl;
501 std::cout << std::endl; 396
502 397 std::cout << "Nickname : " << json_util::pretty(result["nickname"]) << std::endl;
503 std::cout << "Nickname : " << json_util::pretty(result["nickname"]) << std::endl; 398 std::cout << "User name : " << json_util::pretty(result["username"]) << std::endl;
504 std::cout << "User name : " << json_util::pretty(result["username"]) << std::endl; 399 std::cout << "Real name : " << json_util::pretty(result["realname"]) << std::endl;
505 std::cout << "Real name : " << json_util::pretty(result["realname"]) << std::endl; 400 });
506 } 401 }
507 402
508 /* 403 /*
509 * ServerInviteCli. 404 * server_invite_cli.
510 * ------------------------------------------------------------------ 405 * ------------------------------------------------------------------
511 */ 406 */
512 407
513 ServerInviteCli::ServerInviteCli() 408 std::string server_invite_cli::name() const
514 : Cli("server-invite", 409 {
515 "invite someone", 410 return "server-invite";
516 "server-invite server nickname channel", 411 }
517 "Invite the specified target on the channel.\n\n" 412
518 "Example:\n" 413 void server_invite_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
519 "\tirccdctl server-invite freenode xorg62 #staff")
520 {
521 }
522
523 void ServerInviteCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
524 { 414 {
525 if (args.size() < 3) 415 if (args.size() < 3)
526 throw std::invalid_argument("server-invite requires 3 arguments"); 416 throw std::invalid_argument("server-invite requires 3 arguments");
527 417
528 check(request(irccdctl, { 418 request(ctl, {
529 { "command", "server-invite" }, 419 { "command", "server-invite" },
530 { "server", args[0] }, 420 { "server", args[0] },
531 { "target", args[1] }, 421 { "target", args[1] },
532 { "channel", args[2] } 422 { "channel", args[2] }
533 })); 423 });
534 } 424 }
535 425
536 /* 426 /*
537 * ServerJoinCli. 427 * server_join_cli.
538 * ------------------------------------------------------------------ 428 * ------------------------------------------------------------------
539 */ 429 */
540 430
541 ServerJoinCli::ServerJoinCli() 431 std::string server_join_cli::name() const
542 : Cli("server-join", 432 {
543 "join a channel", 433 return "server-join";
544 "server-join server channel [password]", 434 }
545 "Join the specified channel, the password is optional.\n\n" 435
546 "Example:\n" 436 void server_join_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
547 "\tirccdctl server-join freenode #test\n"
548 "\tirccdctl server-join freenode #private-club secret")
549 {
550 }
551
552 void ServerJoinCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
553 { 437 {
554 if (args.size() < 2) 438 if (args.size() < 2)
555 throw std::invalid_argument("server-join requires at least 2 arguments"); 439 throw std::invalid_argument("server-join requires at least 2 arguments");
556 440
557 auto object = nlohmann::json::object({ 441 auto object = nlohmann::json::object({
560 }); 444 });
561 445
562 if (args.size() == 3) 446 if (args.size() == 3)
563 object["password"] = args[2]; 447 object["password"] = args[2];
564 448
565 check(request(irccdctl, object)); 449 request(ctl, object);
566 } 450 }
567 451
568 /* 452 /*
569 * ServerKickCli. 453 * server_kick_cli.
570 * ------------------------------------------------------------------ 454 * ------------------------------------------------------------------
571 */ 455 */
572 456
573 ServerKickCli::ServerKickCli() 457 std::string server_kick_cli::name() const
574 : Cli("server-kick", 458 {
575 "kick someone from a channel", 459 return "server-kick";
576 "server-kick server target channel [reason]", 460 }
577 "Kick the specified target from the channel, the reason is optional.\n\n" 461
578 "Example:\n" 462 void server_kick_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
579 "\tirccdctl server-kick freenode jean #staff \"Stop flooding\"")
580 {
581 }
582
583 void ServerKickCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
584 { 463 {
585 if (args.size() < 3) 464 if (args.size() < 3)
586 throw std::invalid_argument("server-kick requires at least 3 arguments "); 465 throw std::invalid_argument("server-kick requires at least 3 arguments ");
587 466
588 auto object = nlohmann::json::object({ 467 auto object = nlohmann::json::object({
592 }); 471 });
593 472
594 if (args.size() == 4) 473 if (args.size() == 4)
595 object["reason"] = args[3]; 474 object["reason"] = args[3];
596 475
597 check(request(irccdctl, object)); 476 request(ctl, object);
598 } 477 }
599 478
600 /* 479 /*
601 * ServerListCli. 480 * server_list_cli.
602 * ------------------------------------------------------------------ 481 * ------------------------------------------------------------------
603 */ 482 */
604 483
605 ServerListCli::ServerListCli() 484 std::string server_list_cli::name() const
606 : Cli("server-list", 485 {
607 "get list of servers", 486 return "server-list";
608 "server-list", 487 }
609 "Get the list of all connected servers.\n\n" 488
610 "Example:\n" 489 void server_list_cli::exec(ctl::controller& ctl, const std::vector<std::string>&)
611 "\tirccdctl server-list") 490 {
612 { 491 request(ctl, {{ "command", "server-list" }}, [] (auto result) {
613 } 492 for (const auto &n : result["list"])
614 493 if (n.is_string())
615 void ServerListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) 494 std::cout << n.template get<std::string>() << std::endl;
616 { 495 });
617 auto response = request(irccdctl); 496 }
618 497
619 check(response); 498 /*
620 499 * server_me_cli.
621 for (const auto &n : response["list"]) 500 * ------------------------------------------------------------------
622 if (n.is_string()) 501 */
623 std::cout << n.get<std::string>() << std::endl; 502
624 } 503 std::string server_me_cli::name() const
625 504 {
626 /* 505 return "server-me";
627 * ServerMeCli. 506 }
628 * ------------------------------------------------------------------ 507
629 */ 508 void server_me_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
630
631 ServerMeCli::ServerMeCli()
632 : Cli("server-me",
633 "send an action emote",
634 "server-me server target message",
635 "Send an action emote.\n\n"
636 "Example:\n"
637 "\tirccdctl server-me freenode #staff \"going back soon\"")
638 {
639 }
640
641 void ServerMeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
642 { 509 {
643 if (args.size() < 3) 510 if (args.size() < 3)
644 throw std::runtime_error("server-me requires 3 arguments"); 511 throw std::runtime_error("server-me requires 3 arguments");
645 512
646 check(request(irccdctl, { 513 request(ctl, {
647 { "server", args[0] }, 514 { "server", args[0] },
648 { "target", args[1] }, 515 { "target", args[1] },
649 { "message", args[2] } 516 { "message", args[2] }
650 })); 517 });
651 } 518 }
652 519
653 /* 520 /*
654 * ServerMessageCli. 521 * server_message_cli.
655 * ------------------------------------------------------------------ 522 * ------------------------------------------------------------------
656 */ 523 */
657 524
658 ServerMessageCli::ServerMessageCli() 525 std::string server_message_cli::name() const
659 : Cli("server-message", 526 {
660 "send a message", 527 return "server-message";
661 "server-message server target message", 528 }
662 "Send a message to the specified target or channel.\n\n" 529
663 "Example:\n" 530 void server_message_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
664 "\tirccdctl server-message freenode #staff \"Hello from irccd\"")
665 {
666 }
667
668 void ServerMessageCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
669 { 531 {
670 if (args.size() < 3) 532 if (args.size() < 3)
671 throw std::invalid_argument("server-message requires 3 arguments"); 533 throw std::invalid_argument("server-message requires 3 arguments");
672 534
673 check(request(irccdctl, { 535 request(ctl, {
674 { "server", args[0] }, 536 { "server", args[0] },
675 { "target", args[1] }, 537 { "target", args[1] },
676 { "message", args[2] } 538 { "message", args[2] }
677 })); 539 });
678 } 540 }
679 541
680 /* 542 /*
681 * ServerModeCli. 543 * server_mode_cli.
682 * ------------------------------------------------------------------ 544 * ------------------------------------------------------------------
683 */ 545 */
684 546
685 ServerModeCli::ServerModeCli() 547 std::string server_mode_cli::name() const
686 : Cli("server-mode", 548 {
687 "the the user mode", 549 return "server-mode";
688 "server-mode server mode", 550 }
689 "Set the irccd's user mode.\n\n" 551
690 "Example:\n" 552 void server_mode_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
691 "\tirccdctl server-mode +i")
692 {
693 }
694
695 void ServerModeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
696 { 553 {
697 if (args.size() < 2) 554 if (args.size() < 2)
698 throw std::invalid_argument("server-mode requires 2 arguments"); 555 throw std::invalid_argument("server-mode requires 2 arguments");
699 556
700 check(request(irccdctl, { 557 request(ctl, {
701 { "server", args[0] }, 558 { "server", args[0] },
702 { "mode", args[1] } 559 { "mode", args[1] }
703 })); 560 });
704 } 561 }
705 562
706 /* 563 /*
707 * ServerNickCli. 564 * server_nick_cli.
708 * ------------------------------------------------------------------ 565 * ------------------------------------------------------------------
709 */ 566 */
710 567
711 ServerNickCli::ServerNickCli() 568 std::string server_nick_cli::name() const
712 : Cli("server-nick", 569 {
713 "change your nickname", 570 return "server-nick";
714 "server-nick server nickname", 571 }
715 "Change irccd's nickname.\n\n" 572
716 "Example:\n" 573 void server_nick_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
717 "\tirccdctl server-nick freenode david")
718 {
719 }
720
721 void ServerNickCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
722 { 574 {
723 if (args.size() < 2) 575 if (args.size() < 2)
724 throw std::invalid_argument("server-nick requires 2 arguments"); 576 throw std::invalid_argument("server-nick requires 2 arguments");
725 577
726 check(request(irccdctl, { 578 request(ctl, {
727 { "server", args[0] }, 579 { "server", args[0] },
728 { "nickname", args[1] } 580 { "nickname", args[1] }
729 })); 581 });
730 } 582 }
731 583
732 /* 584 /*
733 * ServerNoticeCli. 585 * server_notice_cli.
734 * ------------------------------------------------------------------ 586 * ------------------------------------------------------------------
735 */ 587 */
736 588
737 ServerNoticeCli::ServerNoticeCli() 589 std::string server_notice_cli::name() const
738 : Cli("server-notice", 590 {
739 "send a private notice", 591 return "server-notice";
740 "server-notice server target message", 592 }
741 "Send a private notice to the specified target.\n\n" 593
742 "Example:\n" 594 void server_notice_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
743 "\tirccdctl server-notice freenode jean \"I know you are here.\"")
744 {
745 }
746
747 void ServerNoticeCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
748 { 595 {
749 if (args.size() < 3) 596 if (args.size() < 3)
750 throw std::invalid_argument("server-notice requires 3 arguments"); 597 throw std::invalid_argument("server-notice requires 3 arguments");
751 598
752 check(request(irccdctl, { 599 request(ctl, {
753 { "server", args[0] }, 600 { "server", args[0] },
754 { "target", args[1] }, 601 { "target", args[1] },
755 { "message", args[2] } 602 { "message", args[2] }
756 })); 603 });
757 } 604 }
758 605
759 /* 606 /*
760 * ServerPartCli. 607 * server_part_cli.
761 * ------------------------------------------------------------------ 608 * ------------------------------------------------------------------
762 */ 609 */
763 610
764 ServerPartCli::ServerPartCli() 611 std::string server_part_cli::name() const
765 : Cli("server-part", 612 {
766 "leave a channel", 613 return "server-part";
767 "server-part server channel [reason]", 614 }
768 "Leave the specified channel, the reason is optional.\n\n" 615
769 "Not all IRC servers support giving a reason to leave a channel, do not specify\n" 616 void server_part_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
770 "it if this is a concern.\n\n"
771 "Example:\n"
772 "\tirccdctl server-part freenode #staff\n"
773 "\tirccdctl server-part freenode #botwar \"too noisy\"")
774 {
775 }
776
777 void ServerPartCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
778 { 617 {
779 if (args.size() < 2) 618 if (args.size() < 2)
780 throw std::invalid_argument("server-part requires at least 2 arguments"); 619 throw std::invalid_argument("server-part requires at least 2 arguments");
781 620
782 auto object = nlohmann::json::object({ 621 auto object = nlohmann::json::object({
785 }); 624 });
786 625
787 if (args.size() >= 3) 626 if (args.size() >= 3)
788 object["reason"] = args[2]; 627 object["reason"] = args[2];
789 628
790 check(request(irccdctl, object)); 629 request(ctl, object);
791 } 630 }
792 631
793 /* 632 /*
794 * ServerReconnectCli. 633 * server_reconnect_cli.
795 * ------------------------------------------------------------------ 634 * ------------------------------------------------------------------
796 */ 635 */
797 636
798 ServerReconnectCli::ServerReconnectCli() 637 std::string server_reconnect_cli::name() const
799 : Cli("server-reconnect", 638 {
800 "force reconnection of a server", 639 return "server-reconnect";
801 "server-reconnect [server]", 640 }
802 "Force reconnection of one or all servers.\n\n" 641
803 "If server is not specified, all servers will try to reconnect.\n\n" 642 void server_reconnect_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
804 "Example:\n"
805 "\tirccdctl server-reconnect\n"
806 "\tirccdctl server-reconnect wanadoo")
807 {
808 }
809
810 void ServerReconnectCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
811 { 643 {
812 auto object = nlohmann::json::object({ 644 auto object = nlohmann::json::object({
813 { "command", "server-reconnect" } 645 { "command", "server-reconnect" }
814 }); 646 });
815 647
816 if (args.size() >= 1) 648 if (args.size() >= 1)
817 object["server"] = args[0]; 649 object["server"] = args[0];
818 650
819 check(request(irccdctl, object)); 651 request(ctl, object);
820 } 652 }
821 653
822 /* 654 /*
823 * ServerTopicCli. 655 * server_topic_cli.
824 * ------------------------------------------------------------------ 656 * ------------------------------------------------------------------
825 */ 657 */
826 658
827 ServerTopicCli::ServerTopicCli() 659 std::string server_topic_cli::name() const
828 : Cli("server-topic", 660 {
829 "change channel topic", 661 return "server-topic";
830 "server-topic server channel topic", 662 }
831 "Change the topic of the specified channel.\n\n" 663
832 "Example:\n" 664 void server_topic_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
833 "\tirccdctl server-topic freenode #wmfs \"This is the best channel\"")
834 {
835 }
836
837 void ServerTopicCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
838 { 665 {
839 if (args.size() < 3) 666 if (args.size() < 3)
840 throw std::invalid_argument("server-topic requires 3 arguments"); 667 throw std::invalid_argument("server-topic requires 3 arguments");
841 668
842 check(request(irccdctl, { 669 request(ctl, {
843 { "server", args[0] }, 670 { "server", args[0] },
844 { "channel", args[1] }, 671 { "channel", args[1] },
845 { "topic", args[2] } 672 { "topic", args[2] }
846 })); 673 });
847 } 674 }
848 675
849 /* 676 /*
850 * RuleAddCli. 677 * rule_add_cli.
851 * ------------------------------------------------------------------ 678 * ------------------------------------------------------------------
852 */ 679 */
853 680
854 RuleAddCli::RuleAddCli() 681 std::string rule_add_cli::name() const
855 : Cli("rule-add", 682 {
856 "add a new rule", 683 return "rule-add";
857 "rule-add [options] accept|drop", 684 }
858 "Add a new rule to irccd.\n\n" 685
859 "If no index is specified, the rule is added to the end.\n\n" 686 void rule_add_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
860 "Available options:\n"
861 " -c, --add-channel\t\tmatch a channel\n"
862 " -e, --add-event\t\tmatch an event\n"
863 " -i, --index\t\t\trule position\n"
864 " -p, --add-plugin\t\tmatch a plugin\n"
865 " -s, --add-server\t\tmatch a server\n\n"
866 "Example:\n"
867 "\tirccdctl rule-add -p hangman drop\n"
868 "\tirccdctl rule-add -s localhost -c #games -p hangman accept")
869 {
870 }
871
872 void RuleAddCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
873 { 687 {
874 static const option::options options{ 688 static const option::options options{
875 { "-c", true }, 689 { "-c", true },
876 { "--add-channel", true }, 690 { "--add-channel", true },
877 { "-e", true }, 691 { "-e", true },
916 if (result.count("--index") > 0) 730 if (result.count("--index") > 0)
917 json["index"] = string_util::to_number<unsigned>(result.find("--index")->second); 731 json["index"] = string_util::to_number<unsigned>(result.find("--index")->second);
918 732
919 // And action. 733 // And action.
920 if (copy[0] != "accept" && copy[0] != "drop") 734 if (copy[0] != "accept" && copy[0] != "drop")
921 throw std::runtime_error("invalid action '"s + copy[0] + "'"); 735 throw std::runtime_error(string_util::sprintf("invalid action '%s'", copy[0]));
922 736
923 json["action"] = copy[0]; 737 json["action"] = copy[0];
924 738
925 check(request(irccdctl, json)); 739 request(ctl, json);
926 } 740 }
927 741
928 /* 742 /*
929 * RuleEditCli. 743 * rule_edit_cli.
930 * ------------------------------------------------------------------ 744 * ------------------------------------------------------------------
931 */ 745 */
932 746
933 RuleEditCli::RuleEditCli() 747 std::string rule_edit_cli::name() const
934 : Cli("rule-edit", 748 {
935 "edit an existing rule", 749 return "rule-edit";
936 "rule-edit [options] index", 750 }
937 "Edit an existing rule in irccd.\n\n" 751
938 "All options can be specified multiple times.\n\n" 752 void rule_edit_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
939 "Available options:\n"
940 " -a, --action\t\t\tset action\n"
941 " -c, --add-channel\t\tmatch a channel\n"
942 " -C, --remove-channel\t\tremove a channel\n"
943 " -e, --add-event\t\tmatch an event\n"
944 " -E, --remove-event\t\tremove an event\n"
945 " -p, --add-plugin\t\tmatch a plugin\n"
946 " -P, --add-plugin\t\tremove a plugin\n"
947 " -s, --add-server\t\tmatch a server\n"
948 " -S, --remove-server\t\tremove a server\n\n"
949 "Example:\n"
950 "\tirccdctl rule-edit -p hangman 0\n"
951 "\tirccdctl rule-edit -S localhost -c #games -p hangman 1")
952 {
953 }
954
955 void RuleEditCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
956 { 753 {
957 static const option::options options{ 754 static const option::options options{
958 { "-a", true }, 755 { "-a", true },
959 { "--action", true }, 756 { "--action", true },
960 { "-c", true }, 757 { "-c", true },
1016 } 813 }
1017 814
1018 // Index. 815 // Index.
1019 json["index"] = string_util::to_number<unsigned>(copy[0]); 816 json["index"] = string_util::to_number<unsigned>(copy[0]);
1020 817
1021 check(request(irccdctl, json)); 818 request(ctl, json);
1022 } 819 }
1023 820
1024 /* 821 /*
1025 * RuleListCli. 822 * rule_list_cli.
1026 * ------------------------------------------------------------------ 823 * ------------------------------------------------------------------
1027 */ 824 */
1028 825
1029 namespace { 826 namespace {
1030 827
1031 void showRule(const nlohmann::json& json, int index) 828 void show_rule(const nlohmann::json& json, int index)
1032 { 829 {
1033 assert(json.is_object()); 830 assert(json.is_object());
1034 831
1035 auto unjoin = [] (auto array) { 832 auto unjoin = [] (auto array) {
1036 std::ostringstream oss; 833 std::ostringstream oss;
1060 std::cout << std::endl; 857 std::cout << std::endl;
1061 } 858 }
1062 859
1063 } // !namespace 860 } // !namespace
1064 861
1065 RuleListCli::RuleListCli() 862 std::string rule_list_cli::name() const
1066 : Cli("rule-list", 863 {
1067 "list all rules", 864 return "rule-list";
1068 "rule-list", 865 }
1069 "List all rules.\n\n" 866
1070 "Example:\n" 867 void rule_list_cli::exec(ctl::controller& ctl, const std::vector<std::string>&)
1071 "\tirccdctl rule-list") 868 {
1072 { 869 request(ctl, {{ "command", "rule-list" }}, [] (auto result) {
1073 } 870 auto pos = 0;
1074 871
1075 void RuleListCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &) 872 for (const auto& obj : result["list"]) {
1076 { 873 if (obj.is_object())
1077 auto response = request(irccdctl); 874 show_rule(obj, pos++);
1078 auto pos = 0; 875 }
1079 876 });
1080 check(response); 877 }
1081 878
1082 for (const auto &obj : response["list"]) { 879 /*
1083 if (!obj.is_object()) 880 * rule_info_cli.
1084 continue; 881 * ------------------------------------------------------------------
1085 882 */
1086 showRule(obj, pos++); 883
1087 } 884 std::string rule_info_cli::name() const
1088 } 885 {
1089 886 return "rule-info";
1090 /* 887 }
1091 * RuleInfoCli. 888
1092 * ------------------------------------------------------------------ 889 void rule_info_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
1093 */
1094
1095 RuleInfoCli::RuleInfoCli()
1096 : Cli("rule-info",
1097 "show a rule",
1098 "rule-info index",
1099 "Show a rule.\n\n"
1100 "Example:\n"
1101 "\tirccdctl rule-info 0\n"
1102 "\tirccdctl rule-info 1")
1103 {
1104 }
1105
1106 void RuleInfoCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
1107 { 890 {
1108 if (args.size() < 1) 891 if (args.size() < 1)
1109 throw std::invalid_argument("rule-info requires 1 argument"); 892 throw std::invalid_argument("rule-info requires 1 argument");
1110 893
1111 int index = 0; 894 int index = 0;
1114 index = std::stoi(args[0]); 897 index = std::stoi(args[0]);
1115 } catch (...) { 898 } catch (...) {
1116 throw std::invalid_argument("invalid number '" + args[0] + "'"); 899 throw std::invalid_argument("invalid number '" + args[0] + "'");
1117 } 900 }
1118 901
1119 auto result = request(irccdctl, { 902 auto json = nlohmann::json::object({
1120 { "command", "rule-info" }, 903 { "command", "rule-info" },
1121 { "index", index } 904 { "index", index }
1122 }); 905 });
1123 906
1124 check(result); 907 request(ctl, std::move(json), [] (auto result) {
1125 showRule(result, 0); 908 show_rule(result, 0);
1126 } 909 });
1127 910 }
1128 /* 911
1129 * RuleRemoveCli. 912 /*
1130 * ------------------------------------------------------------------ 913 * rule_remove_cli.
1131 */ 914 * ------------------------------------------------------------------
1132 915 */
1133 RuleRemoveCli::RuleRemoveCli() 916
1134 : Cli("rule-remove", 917 std::string rule_remove_cli::name() const
1135 "remove a rule", 918 {
1136 "rule-remove index", 919 return "rule-remove";
1137 "Remove an existing rule.\n\n" 920 }
1138 "Example:\n" 921
1139 "\tirccdctl rule-remove 0\n" 922 void rule_remove_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
1140 "\tirccdctl rule-remove 1")
1141 {
1142 }
1143
1144 void RuleRemoveCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
1145 { 923 {
1146 if (args.size() < 1) 924 if (args.size() < 1)
1147 throw std::invalid_argument("rule-remove requires 1 argument"); 925 throw std::invalid_argument("rule-remove requires 1 argument");
1148 926
1149 int index = 0; 927 int index = 0;
1152 index = std::stoi(args[0]); 930 index = std::stoi(args[0]);
1153 } catch (...) { 931 } catch (...) {
1154 throw std::invalid_argument("invalid number '" + args[0] + "'"); 932 throw std::invalid_argument("invalid number '" + args[0] + "'");
1155 } 933 }
1156 934
1157 auto result = request(irccdctl, { 935 request(ctl, {
1158 { "command", "rule-remove" }, 936 { "command", "rule-remove" },
1159 { "index", index } 937 { "index", index }
1160 }); 938 });
1161 939 }
1162 check(result); 940
1163 } 941 /*
1164 942 * rule_move_cli.
1165 /* 943 * ------------------------------------------------------------------
1166 * RuleMoveCli. 944 */
1167 * ------------------------------------------------------------------ 945
1168 */ 946 std::string rule_move_cli::name() const
1169 947 {
1170 RuleMoveCli::RuleMoveCli() 948 return "rule-move";
1171 : Cli("rule-move", 949 }
1172 "move a rule to a new position", 950
1173 "rule-move source destination", 951 void rule_move_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
1174 "Move a rule from the given source at the specified destination index.\n\n"
1175 "The rule will replace the existing one at the given destination moving\ndown every "
1176 "other rules. If destination is greater or equal the number of rules,\nthe rule "
1177 "is moved to the end.\n\n"
1178 "Example:\n"
1179 "\tirccdctl rule-move 0 5\n"
1180 "\tirccdctl rule-move 4 3")
1181 {
1182 }
1183
1184 void RuleMoveCli::exec(Irccdctl &irccdctl, const std::vector<std::string> &args)
1185 { 952 {
1186 if (args.size() < 2) 953 if (args.size() < 2)
1187 throw std::invalid_argument("rule-move requires 2 arguments"); 954 throw std::invalid_argument("rule-move requires 2 arguments");
1188 955
1189 int from = 0; 956 int from = 0;
1194 to = std::stoi(args[1]); 961 to = std::stoi(args[1]);
1195 } catch (...) { 962 } catch (...) {
1196 throw std::invalid_argument("invalid number"); 963 throw std::invalid_argument("invalid number");
1197 } 964 }
1198 965
1199 check(request(irccdctl, { 966 request(ctl, {
1200 { "command", "rule-move" }, 967 { "command", "rule-move" },
1201 { "from", from }, 968 { "from", from },
1202 { "to", to } 969 { "to", to }
1203 })); 970 });
1204 } 971 }
1205 972
1206 /* 973 /*
1207 * WatchCli. 974 * WatchCli.
1208 * ------------------------------------------------------------------ 975 * ------------------------------------------------------------------
1359 std::cout << "username: " << json_util::pretty(v, "username") << "\n"; 1126 std::cout << "username: " << json_util::pretty(v, "username") << "\n";
1360 std::cout << "host: " << json_util::pretty(v, "host") << "\n"; 1127 std::cout << "host: " << json_util::pretty(v, "host") << "\n";
1361 std::cout << "realname: " << json_util::pretty(v, "realname") << "\n"; 1128 std::cout << "realname: " << json_util::pretty(v, "realname") << "\n";
1362 } 1129 }
1363 1130
1364 const std::unordered_map<std::string, std::function<void (const nlohmann::json &)>> events{ 1131 const std::unordered_map<std::string, std::function<void (const nlohmann::json&)>> events{
1365 { "onChannelMode", onChannelMode }, 1132 { "onChannelMode", onChannelMode },
1366 { "onChannelNotice", onChannelNotice }, 1133 { "onChannelNotice", onChannelNotice },
1367 { "onConnect", onConnect }, 1134 { "onConnect", onConnect },
1368 { "onInvite", onInvite }, 1135 { "onInvite", onInvite },
1369 { "onJoin", onJoin }, 1136 { "onJoin", onJoin },
1378 { "onQuery", onQuery }, 1145 { "onQuery", onQuery },
1379 { "onTopic", onTopic }, 1146 { "onTopic", onTopic },
1380 { "onWhois", onWhois } 1147 { "onWhois", onWhois }
1381 }; 1148 };
1382 1149
1150 void get_event(ctl::controller& ctl, std::string fmt)
1151 {
1152 ctl.recv([&ctl, fmt] (auto code, auto message) {
1153 if (code)
1154 throw boost::system::system_error(code);
1155
1156 auto it = events.find(json_util::to_string(message["event"]));
1157
1158 if (it != events.end()) {
1159 if (fmt == "json")
1160 std::cout << message.dump(4) << std::endl;
1161 else {
1162 it->second(message);
1163 std::cout << std::endl;
1164 }
1165 }
1166
1167 get_event(ctl, std::move(fmt));
1168 });
1169 }
1170
1383 } // !namespace 1171 } // !namespace
1384 1172
1385 WatchCli::WatchCli() 1173 std::string watch_cli::name() const
1386 : Cli("watch", 1174 {
1387 "watch irccd events", 1175 return "watch";
1388 "watch [-f|--format json|native]", 1176 }
1389 "Start watching irccd events.\n\n" 1177
1390 "You can use different output formats, native is human readable format, json is\n" 1178 void watch_cli::exec(ctl::controller& ctl, const std::vector<std::string>& args)
1391 "pretty formatted json.\n\n" 1179 {
1392 "Example:\n" 1180 auto fmt = format(args);
1393 "\tirccdctl watch\n"
1394 "\tirccdctl watch -f json")
1395 {
1396 }
1397
1398 void WatchCli::exec(Irccdctl& client, const std::vector<std::string>& args)
1399 {
1400 std::string fmt = format(args);
1401 1181
1402 if (fmt != "native" && fmt != "json") 1182 if (fmt != "native" && fmt != "json")
1403 throw std::invalid_argument("invalid format given: " + fmt); 1183 throw std::invalid_argument("invalid format given: " + fmt);
1404 1184
1405 auto id = client.client().onEvent.connect([&] (auto ev) { 1185 get_event(ctl, fmt);
1406 try {
1407 auto name = ev.find("event");
1408
1409 if (name == ev.end() || !name->is_string())
1410 return;
1411
1412 auto it = events.find(*name);
1413
1414 // Silently ignore to avoid breaking user output.
1415 if (it == events.end())
1416 return;
1417
1418 if (fmt == "json")
1419 std::cout << ev.dump(4) << std::endl;
1420 else {
1421 it->second(ev);
1422 std::cout << std::endl;
1423 }
1424 } catch (...) {
1425 }
1426 });
1427
1428 try {
1429 while (client.client().isConnected()) {
1430 net_util::poll(500, client);
1431 }
1432 } catch (const std::exception &ex) {
1433 log::warning() << ex.what() << std::endl;
1434 }
1435
1436 client.client().onEvent.disconnect(id);
1437 } 1186 }
1438 1187
1439 } // !cli 1188 } // !cli
1440 1189
1441 } // !irccd 1190 } // !irccd