1 /++
2     Connects to a server and joins all specified channels.
3     Logs all events that occur to stdout.
4 +/
5 module app;
6 
7 import core.time;
8 import std.functional;
9 import std.stdio;
10 import std.string;
11 
12 import vibe.core.args;
13 import vibe.core.core;
14 
15 import vibeirc;
16 
17 IRCClient bot;
18 string host;
19 string nickname;
20 string password;
21 string[] channels;
22 ushort port = 6667;
23 
24 shared static this()
25 {
26     host = readRequiredOption!string(
27         "a|host",
28         "Host to connect to",
29     );
30     nickname = readRequiredOption!string(
31         "n|nickname",
32         "Nickname to use",
33     );
34     
35     //readOption can't handle string[] :(
36     auto channelList = readRequiredOption!string(
37         "c|channels",
38         "List of channels to join, separated by commas",
39     );
40     channels = channelList.split(",");
41     
42     readOption(
43         "p|port",
44         &port,
45         "Port to connect on",
46     );
47     readOption(
48         "P|password",
49         &password,
50         "Password to use when connecting, if any",
51     );
52     
53     bot = new IRCClient;
54     bot.nickname = nickname;
55     bot.onDisconnect = toDelegate(&onDisconnect);
56     bot.onLogin = toDelegate(&onLogin);
57     bot.onMessage = toDelegate(&onMessage);
58     bot.onNotice = toDelegate(&onMessage);
59     bot.onUserJoin = toDelegate(&onUserJoin);
60     bot.onUserPart = toDelegate(&onUserPart);
61     bot.onUserQuit = toDelegate(&onUserQuit);
62     bot.onUserRename = toDelegate(&onUserRename);
63     bot.onUserKick = toDelegate(&onUserKick);
64     
65     //defer connection until after arguments have been processed
66     runTask(toDelegate(&connect));
67 }
68 
69 void connect()
70 {
71     bot.connect(host, port, password);
72 }
73 
74 void onDisconnect(string reason)
75 {
76     writeln("Disconnected: ", reason);
77     sleep(10.seconds);
78     writeln("Attempting to reconnect");
79     connect;
80 }
81 
82 void onLogin()
83 {
84     writeln("Logged in");
85     
86     foreach(channel; channels)
87     {
88         writeln("Joining ", channel);
89         bot.join(channel);
90     }
91 }
92 
93 void onMessage(Message message)
94 {
95     string bodyFormat;
96     string bracketText;
97     
98     if(message.isCTCP)
99     {
100         if(message.ctcpCommand != "ACTION")
101         {
102             writefln(
103                 "%s sends CTCP %s",
104                 message.sender.nickname,
105                 message.ctcpCommand,
106             );
107             
108             return;
109         }
110         
111         bodyFormat = "* %s %s";
112     }
113     else
114         bodyFormat = "<%s> %s";
115     
116     if(message.target == bot.nickname)
117         bracketText = "Private Message";
118     else
119         bracketText = message.target;
120     
121     writefln(
122         "[%s] " ~ bodyFormat,
123         bracketText,
124         message.sender.nickname,
125         message.message,
126     );
127 }
128 
129 void onUserJoin(User user, string channel)
130 {
131     writefln(
132         "[%s] %s joined",
133         channel,
134         user.nickname,
135     );
136 }
137 
138 void onUserPart(User user, string channel, string reason)
139 {
140     writefln(
141         "[%s] %s left (%s)",
142         channel,
143         user.nickname,
144         reason == null ? "No reason given" : reason,
145     );
146 }
147 
148 void onUserQuit(User user, string reason)
149 {
150     writefln(
151         "%s quit (%s)",
152         user.nickname,
153         reason == null ? "No reason given" : reason,
154     );
155 }
156 
157 void onUserRename(User user, string newNick)
158 {
159     writefln(
160         "%s is now known as %s",
161         user.nickname,
162         newNick,
163     );
164 }
165 
166 void onUserKick(User kicker, string kickee, string channel, string reason)
167 {
168     writefln(
169         "[%s] %s kicked %s (%s)",
170         channel,
171         kicker.nickname,
172         kickee,
173         reason == null ? "No reason given" : reason,
174     );
175 }