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 	tagmsg,
30 }
31 /++
32 + An IRC message, passed between clients.
33 +/
34 struct Message {
35 	import std.algorithm.iteration : splitter;
36 	import std.algorithm.searching : endsWith, findSplit, startsWith;
37 	///This message's payload. Will include \x01 characters if the message is CTCP.
38 	string msg;
39 
40 	/++
41 	+ Type of message.
42 	+
43 	+ NOTICE and PRIVMSG are identical, but replying to a NOTICE
44 	+ is discouraged.
45 	+/
46 	MessageType type;
47 
48 	///Whether or not the message was the result of the server echoing back our messages.
49 	bool isEcho;
50 
51 	/++
52 	+ Whether or not the message was a CTCP message.
53 	+
54 	+ Note that some clients may mangle long CTCP messages by truncation. Those
55 	+ messages will not be detected as CTCP messages.
56 	+/
57 	auto isCTCP() const {
58 		return (msg.startsWith("\x01")) && (msg.endsWith("\x01"));
59 	}
60 	///Whether or not the message is safe to reply to.
61 	auto isReplyable() const {
62 		return type != MessageType.notice;
63 	}
64 	///The CTCP command, if this is a CTCP message.
65 	auto ctcpCommand() const
66 		in(isCTCP, "This is not a CTCP message!")
67 	{
68 		auto split = msg[1..$-1].splitter(" ");
69 		return split.front;
70 	}
71 	///The arguments after the CTCP command, if this is a CTCP message.
72 	auto ctcpArgs() const
73 		in(isCTCP, "This is not a CTCP message!")
74 	{
75 		auto split = msg[1..$-1].findSplit(" ");
76 		return split[2];
77 	}
78 	bool opEquals(string str) @safe pure nothrow @nogc const {
79 		return str == msg;
80 	}
81 	auto toHash() const {
82 		return hashOf(msg);
83 	}
84 	string toString() @safe pure nothrow @nogc const {
85 		return msg;
86 	}
87 }
88 ///
89 @safe pure nothrow @nogc unittest {
90 	{
91 		auto msg = Message("Hello!", MessageType.notice);
92 		assert(!msg.isCTCP);
93 		assert(!msg.isReplyable);
94 	}
95 	{
96 		auto msg = Message("Hello!", MessageType.privmsg);
97 		assert(msg.isReplyable);
98 	}
99 	{
100 		auto msg = Message("\x01ACTION does a thing\x01", MessageType.privmsg);
101 		assert(msg.isCTCP);
102 		assert(msg.ctcpCommand == "ACTION");
103 		assert(msg.ctcpArgs == "does a thing");
104 	}
105 	{
106 		auto msg = Message("\x01VERSION\x01", MessageType.privmsg);
107 		assert(msg.isCTCP);
108 		assert(msg.ctcpCommand == "VERSION");
109 		assert(msg.ctcpArgs == "");
110 	}
111 }
112 @safe pure nothrow @nogc unittest {
113 	assert(Message("Hello!", MessageType.notice).toHash == Message("Hello!", MessageType.privmsg).toHash);
114 }