1 module virc.message;
2 
3 
4 /++
5 +
6 +/
7 struct MessageMetadata {
8 	import std.datetime.systime : SysTime;
9 	import std.typecons : Nullable;
10 	import virc.ircv3.batch : BatchInformation;
11 	import virc.numerics.definitions : Numeric;
12 	///
13 	SysTime time;
14 	///
15 	string[string] tags;
16 	///
17 	Nullable!Numeric messageNumeric;
18 	///
19 	string original;
20 	///
21 	BatchInformation batch;
22 }
23 /++
24 +
25 +/
26 enum MessageType {
27 	notice,
28 	privmsg
29 }
30 /++
31 + An IRC message, passed between clients.
32 +/
33 struct Message {
34 	import std.algorithm.iteration : splitter;
35 	import std.algorithm.searching : endsWith, findSplit, startsWith;
36 	///This message's payload. Will include \x01 characters if the message is CTCP.
37 	string msg;
38 
39 	/++
40 	+ Type of message.
41 	+
42 	+ NOTICE and PRIVMSG are identical, but replying to a NOTICE
43 	+ is discouraged.
44 	+/
45 	MessageType type;
46 
47 	///Whether or not the message was the result of the server echoing back our messages.
48 	bool isEcho;
49 
50 	/++
51 	+ Whether or not the message was a CTCP message.
52 	+
53 	+ Note that some clients may mangle long CTCP messages by truncation. Those
54 	+ messages will not be detected as CTCP messages.
55 	+/
56 	auto isCTCP() const {
57 		return (msg.startsWith("\x01")) && (msg.endsWith("\x01"));
58 	}
59 	///Whether or not the message is safe to reply to.
60 	auto isReplyable() const {
61 		return type != MessageType.notice;
62 	}
63 	///The CTCP command, if this is a CTCP message.
64 	auto ctcpCommand() const
65 		in(isCTCP, "This is not a CTCP message!")
66 	{
67 		auto split = msg[1..$-1].splitter(" ");
68 		return split.front;
69 	}
70 	///The arguments after the CTCP command, if this is a CTCP message.
71 	auto ctcpArgs() const
72 		in(isCTCP, "This is not a CTCP message!")
73 	{
74 		auto split = msg[1..$-1].findSplit(" ");
75 		return split[2];
76 	}
77 	bool opEquals(string str) @safe pure nothrow @nogc const {
78 		return str == msg;
79 	}
80 	auto toHash() const {
81 		return hashOf(msg);
82 	}
83 	string toString() @safe pure nothrow @nogc const {
84 		return msg;
85 	}
86 }
87 ///
88 @safe pure nothrow @nogc unittest {
89 	{
90 		auto msg = Message("Hello!", MessageType.notice);
91 		assert(!msg.isCTCP);
92 		assert(!msg.isReplyable);
93 	}
94 	{
95 		auto msg = Message("Hello!", MessageType.privmsg);
96 		assert(msg.isReplyable);
97 	}
98 	{
99 		auto msg = Message("\x01ACTION does a thing\x01", MessageType.privmsg);
100 		assert(msg.isCTCP);
101 		assert(msg.ctcpCommand == "ACTION");
102 		assert(msg.ctcpArgs == "does a thing");
103 	}
104 	{
105 		auto msg = Message("\x01VERSION\x01", MessageType.privmsg);
106 		assert(msg.isCTCP);
107 		assert(msg.ctcpCommand == "VERSION");
108 		assert(msg.ctcpArgs == "");
109 	}
110 }
111 @system pure nothrow @nogc unittest {
112 	assert(Message("Hello!", MessageType.notice).toHash == Message("Hello!", MessageType.privmsg).toHash);
113 }