1 /++
2     Implementation of Admin plugin functionality that borders on debugging.
3     For internal use.
4 
5     The [dialect.defs.IRCEvent|IRCEvent]-annotated handlers must be in the same module
6     as the [kameloso.plugins.admin.base.AdminPlugin|AdminPlugin], but these implementation
7     functions can be offloaded here to limit module size a bit.
8 
9     See_Also:
10         [kameloso.plugins.admin.base]
11 
12     Copyright: [JR](https://github.com/zorael)
13     License: [Boost Software License 1.0](https://www.boost.org/users/license.html)
14 
15     Authors:
16         [JR](https://github.com/zorael)
17  +/
18 module kameloso.plugins.admin.debugging;
19 
20 version(WithAdminPlugin):
21 debug:
22 
23 private:
24 
25 import kameloso.plugins.admin.base : AdminPlugin;
26 
27 import kameloso.messaging;
28 import dialect.defs;
29 import std.stdio : stdout;
30 import std.typecons : Flag, No, Yes;
31 
32 package:
33 
34 
35 // onAnyEventImpl
36 /++
37     Prints incoming events to the local terminal, in forms depending on
38     which flags have been set with bot commands.
39 
40     If [kameloso.plugins.admin.base.AdminPlugin.printRaw|AdminPlugin.printRaw] is set by way of
41     invoking [kameloso.plugins.admin.base.onCommandPrintRaw|onCommandPrintRaw], prints all incoming server strings.
42 
43     If [kameloso.plugins.admin.base.AdminPlugin.printBytes|AdminPlugin.printBytes] is set by way of
44     invoking [kameloso.plugins.admin.base.onCommandPrintBytes|onCommandPrintBytes], prints all incoming server strings byte by byte.
45  +/
46 void onAnyEventImpl(AdminPlugin plugin, const ref IRCEvent event)
47 {
48     import std.stdio : write, writefln, writeln;
49 
50     if (plugin.state.settings.headless) return;
51 
52     if (plugin.adminSettings.printRaw)
53     {
54         if (event.tags.length) write('@', event.tags, ' ');
55         writeln(event.raw, '$');
56     }
57 
58     if (plugin.adminSettings.printBytes)
59     {
60         import std.string : representation;
61 
62         foreach (immutable i, immutable c; event.content.representation)
63         {
64             import std.encoding : isValidCodeUnit;
65             import std.utf : replacementDchar;
66 
67             immutable dc = isValidCodeUnit(c) ? dchar(c) : replacementDchar;
68             writefln("[%3d] %s : %03d", i, dc, c);
69         }
70     }
71 
72     if (plugin.state.settings.flush) stdout.flush();
73 }
74 
75 
76 // onCommandShowUserImpl
77 /++
78     Prints the details of one or more specific, supplied users to the local terminal.
79 
80     It basically prints the matching [dialect.defs.IRCUser|IRCUser].
81  +/
82 version(IncludeHeavyStuff)
83 void onCommandShowUserImpl(AdminPlugin plugin, const ref IRCEvent event)
84 {
85     import kameloso.printing : printObject;
86     import std.algorithm.iteration : splitter;
87 
88     if (plugin.state.settings.headless) return;
89 
90     foreach (immutable username; event.content.splitter(' '))
91     {
92         if (const user = username in plugin.state.users)
93         {
94             printObject(*user);
95         }
96         else
97         {
98             import std.format : format;
99 
100             enum pattern = "No such user: <4>%s<c>";
101             immutable message = pattern.format(username);
102             privmsg(plugin.state, event.channel, event.sender.nickname, message);
103         }
104     }
105 }
106 
107 
108 // onCommandShowUsersImpl
109 /++
110     Prints out the current `users` array of the [kameloso.plugins.admin.base.AdminPlugin|AdminPlugin]'s
111     [kameloso.plugins.common.core.IRCPluginState|IRCPluginState] to the local terminal.
112  +/
113 version(IncludeHeavyStuff)
114 void onCommandShowUsersImpl(AdminPlugin plugin)
115 {
116     import kameloso.printing : printObject;
117     import std.stdio : writeln;
118 
119     if (plugin.state.settings.headless) return;
120 
121     foreach (immutable name, const user; plugin.state.users)
122     {
123         writeln(name);
124         printObject(user);
125     }
126 
127     writeln(plugin.state.users.length, " users.");
128     if (plugin.state.settings.flush) stdout.flush();
129 }
130 
131 
132 // onCommandSudoImpl
133 /++
134     Sends supplied text to the server, verbatim.
135 
136     You need basic knowledge of IRC server strings to use this.
137  +/
138 void onCommandSudoImpl(AdminPlugin plugin, const ref IRCEvent event)
139 {
140     raw(plugin.state, event.content);
141 }
142 
143 
144 // onCommandPrintRawImpl
145 /++
146     Toggles a flag to print all incoming events *raw*.
147 
148     This is for debugging purposes.
149  +/
150 void onCommandPrintRawImpl(AdminPlugin plugin, const ref IRCEvent event)
151 {
152     import std.conv : text;
153     import std.format : format;
154 
155     if (plugin.state.settings.headless) return;
156 
157     plugin.adminSettings.printRaw = !plugin.adminSettings.printRaw;
158 
159     enum pattern = "Printing all: <b>%s<b>";
160     immutable message = pattern.format(plugin.adminSettings.printRaw);
161     privmsg(plugin.state, event.channel, event.sender.nickname, message);
162 }
163 
164 
165 // onCommandPrintBytesImpl
166 /++
167     Toggles a flag to print all incoming events *as individual bytes*.
168 
169     This is for debugging purposes.
170  +/
171 void onCommandPrintBytesImpl(AdminPlugin plugin, const ref IRCEvent event)
172 {
173     import std.conv : text;
174     import std.format : format;
175 
176     if (plugin.state.settings.headless) return;
177 
178     plugin.adminSettings.printBytes = !plugin.adminSettings.printBytes;
179 
180     enum pattern = "Printing bytes: <b>%s<b>";
181     immutable message = pattern.format(plugin.adminSettings.printBytes);
182     privmsg(plugin.state, event.channel, event.sender.nickname, message);
183 }
184 
185 
186 // onCommandStatusImpl
187 /++
188     Dumps information about the current state of the bot to the local terminal.
189 
190     This can be very spammy.
191  +/
192 version(IncludeHeavyStuff)
193 void onCommandStatusImpl(AdminPlugin plugin)
194 {
195     import kameloso.common : logger;
196     import kameloso.printing : printObjects;
197     import std.stdio : writeln;
198 
199     if (plugin.state.settings.headless) return;
200 
201     logger.log("Current state:");
202     printObjects!(Yes.all)(plugin.state.client, plugin.state.server);
203     writeln();
204 
205     logger.log("Channels:");
206     foreach (immutable channelName, const channel; plugin.state.channels)
207     {
208         writeln(channelName);
209         printObjects(channel);
210     }
211     //writeln();
212 
213     /*logger.log("Users:");
214     foreach (immutable nickname, const user; plugin.state.users)
215     {
216         writeln(nickname);
217         printObject(user);
218     }*/
219 
220     if (plugin.state.settings.flush) stdout.flush();
221 }
222 
223 
224 // onCommandBusImpl
225 /++
226     Sends an internal bus message to other plugins, much like how such can be
227     sent with the Pipeline plugin.
228  +/
229 void onCommandBusImpl(AdminPlugin plugin, const string input)
230 {
231     import kameloso.common : logger;
232     import kameloso.thread : ThreadMessage, boxed;
233     import lu.string : contains, nom;
234     import std.concurrency : send;
235     import std.stdio : writeln;
236 
237     if (!input.length) return;
238 
239     if (!input.contains!(Yes.decode)(' '))
240     {
241         if (!plugin.state.settings.headless)
242         {
243             logger.info("Sending bus message.");
244             writeln("Header: ", input);
245             writeln("Content: (empty)");
246         }
247 
248         plugin.state.mainThread.send(ThreadMessage.busMessage(input));
249     }
250     else
251     {
252         string slice = input;  // mutable
253         immutable header = slice.nom(' ');
254 
255         if (!plugin.state.settings.headless)
256         {
257             logger.info("Sending bus message.");
258             writeln("Header: ", header);
259             writeln("Content: ", slice);
260         }
261 
262         plugin.state.mainThread.send(ThreadMessage.busMessage(header, boxed(slice)));
263     }
264 
265     if (!plugin.state.settings.headless && plugin.state.settings.flush)
266     {
267         stdout.flush();
268     }
269 }