2011-01-11 03:33:13 +00:00
|
|
|
/**
|
|
|
|
* ZNC Notifo Module
|
|
|
|
*
|
|
|
|
* Allows the user to enter a Notifo user and API token, and sends
|
|
|
|
* channel highlights and personal messages to Notifo.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011 John Reese
|
|
|
|
* Licensed under the MIT license
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define REQUIRESSL
|
|
|
|
|
|
|
|
#include "znc.h"
|
|
|
|
#include "Chan.h"
|
|
|
|
#include "User.h"
|
|
|
|
#include "Modules.h"
|
|
|
|
|
|
|
|
#if (!defined(VERSION_MAJOR) || !defined(VERSION_MINOR) || (VERSION_MAJOR == 0 && VERSION_MINOR < 72))
|
|
|
|
#error This module needs ZNC 0.072 or newer.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class CNotifoMod : public CModule
|
|
|
|
{
|
2011-01-13 21:26:31 +00:00
|
|
|
protected:
|
|
|
|
|
2011-01-13 22:09:17 +00:00
|
|
|
// Application name
|
|
|
|
CString app;
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
// Too lazy to add CString("\r\n\") everywhere
|
|
|
|
CString crlf;
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
// BASIC auth string, needs to be encoded each time username/secret is changed
|
|
|
|
CString notifo_auth;
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
// Host and URL to send messages to
|
|
|
|
CString notifo_host;
|
|
|
|
CString notifo_url;
|
|
|
|
|
|
|
|
// User agent to use
|
|
|
|
CString user_agent;
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
// User object
|
|
|
|
CUser *user;
|
2011-01-13 22:16:43 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
// Configuration options
|
|
|
|
MCString options;
|
2011-01-14 03:52:50 +00:00
|
|
|
MCString defaults;
|
2011-01-14 00:50:29 +00:00
|
|
|
|
2011-01-11 03:33:13 +00:00
|
|
|
public:
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
MODCONSTRUCTOR(CNotifoMod) {
|
2011-01-13 22:09:17 +00:00
|
|
|
app = "ZNC";
|
2011-01-13 21:26:31 +00:00
|
|
|
crlf = "\r\n";
|
|
|
|
|
2011-01-14 04:01:27 +00:00
|
|
|
notifo_auth = "";
|
2011-01-13 21:26:31 +00:00
|
|
|
notifo_host = "api.notifo.com";
|
|
|
|
notifo_url = "/v1/send_notification";
|
|
|
|
user_agent = "ZNC To Notifo";
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
// Current user
|
|
|
|
user = GetUser();
|
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
// Notifo user account and secret
|
2011-01-14 03:52:50 +00:00
|
|
|
defaults["username"] = "";
|
|
|
|
defaults["secret"] = "";
|
2011-01-14 03:00:24 +00:00
|
|
|
|
|
|
|
// Notification conditions
|
2011-01-14 14:45:47 +00:00
|
|
|
defaults["away_only"] = "no";
|
2011-01-14 03:52:50 +00:00
|
|
|
defaults["client_count_less_than"] = "0";
|
2011-01-14 03:27:26 +00:00
|
|
|
|
|
|
|
// Notification settings
|
2011-01-14 03:52:50 +00:00
|
|
|
defaults["message_length"] = "100";
|
|
|
|
defaults["message_uri"] = "";
|
2011-01-11 03:33:13 +00:00
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
virtual ~CNotifoMod() {}
|
2011-01-11 03:33:13 +00:00
|
|
|
|
|
|
|
protected:
|
2011-01-13 21:26:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Shorthand for encoding a string for a URL.
|
|
|
|
*
|
|
|
|
* @param str String to be encoded
|
|
|
|
* @return Encoded string
|
|
|
|
*/
|
2011-01-11 03:33:13 +00:00
|
|
|
CString urlencode(const CString& str)
|
|
|
|
{
|
|
|
|
return str.Escape_n(CString::EASCII, CString::EURL);
|
|
|
|
}
|
|
|
|
|
2011-01-13 22:16:43 +00:00
|
|
|
/**
|
|
|
|
* Re-encode the authentication credentials.
|
|
|
|
*/
|
|
|
|
void authencode()
|
|
|
|
{
|
|
|
|
// BASIC auth, base64-encoded username:password
|
2011-01-14 00:50:29 +00:00
|
|
|
CString auth = options["username"] + CString(":") + options["secret"];
|
2011-01-13 22:16:43 +00:00
|
|
|
notifo_auth = auth.Base64Encode_n();
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
/**
|
|
|
|
* Send a message to the currently-configured Notifo account.
|
|
|
|
* Requires (and assumes) that the user has already configured their
|
|
|
|
* username and API secret using the 'set' command.
|
|
|
|
*
|
|
|
|
* @param message Message to be sent to the user
|
2011-01-13 22:09:17 +00:00
|
|
|
* @param title Message title to use
|
2011-01-13 21:26:31 +00:00
|
|
|
*/
|
2011-01-13 22:09:17 +00:00
|
|
|
void send_message(const CString& message, const CString& title="New Message")
|
2011-01-11 03:33:13 +00:00
|
|
|
{
|
2011-01-14 03:46:02 +00:00
|
|
|
// Shorten message if needed
|
|
|
|
unsigned int message_length = options["message_length"].ToUInt();
|
|
|
|
CString short_message = message;
|
|
|
|
if (message_length > 0)
|
|
|
|
{
|
|
|
|
short_message = message.Ellipsize(message_length);
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
// POST body parameters for the request
|
2011-01-14 00:50:29 +00:00
|
|
|
CString post = "to=" + urlencode(options["username"]);
|
2011-01-14 03:46:02 +00:00
|
|
|
post += "&msg=" + urlencode(short_message);
|
2011-01-13 22:09:17 +00:00
|
|
|
post += "&label=" + urlencode(app);
|
|
|
|
post += "&title=" + urlencode(title);
|
2011-01-14 03:27:26 +00:00
|
|
|
post += "&uri=" + urlencode(options["message_uri"]);
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
// Request headers and POST body
|
|
|
|
CString request = "POST " + notifo_url + " HTTP/1.1" + crlf;
|
|
|
|
request += "Host: " + notifo_host + crlf;
|
|
|
|
request += "Content-Type: application/x-www-form-urlencoded" + crlf;
|
|
|
|
request += "Content-Length: " + CString(post.length()) + crlf;
|
|
|
|
request += "User-Agent: " + user_agent + crlf;
|
2011-01-13 22:16:43 +00:00
|
|
|
request += "Authorization: Basic " + notifo_auth + crlf;
|
2011-01-13 21:26:31 +00:00
|
|
|
request += crlf;
|
|
|
|
request += post + crlf;
|
2011-01-11 03:33:13 +00:00
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
// Create the socket connection, write to it, and add it to the queue
|
2011-01-11 03:33:13 +00:00
|
|
|
CSocket *sock = new CSocket(this);
|
2011-01-13 21:26:31 +00:00
|
|
|
sock->Connect(notifo_host, 443, true);
|
|
|
|
sock->Write(request);
|
2011-01-11 03:33:13 +00:00
|
|
|
sock->Close(Csock::CLT_AFTERWRITE);
|
|
|
|
AddSocket(sock);
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
|
2011-01-14 14:45:47 +00:00
|
|
|
protected:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the away status condition is met.
|
|
|
|
*
|
|
|
|
* @return True if away_only is not "yes" or away status is set
|
|
|
|
*/
|
|
|
|
bool away_only()
|
|
|
|
{
|
|
|
|
CString value = options["away_only"].AsLower();
|
|
|
|
return value != "yes" || user->IsIRCAway();
|
|
|
|
}
|
|
|
|
|
2011-01-14 03:00:24 +00:00
|
|
|
/**
|
|
|
|
* Check how many clients are connected to ZNC.
|
|
|
|
*
|
|
|
|
* @return Number of connected clients
|
|
|
|
*/
|
|
|
|
unsigned int client_count()
|
|
|
|
{
|
|
|
|
return user->GetClients().size();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the client_count condition is met.
|
|
|
|
*
|
|
|
|
* @return True if client_count is less than client_count_less_than or if client_count_less_than is zero
|
|
|
|
*/
|
|
|
|
bool client_count_less_than()
|
|
|
|
{
|
|
|
|
unsigned int value = options["client_count_less_than"].ToUInt();
|
|
|
|
return value == 0 || client_count() < value;
|
|
|
|
}
|
|
|
|
|
2011-01-14 01:58:13 +00:00
|
|
|
/**
|
|
|
|
* Determine if the given message matches any highlight rules.
|
|
|
|
*
|
|
|
|
* @param message Message contents
|
|
|
|
* @return True if message matches a highlight
|
|
|
|
*/
|
|
|
|
bool highlight(const CString& message)
|
|
|
|
{
|
|
|
|
CNick nick = user->GetIRCNick();
|
|
|
|
|
|
|
|
if (message.find(nick.GetNick()) != string::npos)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* Determine when to notify the user of a channel message.
|
|
|
|
*
|
2011-01-14 01:39:08 +00:00
|
|
|
* @param nick Nick that sent the message
|
2011-01-14 00:50:29 +00:00
|
|
|
* @param channel Channel the message was sent to
|
2011-01-14 01:39:08 +00:00
|
|
|
* @param message Message contents
|
2011-01-14 00:50:29 +00:00
|
|
|
* @return Notification should be sent
|
|
|
|
*/
|
2011-01-14 01:39:08 +00:00
|
|
|
bool notify_channel(const CNick& nick, const CChan& channel, const CString& message)
|
2011-01-14 00:50:29 +00:00
|
|
|
{
|
2011-01-14 14:48:56 +00:00
|
|
|
return away_only()
|
|
|
|
&& client_count_less_than()
|
|
|
|
&& highlight(message);
|
2011-01-14 00:50:29 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
/**
|
2011-01-14 00:15:17 +00:00
|
|
|
* Determine when to notify the user of a private message.
|
2011-01-13 22:40:41 +00:00
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
2011-01-14 00:15:17 +00:00
|
|
|
* @return Notification should be sent
|
2011-01-13 22:40:41 +00:00
|
|
|
*/
|
2011-01-14 00:15:17 +00:00
|
|
|
bool notify_pm(const CNick& nick)
|
2011-01-13 22:40:41 +00:00
|
|
|
{
|
2011-01-14 14:48:56 +00:00
|
|
|
return away_only();
|
2011-01-13 22:40:41 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
protected:
|
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
/**
|
|
|
|
* Handle the plugin being loaded. Retrieve plugin config values.
|
|
|
|
*
|
|
|
|
* @param args Plugin arguments
|
|
|
|
* @param message Message to show the user after loading
|
|
|
|
*/
|
2011-01-13 21:26:31 +00:00
|
|
|
bool OnLoad(const CString& args, CString& message)
|
|
|
|
{
|
2011-01-14 03:52:50 +00:00
|
|
|
for (MCString::iterator i = defaults.begin(); i != defaults.end(); i++)
|
2011-01-14 00:50:29 +00:00
|
|
|
{
|
2011-01-14 02:59:44 +00:00
|
|
|
CString value = GetNV(i->first);
|
|
|
|
if (value != "")
|
|
|
|
{
|
|
|
|
options[i->first] = value;
|
|
|
|
}
|
2011-01-14 03:52:50 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
options[i->first] = defaults[i->first];
|
|
|
|
}
|
2011-01-14 00:50:29 +00:00
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-13 22:16:43 +00:00
|
|
|
authencode();
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-01-14 01:39:08 +00:00
|
|
|
/**
|
|
|
|
* Handle channel messages.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
|
|
|
* @param channel Channel the message was sent to
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnChanMsg(CNick& nick, CChan& channel, CString& message)
|
|
|
|
{
|
|
|
|
if (notify_channel(nick, channel, message))
|
|
|
|
{
|
|
|
|
CString title = "Highlight";
|
|
|
|
CString msg = channel.GetName();
|
|
|
|
msg += ": <" + nick.GetNick();
|
|
|
|
msg += "> " + message;
|
|
|
|
|
|
|
|
send_message(msg, title);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle channel actions.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the action
|
|
|
|
* @param channel Channel the message was sent to
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnChanAction(CNick& nick, CChan& channel, CString& message)
|
|
|
|
{
|
|
|
|
if (notify_channel(nick, channel, message))
|
|
|
|
{
|
|
|
|
CString title = "Highlight";
|
|
|
|
CString msg = channel.GetName();
|
|
|
|
msg += ": " + nick.GetNick();
|
|
|
|
msg += " " + message;
|
|
|
|
|
|
|
|
send_message(msg, title);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
/**
|
2011-01-14 00:15:17 +00:00
|
|
|
* Handle a private message.
|
2011-01-13 22:40:41 +00:00
|
|
|
*
|
|
|
|
* @param nick Nick that sent the message
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnPrivMsg(CNick& nick, CString& message)
|
|
|
|
{
|
2011-01-14 00:15:17 +00:00
|
|
|
if (notify_pm(nick))
|
|
|
|
{
|
|
|
|
CString title = "Private Message";
|
|
|
|
CString msg = "From " + nick.GetNick();
|
|
|
|
msg += ": " + message;
|
|
|
|
|
|
|
|
send_message(msg, title);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a private action.
|
|
|
|
*
|
|
|
|
* @param nick Nick that sent the action
|
|
|
|
* @param message Message contents
|
|
|
|
*/
|
|
|
|
EModRet OnPrivAction(CNick& nick, CString& message)
|
|
|
|
{
|
|
|
|
if (notify_pm(nick))
|
|
|
|
{
|
|
|
|
CString title = "Private Message";
|
|
|
|
CString msg = "* " + nick.GetNick();
|
|
|
|
msg += " " + message;
|
|
|
|
|
|
|
|
send_message(msg, title);
|
|
|
|
}
|
|
|
|
|
2011-01-13 22:40:41 +00:00
|
|
|
return CONTINUE;
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
/**
|
|
|
|
* Handle direct commands to the *notifo virtual user.
|
|
|
|
*
|
|
|
|
* @param command Command string
|
|
|
|
*/
|
|
|
|
void OnModCommand(const CString& command)
|
|
|
|
{
|
|
|
|
VCString tokens;
|
|
|
|
int token_count = command.Split(" ", tokens, false);
|
|
|
|
|
2011-01-14 03:00:08 +00:00
|
|
|
if (token_count < 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-01-13 21:26:31 +00:00
|
|
|
CString action = tokens[0].AsLower();
|
|
|
|
|
|
|
|
// SET command
|
|
|
|
if (action == "set")
|
|
|
|
{
|
|
|
|
if (token_count != 3)
|
|
|
|
{
|
|
|
|
PutModule("Usage: set <option> <value>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
|
|
|
CString value = tokens[2];
|
2011-01-14 00:50:29 +00:00
|
|
|
MCString::iterator pos = options.find(option);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
if (pos == options.end())
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
PutModule("Error: invalid option name");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
options[option] = value;
|
|
|
|
SetNV(option, value);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
authencode();
|
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
2011-01-14 03:52:50 +00:00
|
|
|
// UNSET command
|
|
|
|
else if (action == "unset")
|
|
|
|
{
|
|
|
|
if (token_count != 2)
|
|
|
|
{
|
|
|
|
PutModule("Usage: unset <option>");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
|
|
|
MCString::iterator pos = options.find(option);
|
|
|
|
|
|
|
|
if (pos == options.end())
|
|
|
|
{
|
|
|
|
PutModule("Error: invalid option name");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
options[option] = defaults[option];
|
|
|
|
DelNV(option);
|
|
|
|
|
|
|
|
authencode();
|
|
|
|
}
|
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
// GET command
|
|
|
|
else if (action == "get")
|
|
|
|
{
|
2011-01-14 03:03:29 +00:00
|
|
|
if (token_count > 2)
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 03:03:29 +00:00
|
|
|
PutModule("Usage: get [<option>]");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token_count < 2)
|
|
|
|
{
|
|
|
|
CTable table;
|
|
|
|
|
|
|
|
table.AddColumn("Option");
|
|
|
|
table.AddColumn("Value");
|
|
|
|
|
|
|
|
for (MCString::iterator i = options.begin(); i != options.end(); i++)
|
|
|
|
{
|
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Option", i->first);
|
|
|
|
table.SetCell("Value", i->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
PutModule(table);
|
2011-01-13 21:26:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CString option = tokens[1].AsLower();
|
2011-01-14 00:50:29 +00:00
|
|
|
MCString::iterator pos = options.find(option);
|
2011-01-13 21:26:31 +00:00
|
|
|
|
2011-01-14 00:50:29 +00:00
|
|
|
if (pos == options.end())
|
2011-01-13 21:26:31 +00:00
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
PutModule("Error: invalid option name");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-01-14 00:50:29 +00:00
|
|
|
PutModule(option + CString(": \"") + options[option] + CString("\""));
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
|
|
|
}
|
2011-01-14 03:03:49 +00:00
|
|
|
// STATUS command
|
|
|
|
else if (action == "status")
|
|
|
|
{
|
|
|
|
CTable table;
|
|
|
|
|
|
|
|
table.AddColumn("Condition");
|
|
|
|
table.AddColumn("Status");
|
|
|
|
|
2011-01-14 14:45:47 +00:00
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "away");
|
|
|
|
table.SetCell("Status", user->IsIRCAway() ? "yes" : "no");
|
|
|
|
|
2011-01-14 03:03:49 +00:00
|
|
|
table.AddRow();
|
|
|
|
table.SetCell("Condition", "client_count");
|
|
|
|
table.SetCell("Status", CString(client_count()));
|
|
|
|
|
|
|
|
PutModule(table);
|
|
|
|
}
|
2011-01-14 03:51:53 +00:00
|
|
|
// SEND command
|
|
|
|
else if (action == "send")
|
|
|
|
{
|
|
|
|
CString message = command.Token(1, true, " ", true);
|
|
|
|
send_message(message);
|
|
|
|
}
|
2011-01-14 03:58:49 +00:00
|
|
|
// HELP command
|
|
|
|
else if (action == "help")
|
|
|
|
{
|
|
|
|
PutModule("View the detailed documentation at https://github.com/jreese/znc-notifo/blob/master/README.md");
|
|
|
|
}
|
2011-01-13 21:26:31 +00:00
|
|
|
else
|
|
|
|
{
|
2011-01-14 03:58:49 +00:00
|
|
|
PutModule("Error: invalid command, try `help`");
|
2011-01-13 21:26:31 +00:00
|
|
|
}
|
2011-01-11 03:33:13 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULEDEFS(CNotifoMod, "Send highlights and personal messages to a Notifo account")
|