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 }