3 // Copyright 2011 Akari <Akari` @ irc.quakenet.org>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
22 cvar_t irc_server = {CVAR_SAVE, "irc_server", "", "IRC server to connect to"};
23 cvar_t irc_port = {CVAR_SAVE, "irc_port", "6667", "Port of the IRC server"};
24 cvar_t irc_password = {CVAR_SAVE, "irc_password", "", "IRC server password"};
25 cvar_t irc_nick = {CVAR_SAVE, "irc_nick", "", "Your nickname to use on IRC. Note: this cvar only defines your prefered nick, do NOT use this to change your nickname while connected, use irc_chnick instead"};
26 cvar_t irc_connected = {CVAR_READONLY, "irc_connected", "0", "IRC connection state (0 = not connected, 1 = connecting, 2 = connected, 3 = disconnecting)"};
27 cvar_t irc_msgprefix = {CVAR_SAVE, "irc_msgprefix", "^5IRC^0|^7", "What all IRC events will be prefixed with when printed to the console"};
28 cvar_t irc_chatwindow = {CVAR_SAVE, "irc_chatwindow", "2", "0 = IRC messages will be printed in the console only, 1 = IRC messages will go to the chat window, 2 = Only hilights and private messages will appear in the chat window"};
29 cvar_t irc_numeric_errorsonly = {CVAR_SAVE, "irc_numeric_errorsonly", "0", "If 1, any numeric event below 400 won't be printed'"};
30 cvar_t irc_save_target = {CVAR_SAVE, "irc_save_target", "1", "Whether to save target for irc_messagemode or not"};
31 cvar_t irc_current_nick = {CVAR_READONLY, "irc_current_nick", "", "Holds your current IRC nick"};
32 cvar_t irc_hilights = {CVAR_SAVE, "irc_hilights", "", "Space separated list of words to hilight"};
33 cvar_t irc_autochangetarget = {CVAR_SAVE, "irc_autochangetarget", "0", "if 1, will automatically change target for irc_messagemode when a hilight or a private message is recieved, so you can reply instantly. Requires irc_save_target"};
34 cvar_t irc_watched_channels = {CVAR_SAVE, "irc_watched_channels", "", "Space separated list of watched channels. PRIVMSGs from those channels will be always printed to the chat area, regardless of irc_chatwindow setting"};
36 irc_session_t *irc_session_global = NULL;
38 char last_channel[MAX_INPUTLINE];
44 void CL_Irc_Init(void)
46 Cvar_RegisterVariable(&irc_server);
47 Cvar_RegisterVariable(&irc_port);
48 Cvar_RegisterVariable(&irc_password);
49 Cvar_RegisterVariable(&irc_nick);
50 Cvar_RegisterVariable(&irc_connected);
51 Cvar_RegisterVariable(&irc_msgprefix);
52 Cvar_RegisterVariable(&irc_chatwindow);
53 Cvar_RegisterVariable(&irc_numeric_errorsonly);
54 Cvar_RegisterVariable(&irc_save_target);
55 Cvar_RegisterVariable(&irc_current_nick);
56 Cvar_RegisterVariable(&irc_hilights);
57 Cvar_RegisterVariable(&irc_autochangetarget);
58 Cvar_RegisterVariable(&irc_watched_channels);
60 Cmd_AddCommand ("irc_connect", CL_Irc_Connect_f, "Connects you to the IRC server");
61 Cmd_AddCommand ("irc_disconnect", CL_Irc_Disconnect_f, "Disconnects you from the IRC server");
62 Cmd_AddCommand ("irc_say", CL_Irc_Say_f, "Sends a privmsg to a channel or nick");
63 Cmd_AddCommand ("irc_notice", CL_Irc_Notice_f, "Sends a notice to a channel or nick");
64 Cmd_AddCommand ("irc_me", CL_Irc_Me_f, "Sends a CTCP ACTION (/me) to a channel or nick");
65 Cmd_AddCommand ("irc_join", CL_Irc_Join_f, "Joins an IRC channel");
66 Cmd_AddCommand ("irc_part", CL_Irc_Part_f, "Parts an IRC channel");
67 Cmd_AddCommand ("irc_names", CL_Irc_Names_f, "Lists users of an IRC channel");
68 Cmd_AddCommand ("irc_messagemode", CL_Irc_MessageMode_f, "Interactive prompt for sending IRC messages. Meant to be bound to a key");
69 Cmd_AddCommand ("irc_chnick", CL_Irc_ChNick_f, "Changes your nick");
70 Cmd_AddCommand ("irc_raw", CL_Irc_Raw_f, "Sends a raw string to the IRC server");
74 // Function that starts IRC main loop in a thread
77 static void IRC_Thread(void *p)
79 if(irc_run(irc_session_global))
81 if(irc_connected.integer == 3) //irc_disconnect
83 Cvar_SetQuick(&irc_connected, "0");
87 Con_Printf("%s^1Error: ^7%s\n",
89 irc_strerror(irc_errno(irc_session_global))
92 CL_Irc_Disconnect_f();
98 // irc_ console commands callbacks
101 static void CL_Irc_Connect_f(void)
104 pthread_t irc_thread;
106 switch(irc_connected.integer)
109 Con_Printf("%sAlready connecting to %s:%i\n",
110 irc_msgprefix.string,
117 Con_Printf("%sAlready connected to %s:%i.\n",
118 irc_msgprefix.string,
125 if(NOTSET(irc_server))
127 Con_Printf("Please set the irc_server variable\n");
133 Con_Printf("Please set the irc_nick variable\n");
139 Con_Printf("Please set the irc_port variable\n");
143 Cvar_SetQuick(&irc_connected, "1");
145 memset(&cb, 0, sizeof(cb));
146 cb.event_connect = event_connect;
147 cb.event_numeric = event_numeric;
148 cb.event_privmsg = event_privmsg;
149 cb.event_channel = event_channel;
150 cb.event_nick = event_nick;
151 cb.event_quit = event_quit;
152 cb.event_join = event_join;
153 cb.event_part = event_part;
154 cb.event_mode = event_mode;
155 cb.event_umode = event_umode;
156 cb.event_topic = event_topic;
157 cb.event_kick = event_kick;
158 cb.event_notice = event_notice;
159 cb.event_invite = event_invite;
160 cb.event_ctcp_action = event_ctcp_action;
162 Cvar_SetQuick(&irc_current_nick, irc_nick.string);
164 irc_session_global = irc_create_session(&cb);
165 irc_option_set(irc_session_global, LIBIRC_OPTION_STRIPNICKS);
170 NOTSET(irc_password)? NULL : irc_password.string,
172 "dpirc", "DPIRC user"
174 { //Connection failed
175 Con_Printf("%s^1Connection failed: ^7%s\n",
176 irc_msgprefix.string,
177 irc_strerror(irc_errno(irc_session_global))
180 CL_Irc_Disconnect_f();
184 pthread_create (&irc_thread, NULL, (void *) &IRC_Thread, NULL);
187 static void CL_Irc_Disconnect_f(void)
189 if(!irc_connected.integer)
191 Con_Printf("%sNot connected\n",
198 Con_Printf("^1Disconnected from the IRC server\n");
199 Cvar_SetQuick(&irc_connected, "3");
200 irc_cmd_quit(irc_session_global, "Disconnected");
201 irc_destroy_session(irc_session_global);
204 static void CL_Irc_Say_Universal_f(void)
206 int cmdlen, i, j, space;
207 const char *cmd, *dest;
208 char message[MAX_INPUTLINE];
211 if(Cmd_Argc() < 3) switch(irc_msgmode)
213 case MSGMODE_PRIVMSG:
214 Con_Printf("Usage: irc_say channel_or_nick message\n");
217 Con_Printf("Usage: irc_notice channel_or_nick message\n");
220 Con_Printf("Usage: irc_me channel_or_nick message\n");
225 cmdlen = strlen(cmd);
228 for(i = 0, j = 0; i < cmdlen; ++i)
232 message[j++] = cmd[i];
234 else if(cmd[i] == ' ') ++space;
243 case MSGMODE_PRIVMSG:
244 irc_cmd_msg(irc_session_global, dest, message);
245 watched = Irc_IsWatchedChannel(dest);
247 if(ISCHANNEL(dest)) Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n",
248 watched? "\001" : CHATWINDOW,
249 irc_msgprefix.string,
251 irc_current_nick.string,
253 ); else Con_Printf("%s%s^1Privmsg to ^2%s^7: ^3%s\n",
255 irc_msgprefix.string,
263 irc_cmd_notice(irc_session_global, dest, message);
265 if(!ISCHANNEL(dest)) Con_Printf("%s%sNotice to ^2%s^7: ^9%s\n",
267 irc_msgprefix.string,
270 ); else Con_Printf("%s%s^3%s^0|^9-^2%s^9- %s\n",
272 irc_msgprefix.string,
274 irc_current_nick.string,
281 irc_cmd_me(irc_session_global, dest, message);
283 if(ISCHANNEL(dest)) Con_Printf("%s%s^3%s^0| ^2* %s ^7%s\n",
284 watched? "\001" : CHATWINDOW,
285 irc_msgprefix.string,
287 irc_current_nick.string,
289 ); else Con_Printf("%s%s^1Privmsg to ^2%s^7: ^2* %s ^3%s\n",
291 irc_msgprefix.string,
293 irc_current_nick.string,
300 static void CL_Irc_Raw_f(void)
304 Con_Printf("Usage: irc_raw command\n");
308 irc_send_raw(irc_session_global, "%s", Cmd_Args());
311 static void CL_Irc_Say_f(void)
314 irc_msgmode = MSGMODE_PRIVMSG;
315 CL_Irc_Say_Universal_f();
318 static void CL_Irc_Notice_f(void)
321 irc_msgmode = MSGMODE_NOTICE;
322 CL_Irc_Say_Universal_f();
325 static void CL_Irc_Me_f(void)
328 irc_msgmode = MSGMODE_ACTION;
329 CL_Irc_Say_Universal_f();
332 static void CL_Irc_Join_f(void)
334 int argc = Cmd_Argc();
341 Con_Printf("Usage: irc_join channel [key]\n");
345 channel = Cmd_Argv(1);
348 irc_cmd_join(irc_session_global, channel, Cmd_Argv(2));
350 irc_cmd_join(irc_session_global, channel, NULL);
353 static void CL_Irc_Part_f(void)
359 Con_Printf("Usage: irc_part channel\n");
363 irc_cmd_part(irc_session_global, Cmd_Argv(1));
366 static void CL_Irc_Names_f(void)
372 Con_Printf("Usage: irc_names channel\n");
376 irc_cmd_names(irc_session_global, Cmd_Argv(1));
379 void CL_Irc_MessageMode_f(void)
383 key_dest = key_message;
388 if(irc_save_target.integer)
390 tmp = Irc_GetLastChannel();
391 strlcpy(chat_buffer, tmp, sizeof(chat_buffer));
392 chat_bufferlen = strlen(chat_buffer);
396 void CL_Irc_ChNick_f(void)
402 Con_Printf("Usage: irc_chnick nick\n");
406 irc_cmd_nick(irc_session_global, Cmd_Argv(1));
410 void Irc_SetLastChannel(const char *value)
412 strlcpy(last_channel, value, sizeof(last_channel));
415 const char* Irc_GetLastChannel(void)
421 void Irc_SendMessage(const char *message)
423 const char* dest = Irc_GetLastChannel();
424 qboolean watched = Irc_IsWatchedChannel(dest);
428 irc_cmd_msg(irc_session_global, dest, message);
430 if(ISCHANNEL(dest)) Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n",
431 watched? "\001" : CHATWINDOW,
432 irc_msgprefix.string,
434 irc_current_nick.string,
436 ); else Con_Printf("%s%s^1Privmsg to ^2%s^7: ^3%s\n",
438 irc_msgprefix.string,
448 IRCEVENT(event_connect)
450 Cvar_SetQuick(&irc_connected, "2");
451 Con_Printf("%sConnected to %s:%i\n",
452 irc_msgprefix.string,
458 IRCNUMEVENT(event_numeric)
460 //Get our initial nick from the welcome message
462 Cvar_SetQuick(&irc_current_nick, params[0]);
464 if (!irc_numeric_errorsonly.integer || event > 400)
466 Con_Printf("%s^3%d ^2%s ^1%s^7 %s %s %s\n",
467 irc_msgprefix.string,
469 origin ? origin : "^8(unknown)",
471 count > 1 ? params[1] : "",
472 count > 2 ? params[2] : "",
473 count > 3 ? params[3] : ""
478 IRCEVENT(event_privmsg)
483 msgstr = irc_color_strip_from_mirc(params[1]);
487 Con_Printf("%s%s^1Privmsg from ^2%s^7: ^3%s\n",
489 irc_msgprefix.string,
494 if(count > 1) free(msgstr);
498 IRCEVENT(event_channel)
504 if(!ISCHANNEL(params[0]))
505 return event_privmsg(session, event, origin, params, count);
508 msgstr = irc_color_strip_from_mirc(params[1]);
510 watched = Irc_IsWatchedChannel(params[0]);
512 if(Irc_CheckHilight(msgstr))
514 UPDATETARGET(params[0])
516 Con_Printf("%s%s^3%s^0|^7<^2%s^7> ^1%s\n",
517 watched? "\001" : CHATWINDOW_URGENT,
518 irc_msgprefix.string,
524 else Con_Printf("%s%s^3%s^0|^7<^2%s^7> %s\n",
525 watched? "\001" : CHATWINDOW,
526 irc_msgprefix.string,
532 if(count > 1) free(msgstr);
537 //printf("%sorigin -> %s (%s, %s)\n", origin, params[0], irc_current_nick.string, irc_nick.string);
540 //Cvar_SetQuick(&dumb, NULL);
542 if(!strncmp(origin, irc_current_nick.string, 512)) //Our nick is changed
544 Cvar_SetQuick(&irc_current_nick, params[0]);
546 Con_Printf("%s%s^7Your nick is now ^2%s\n",
548 irc_msgprefix.string,
555 Con_Printf("%s%s^2%s^7 is now known as ^2%s\n",
557 irc_msgprefix.string,
565 Con_Printf("%s%s^2%s^7 has quit IRC: ^2%s\n",
567 irc_msgprefix.string,
569 count? params[0] : "^8(quit message missing)"
575 Con_Printf("%s%s^2%s^7 has joined ^3%s\n",
577 irc_msgprefix.string,
585 Con_Printf("%s%s^2%s^7 has left ^3%s^7: %s\n",
587 irc_msgprefix.string,
590 count > 1? params[1] : "^8(part message missing)"
596 char paramstring[MAX_INPUTLINE] = "";
599 for(i = 2; i < count; i++)
601 strlcat(paramstring, params[i], sizeof(paramstring));
602 strlcat(paramstring, " ", sizeof(paramstring));
605 Con_Printf("%s%s^2%s^7 set mode on ^3%s^7: %s %s\n",
607 irc_msgprefix.string,
615 IRCEVENT(event_umode)
617 Con_Printf("%sUsermode changed for ^2%s^7: %s\n",
618 irc_msgprefix.string,
624 IRCEVENT(event_topic)
626 if(count > 1) Con_Printf("%s%s^2%s^7 changed the topic of ^3%s^7: %s\n",
628 irc_msgprefix.string,
637 Con_Printf("%s%s^2%s^7 has kicked ^2%s^7 out of ^3%s^7: %s\n",
639 irc_msgprefix.string,
641 count > 1? params[1] : "^8(nick missing)", //libircclient documentation says params[1] is optinal.
643 count > 2? params[2] : "^8(random kick victim! yay!)"
647 IRCEVENT(event_notice)
652 msgstr = irc_color_strip_from_mirc(params[1]);
654 if(!ISCHANNEL(params[0])) Con_Printf("%s%sNotice from ^2%s^7: ^9%s\n",
656 irc_msgprefix.string,
659 ); else Con_Printf("%s%s^3%s^0|^9-^2%s^9- %s\n",
661 irc_msgprefix.string,
667 if(count > 1) free(msgstr);
670 IRCEVENT(event_invite)
672 Con_Printf("%s%s^2%s^7 invites you to ^3%s\n",
674 irc_msgprefix.string,
680 IRCEVENT(event_ctcp_action)
685 if(!ISCHANNEL(params[0]))
687 event_ctcp_action_priv(session, event, origin, params, count);
692 msgstr = irc_color_strip_from_mirc(params[1]);
694 watched = Irc_IsWatchedChannel(params[0]);
696 if(Irc_CheckHilight(msgstr))
698 UPDATETARGET(params[0])
700 Con_Printf("%s%s^3%s^0| ^1* %s ^7%s\n",
701 watched? "\001" : CHATWINDOW_URGENT,
702 irc_msgprefix.string,
708 else Con_Printf("%s%s^3%s^0| ^2* %s ^7%s\n",
709 watched? "\001" : CHATWINDOW,
710 irc_msgprefix.string,
716 if(count > 1) free(msgstr);
719 //called by event_ctcp_action
720 IRCEVENT(event_ctcp_action_priv)
725 msgstr = irc_color_strip_from_mirc(params[1]);
729 Con_Printf("%s%s^1Privmsg^7: ^2* %s ^3%s\n",
731 irc_msgprefix.string,
736 if(count > 1) free(msgstr);
740 // Function that checks if a message contains hilights
743 qboolean Irc_CheckHilight(const char *msg)
748 if(strcasestr(msg, irc_current_nick.string))
749 return TRUE; //Contains our nick
751 if(NOTSET(irc_hilights))
754 len = strlen(irc_hilights.string);
757 for(idx = 0; idx < len; ++idx)
759 if(irc_hilights.string[idx] == ' ')
761 strlcpy(buffer, irc_hilights.string+start, idx+1);
762 if(strcasestr(msg, buffer))
763 return TRUE; //Contains a word from hilight list
769 //Catch the final word
770 strlcpy(buffer, irc_hilights.string+start, idx+1);
771 if(strcasestr(msg, buffer))
772 return TRUE; //Contains a word from hilight list
778 // Checks if channel is watched
781 qboolean Irc_IsWatchedChannel(const char* chan)
786 if(NOTSET(irc_watched_channels))
789 len = strlen(irc_watched_channels.string);
792 for(idx = 0; idx < len; ++idx)
794 if(irc_watched_channels.string[idx] == ' ')
796 strlcpy(buffer, irc_watched_channels.string+start, idx+1);
797 if(strcasestr(chan, buffer))
804 //Catch the final channel
805 strlcpy(buffer, irc_watched_channels.string+start, idx+1);
806 if(strcasestr(chan, buffer))
813 // Functions that should have been in libircclient
816 int irc_cmd_nick(irc_session_t * session, const char *nick)
820 //session->lasterror = LIBIRC_ERR_STATE; Won't compile
824 return irc_send_raw(session, "NICK %s", nick);