1 /++
2     A collection of constants used throughout the program.
3 
4     This acts as a compile-time configuration file to reduce ad-hoc magic numbers.
5 
6     Copyright: [JR](https://github.com/zorael)
7     License: [Boost Software License 1.0](https://www.boost.org/users/license.html)
8 
9     Authors:
10         [JR](https://github.com/zorael)
11  +/
12 module kameloso.constants;
13 
14 private:
15 
16 import kameloso.semver : KamelosoSemVer, KamelosoSemVerPrerelease;
17 
18 
19 version(DigitalMars)
20 {
21     /// String of the compiler that was used to compile this binary with. Here: `dmd`.
22     enum compiler = "dmd";
23 }
24 else version(LDC)
25 {
26     /// String of the compiler that was used to compile this binary with. Here: `ldc`.
27     enum compiler = "ldc";
28 }
29 else version(GNU)
30 {
31     /// String of the compiler that was used to compile this binary with. Here: `gdc`.
32     enum compiler = "gdc";
33 }
34 else
35 {
36     /// String of the compiler that was used to compile this binary with. Here: no idea.
37     enum compiler = "<unknown>";
38 }
39 
40 
41 // buildCompilerVersionString
42 /++
43     Replaces the following expression and lowers compilation memory by avoiding
44     use of compile-time [std.format.format|format].
45 
46     ---
47     import std.compiler;
48     enum compilerVersion = format("%d.%03d", version_major, version_minor);
49     ---
50 
51     Returns:
52         The compiler version as a string in the format of `{MAJOR}.{MINOR}` (eg. `2.100`).
53  +/
54 auto buildCompilerVersionString()
55 {
56     import lu.conv : toAlphaInto;
57     import std.array : Appender;
58 
59     enum major = cast(uint)(__VERSION__ / 1000);
60     enum minor = cast(uint)(__VERSION__ % 1000);
61 
62     Appender!(char[]) sink;
63     sink.reserve(5);  // 2.098
64 
65     major.toAlphaInto(sink);
66     sink.put('.');
67     minor.toAlphaInto!(3, 3)(sink);
68 
69     return sink.data.idup;
70 }
71 
72 
73 // buildVersionString
74 /++
75     Replaces the following expression and lowers compilation memory by avoiding
76     use of compile-time [std.format.format|format].
77 
78     ---
79     enum version_ = "%d.%d.%d%s%s"
80         .format(
81             KamelosoSemVer.majorVersion,
82             KamelosoSemVer.minorVersion,
83             KamelosoSemVer.patchVersion,
84             KamelosoSemVerPrerelease.length ? "-" : string.init,
85             KamelosoSemVerPrerelease);
86     ---
87 
88     Returns:
89         The program version as a string in the format of
90         `{MAJOR}.{MINOR}.{PATCH}{-PRERELEASE}` (eg. `3.2.0-alpha.1`).
91         `{-PRERELEASE}` is optional.
92  +/
93 auto buildVersionString()
94 {
95     import lu.conv : toAlphaInto;
96     import std.array : Appender;
97 
98     Appender!(char[]) sink;
99     sink.reserve(16);  // 10.10.10-alpha.1
100 
101     with (KamelosoSemVer)
102     {
103         majorVersion.toAlphaInto(sink);
104         sink.put('.');
105         minorVersion.toAlphaInto(sink);
106         sink.put('.');
107         patchVersion.toAlphaInto(sink);
108 
109         if (KamelosoSemVerPrerelease.length)
110         {
111             sink.put('-');
112             sink.put(cast(string)KamelosoSemVerPrerelease);
113         }
114     }
115 
116     return sink.data.idup;
117 }
118 
119 
120 public:
121 
122 
123 // KamelosoInfo
124 /++
125     Meta-information about the program.
126  +/
127 enum KamelosoInfo
128 {
129     version_ = .buildVersionString(), /// Version as a string.
130     built = __TIMESTAMP__, /// Timestamp of when the binary was built.
131     compiler = .compiler,  /// Compiler used to build this binary.
132     compilerVersion = .buildCompilerVersionString(),  /// Compiler version used to build this binary.
133     source = "https://github.com/zorael/kameloso",  /// GitHub source link.
134 }
135 
136 
137 // KamelosoDefaults
138 /++
139     Kameloso defaults, strings version.
140  +/
141 enum KamelosoDefaults
142 {
143     /++
144         Default user to use when logging onto a server (the USER command).
145         Additionally becomes the bot's IDENT identifier (prepended with a '~'),
146         if a separate `identd` server is not being run.
147      +/
148     user = "kameloso",
149 
150     /// Default server address.
151     serverAddress = "irc.libera.chat",
152 
153     /// The default GEOC/"real name" string.
154     realName = "kameloso IRC bot v$version",
155 
156     /// The default quit reason, when the bot exits. Supports some string replacements.
157     quitReason = "kameloso IRC bot v$version @ $source",
158 
159     /// The default part reason, when the bot is asked to part a channel.
160     partReason = quitReason,
161 
162     /++
163         When a nickname was already taken during registration, append this followed
164         by some random numbers to it to generate a new one.
165 
166         A separator of "|" and a taken nickname of "guest" thus gives nicknames like "guest|1".
167         A separator of "^" gives nicknames like "guest^2".
168      +/
169     altNickSeparator = "|",
170 }
171 
172 
173 // KamelosoDefaultIntegers
174 /++
175     Kameloso defaults, integers version.
176  +/
177 enum KamelosoDefaultIntegers
178 {
179     /// Default server port.
180     port = 6667,
181 }
182 
183 
184 // KamelosoFilenames
185 /++
186     Kameloso filenames.
187  +/
188 enum KamelosoFilenames
189 {
190     /++
191         The main configuration file.
192      +/
193     configuration = "kameloso.conf",
194 
195     /++
196         The file containing user account classifiers, specifying which accounts
197         are whitelisted, operators and/or blacklisted.
198      +/
199     users = "users.json",
200 
201     /++
202         The file containing user "account" hostmasks, mapping what we still
203         consider accounts to hostmasks, on servers that don't employ services.
204      +/
205     hostmasks = "hostmasks.json",
206 }
207 
208 
209 // ConnectionDefaultIntegers
210 /++
211     Connection defaults, integers version.
212  +/
213 enum ConnectionDefaultIntegers
214 {
215     /// How many times to attempt to connect to an IP before moving on to the next one.
216     retries = 4,
217 }
218 
219 
220 // ConnectionDefaultFloats
221 /++
222     Connection defaults, floating point version.
223  +/
224 enum ConnectionDefaultFloats : double
225 {
226     /// By what to multiply the connect timeout after failing an attempt.
227     delayIncrementMultiplier = 1.5,
228 
229     /// By what to multiply [Timeout.receiveMsecs] with to shorten reads.
230     receiveShorteningMultiplier = 0.25,
231 
232     /// How many messages to send per second, maximum.
233     messageRate = 1.2,
234 
235     /// How many messages to immediately send in one go, before throttling kicks in.
236     messageBurst = 3.0,
237 
238     /++
239         How many messages to send per second, maximum. For *fast* sends on Twitch servers.
240 
241         FIXME: Tweak value.
242      +/
243     messageRateTwitchFast = 3.0,
244 
245     /++
246         How many messages to immediately send in one go, before throttling kicks in.
247         For *fast* sends on Twitch servers.
248 
249         FIXME: Tweak value.
250      +/
251     messageBurstTwitchFast = 10.0,
252 
253     /++
254         How many messages to send per second, maximum. For *slow* sends on Twitch servers.
255 
256         FIXME: Tweak value.
257      +/
258     messageRateTwitchSlow = 0.5,
259 
260     /++
261         How many messages to immediately send in one go, before throttling kicks in.
262         For *slow* sends on Twitch servers.
263 
264         FIXME: Tweak value.
265      +/
266     messageBurstTwitchSlow = 0.5,
267 }
268 
269 
270 // BufferSize
271 /++
272     Buffer sizes in bytes.
273  +/
274 enum BufferSize
275 {
276     /++
277         The receive buffer size as set as a [std.socket.SocketOption|SocketOption].
278      +/
279     socketOptionReceive = 2048,
280 
281     /++
282         The send buffer size as set as a [std.socket.SocketOption|SocketOption].
283      +/
284     socketOptionSend = 1024,
285 
286     /++
287         The actual buffer array size used when reading from the socket.
288      +/
289     socketReceive = 2048,
290 
291     /++
292         The maximum number of queued outgoing lines to buffer. Anything above
293         this will crash the program with a buffer overrun. It can be arbitrarily big.
294      +/
295     outbuffer = 512,
296 
297     /++
298         The maximum number of queued priority lines to buffer. These are rare.
299      +/
300     priorityBuffer = 64,
301 
302     /++
303         How many bytes to preallocate a buffer for when printing objects to
304         screen with the [kameloso.printing] templates. This value times the
305         number of objects to print.
306      +/
307     printObjectBufferPerObject = 1024,
308 
309     /++
310         How many bytes to allocate for the stdout buffer, when we need to do so explicitly.
311      +/
312     vbufStdout = 16_384,
313 
314     /++
315         How large to make [core.thread.fiber.Fiber|Fiber] stacks, so they don't
316         overflow (which they seem to have a knack for doing).
317      +/
318     fiberStack = 131_072,
319 }
320 
321 
322 // Timeout
323 /++
324     Various timeouts, in seconds unless specified otherwise.
325  +/
326 enum Timeout
327 {
328     /++
329         The send attempt timeout as set as a [std.socket.SocketOption|SocketOption],
330         in milliseconds.
331      +/
332     sendMsecs = 15_000,
333 
334     /++
335         The receive attempt timeout as set as a [std.socket.SocketOption|SocketOption],
336         in milliseconds.
337      +/
338     receiveMsecs = 1000,
339 
340     /++
341         The amount of time to spend with a shortened receive timeout, in milliseconds.
342         After this, it reverts to [Timeout.receiveMsecs].
343      +/
344     maxShortenDurationMsecs = 2000,
345 
346     /++
347         The maximum amount of time to wait between connection attempts.
348      +/
349     connectionDelayCap = 300,
350 
351     /++
352         The amount of seconds to wait before retrying after a failed connection attempt.
353      +/
354     connectionRetry = 5,
355 
356     /++
357         The amount of seconds to wait before retrying to connect after an instant
358         failure to register on Twitch.
359      +/
360     twitchRegistrationFailConnectionRetryMsecs = 500,
361 
362     /++
363         How long to wait before allowing to re-issue a WHOIS query for a user.
364 
365         This is merely to stop us from spamming queries for the same person
366         without hysteresis.
367      +/
368     whoisRetry = 30,
369 
370     /++
371         How long a replayable event is expected to be relevant. Before this it
372         will be replayed, after this it will be discarded.
373 
374         Note: WHOIS-replays will break if the ping toward the server reaches this value.
375      +/
376     whoisDiscard = 10,
377 
378     /++
379         The length of the window in which replays may be queued before the timer
380         towards [Timeout.whoisRetry] kicks in.
381      +/
382     whoisGracePeriod = 3,
383 
384     /++
385         How long to wait after encountering an error when reading from the server,
386         before trying anew.
387 
388         Not having a small delay could cause it to spam the screen with errors
389         as fast as it can.
390      +/
391     readErrorGracePeriodMsecs = 100,
392 
393     /++
394         How long to keep trying to read from the sever when not receiving anything
395         at all before the connection is considered lost.
396      +/
397     connectionLost = 600,
398 
399     /++
400         Timeout for HTTP GET requests.
401      +/
402     httpGET = 10,
403 
404     /++
405         Timeout for concurrency message reads (in between socket reads).
406      +/
407     messageReadMsecs = 1500,
408 }
409 
410 
411 // Periodicals
412 /++
413     Timings and timeouts of various periodical events.
414  +/
415 enum Periodicals
416 {
417     /++
418         How often to rehash plugins' `users` associative arrays for more efficient lookups.
419      +/
420     userAARehashMinutes = 60,
421 }
422 
423 
424 // ShellReturnValue
425 /++
426     Magic number shell exit codes.
427  +/
428 enum ShellReturnValue
429 {
430     /++
431         Success. No error encountered.
432      +/
433     success = 0,
434 
435     /++
436         Generic error.
437      +/
438     failure = 1,
439 
440     /++
441         Failure encountered during `getopt`.
442      +/
443     getoptFailure = 2,
444 
445     /++
446         Failure encountered when setting up terminal buffering.
447      +/
448     terminalSetupFailure = 3,
449 
450     /++
451         Settings verification failed.
452      +/
453     settingsVerificationFailure = 4,
454 
455     /++
456         `--set` argument syntax error.
457      +/
458     customConfigSyntaxFailure = 5,
459 
460     /++
461         `--set` other failure.
462      +/
463     customConfigFailure = 6,
464 
465     /++
466         Failure encountered during host address resolution.
467      +/
468     resolutionFailure = 21,
469 
470     /++
471         Failure encountered during connection attempt.
472      +/
473     connectionFailure = 22,
474 
475     /++
476         Failure encountered when a plugin tried to load resources.
477      +/
478     pluginResourceLoadFailure = 31,
479 
480     /++
481         Generic exception was thrown when a plugin tried to load resources.
482      +/
483     pluginResourceLoadException = 32,
484 
485     /++
486         Failure encountered during plugin setup.
487      +/
488     pluginSetupFailure = 33,
489 
490     /++
491         Generic exception was thrown when a plugin tried to setup.
492      +/
493     pluginSetupException = 34,
494 }
495 
496 
497 // MagicErrorStrings
498 /++
499     Hardcoded error strings.
500  +/
501 enum MagicErrorStrings
502 {
503     /++
504         Failed to set up an SSL context, original library line ([requests]).
505      +/
506     sslContextCreationFailure = "can't complete call to TLS_method",
507 
508     /++
509         Could not initialise SSL libraries, original line ([arsd.http2]).
510      +/
511     sslLibraryNotFound = "libssl library not found",
512 
513     /++
514         Could not initialise SSL libraries, rewritten line.
515      +/
516     sslLibraryNotFoundRewritten = "SSL libraries not found",
517 
518     /++
519         Wiki link oneliner, tagged.
520      +/
521     visitWikiOneliner = "Visit <l>https://github.com/zorael/kameloso/wiki/OpenSSL</> for more information.",
522 
523     /++
524         `--get-openssl` suggestion hint oneliner, tagged.
525      +/
526     getOpenSSLSuggestion = "Suggestion: <l>--get-openssl</> may help.",
527 }
528 
529 
530 // DefaultColours
531 /++
532     Default colours gathered in one struct namespace.
533 
534     This makes it easier to compile-time customise colours to your liking.
535  +/
536 version(Colours)
537 struct DefaultColours
538 {
539 private:
540     import kameloso.logger : LogLevel;
541     import kameloso.terminal.colours.defs : TerminalForeground;
542 
543     alias TF = TerminalForeground;
544 
545 public:
546     /++
547         Colours for timestamps, shared between event-printing and logging.
548      +/
549     enum TimestampColour : TerminalForeground
550     {
551         /++
552             For dark terminal backgrounds. Was
553             [kameloso.terminal.colours.defs.TerminalForeground.white_|TerminalForeground.white_].
554          +/
555         dark = TF.default_,
556 
557         /++
558             For bright terminal backgrounds. Was
559             [kameloso.terminal.colours.defs.TerminalForeground.black_|TerminalForeground.black_].
560          +/
561         bright = TF.default_,
562     }
563 
564     /// Logger colours to use with a dark terminal background.
565     static immutable TerminalForeground[256] logcoloursDark  =
566     [
567         LogLevel.all      : TF.white,        /// LogLevel.all, or just `log`
568         LogLevel.trace    : TF.default_,     /// `trace`
569         LogLevel.info     : TF.lightgreen,   /// `info`
570         LogLevel.warning  : TF.lightred,     /// `warning`
571         LogLevel.error    : TF.red,          /// `error`
572         LogLevel.critical : TF.red,          /// `critical`
573         LogLevel.fatal    : TF.red,          /// `fatal`
574         LogLevel.off      : TF.default_,     /// `off`
575     ];
576 
577     /// Logger colours to use with a bright terminal background.
578     static immutable TerminalForeground[256] logcoloursBright  =
579     [
580         LogLevel.all      : TF.black,        /// LogLevel.all, or just `log`
581         LogLevel.trace    : TF.default_,     /// `trace`
582         LogLevel.info     : TF.green,        /// `info`
583         LogLevel.warning  : TF.red,          /// `warning`
584         LogLevel.error    : TF.red,          /// `error`
585         LogLevel.critical : TF.red,          /// `critical`
586         LogLevel.fatal    : TF.red,          /// `fatal`
587         LogLevel.off      : TF.default_,     /// `off`
588     ];
589 }