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 }