/* * Copyright (C) 2004-2009 See the AUTHORS file for details. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * Based on the various "away" modules included with ZNC stock. * Heavily hacked to send email/text/twitter alerts by * Jon Masters * * Twitter feature requires Greg K-H's "bti" utility: * http://www.kernel.org/pub/linux/kernel/people/gregkh/bti/ * * Version: 1.2 * * Changelog: * - 2009/06/25 - Shorten awayping reply. * - 2009/06/17 - Add support for antiping. * - 2009/06/09 - Add support for automatic idle detection. */ #include "Chan.h" #include "User.h" #include #include #include #include using std::list; class CAwayPingTimerEmail : public CTimer { public: CAwayPingTimerEmail(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {} virtual ~CAwayPingTimerEmail() {} virtual int Email(const CString& sEmailAddress, const CString& sEmailSubject, const CString& sEmailBody); protected: virtual void RunJob(); }; class CAwayPingTimerSms : public CAwayPingTimerEmail { public: CAwayPingTimerSms(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CAwayPingTimerEmail(pModule, uInterval, uCycles, sLabel, sDescription) {} virtual ~CAwayPingTimerSms() {} protected: virtual void RunJob(); }; class CAwayPingTimerTwitter : public CTimer { public: CAwayPingTimerTwitter(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {} virtual ~CAwayPingTimerTwitter() {} virtual int Tweet(const CString& sAccount, const CString& sPassword, const CString& sTweet); protected: virtual void RunJob(); }; class CAwayPingNickEntry { public: CAwayPingNickEntry(const CString& sNick, const time_t tTimestamp, const CString& sLastMessage, bool bSentSms, bool bSentTweet) { m_sNick = sNick; m_sLastMessage = sLastMessage; m_tTimestamp = tTimestamp; m_bSentSms = bSentSms; m_bSentTweet = bSentTweet; } virtual ~CAwayPingNickEntry() {} // We use this to lookup a Nick during iteration bool operator ==(const CAwayPingNickEntry& NickEntry) { return (GetNick().Equals(NickEntry.GetNick())); }; // Getters const CString& GetNick() const { return m_sNick; } time_t GetTimestamp() const { return m_tTimestamp; } const CString& GetLastMessage() const { return m_sLastMessage; } bool GetSentSms() const { return m_bSentSms; } bool GetSentTweet() const { return m_bSentTweet; } // !Getters // Setters void SetNick(const CString& s) { m_sNick = s; } void SetTimestamp(const time_t t) { m_tTimestamp = t; } void SetLastMessage(const CString& s) { m_sLastMessage = s; } void SetSentSms(const bool b) { m_bSentSms = b; } void SetSentTweet(const bool b) { m_bSentTweet = b; } // !Setters protected: CString m_sNick; CString m_sLastMessage; bool m_bSentSms; bool m_bSentTweet; time_t m_tTimestamp; }; class CAwayPingSource { public: CAwayPingSource(const CString& sSource, bool bNegated) { m_sSource = sSource; m_bNegated = bNegated; } virtual ~CAwayPingSource() {} // Getters const CString& GetSource() const { return m_sSource; } bool IsNegated() const { return m_bNegated; } // !Getters // Setters // !Setters private: protected: bool m_bNegated; CString m_sSource; }; class CAwayPingWatchEntry { public: CAwayPingWatchEntry(const CString& sHostMask, const CString& sTarget, const CString& sPattern) { m_bDisabled = false; m_sPattern = (sPattern.size()) ? sPattern : "*"; CNick Nick; Nick.Parse(sHostMask); m_sHostMask = (Nick.GetNick().size()) ? Nick.GetNick() : "*"; m_sHostMask += "!"; m_sHostMask += (Nick.GetIdent().size()) ? Nick.GetIdent() : "*"; m_sHostMask += "@"; m_sHostMask += (Nick.GetHost().size()) ? Nick.GetHost() : "*"; if (sTarget.size()) { m_sTarget = sTarget; } else { m_sTarget = "$"; m_sTarget += Nick.GetNick(); } } virtual ~CAwayPingWatchEntry() {} bool IsMatch(const CNick& Nick, const CString& sText, const CString& sSource, const CUser* pUser) { if (IsDisabled()) { return false; } bool bGoodSource = true; if ("priv" == sSource.AsLower()) return true; if (sSource.size() && m_vsSources.size()) { bGoodSource = false; for (unsigned int a = 0; a < m_vsSources.size(); a++) { const CAwayPingSource& WatchSource = m_vsSources[a]; if (sSource.AsLower().WildCmp( WatchSource.GetSource().AsLower())) { if (WatchSource.IsNegated()) { return false; } else { bGoodSource = true; } } } } if (!bGoodSource) return false; if (!Nick.GetHostMask().AsLower().WildCmp( m_sHostMask.AsLower())) return false; if (!sText.AsLower().WildCmp( pUser->ExpandString(m_sPattern).AsLower())) return false; return true; } bool operator ==(const CAwayPingWatchEntry& WatchEntry) { return (GetHostMask().Equals(WatchEntry.GetHostMask()) && GetTarget().Equals(WatchEntry.GetTarget()) && GetPattern().Equals(WatchEntry.GetPattern()) ); } // Getters const CString& GetHostMask() const { return m_sHostMask; } const CString& GetTarget() const { return m_sTarget; } const CString& GetPattern() const { return m_sPattern; } bool IsDisabled() const { return m_bDisabled; } const vector& GetSources() const { return m_vsSources; } CString GetSourcesStr() const { CString sRet; for (unsigned int a = 0; a < m_vsSources.size(); a++) { const CAwayPingSource& WatchSource = m_vsSources[a]; if (a) { sRet += " "; } if (WatchSource.IsNegated()) { sRet += "!"; } sRet += WatchSource.GetSource(); } return sRet; } // !Getters // Setters void SetHostMask(const CString& s) { m_sHostMask = s; } void SetTarget(const CString& s) { m_sTarget = s; } void SetPattern(const CString& s) { m_sPattern = s; } void SetDisabled(bool b = true) { m_bDisabled = b; } void SetSources(const CString& sSources) { unsigned int uIdx = 1; CString sSrc = sSources.Token(0); m_vsSources.clear(); while (sSrc.size()) { if (sSrc[0] == '!') { if (sSrc.size() > 1) { m_vsSources.push_back(CAwayPingSource( sSrc.substr(1), true)); } } else { m_vsSources.push_back(CAwayPingSource(sSrc, false)); } sSrc = sSources.Token(uIdx++); } } // !Setters private: protected: CString m_sHostMask; CString m_sTarget; CString m_sPattern; bool m_bDisabled; vector m_vsSources; }; class CAwayPingMod : public CModule { public: MODCONSTRUCTOR(CAwayPingMod) { m_Buffer.SetLineCount(500); Load(); } virtual ~CAwayPingMod() {} virtual void OnClientLogin(); virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage); virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage); virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage); virtual void OnModCommand(const CString& sCommand); // Getters CBuffer& GetBuffer() { return m_Buffer; } list& GetNickList() { return m_lsNicks; } time_t GetSmsThreshold() const { return m_tSmsThreshold; } const CString& GetEmailAddress() const { return m_sEmailAddress; } const CString& GetSmsAddress() const { return m_sSmsAddress; } const CString& GetTwitterAccount() const { return m_sTwitterAccount; } const CString& GetTwitterPassword() const { return m_sTwitterPassword; } bool GetTwitterPrivacy() const { return m_bTwitterPrivacy; } bool GetAntiPing() const { return m_bAntiPing; } time_t GetEmailTimer() const { return m_tEmailTimer; } time_t GetSmsTimer() const { return m_tSmsTimer; } time_t GetLastReceivedTime() const { return m_tLastReceivedTime; } // !Getters bool m_bDebugAttach; private: void Process(const CNick& Nick, const CString& sMessage, const CString& sSource); void SetDisabled(unsigned int uIdx, bool bDisabled); void List(); void Dump(); void SetSources(unsigned int uIdx, const CString& sSources); void Remove(unsigned int uIdx); void Help(); void Watch(const CString& sHostMask, const CString& sTarget, const CString& sPattern, bool bNotice = false); void Save(); void Load(); CBuffer m_Buffer; list m_lsNicks; list m_lsWatchers; CString m_sEmailAddress; time_t m_tEmailTimer; CString m_sSmsAddress; time_t m_tSmsThreshold; time_t m_tSmsTimer; CString m_sTwitterAccount; CString m_sTwitterPassword; bool m_bTwitterPrivacy; bool m_bAntiPing; time_t m_tTwitterTimer; time_t m_tLastReceivedTime; time_t m_tIdleTimeThreshold; }; void CAwayPingMod::OnClientLogin() { for (list::iterator it = m_lsNicks.begin(); it != m_lsNicks.end(); it++) { it->SetTimestamp(0); it->SetSentSms(true); it->SetSentTweet(true); } m_Buffer.Clear(); } CModule::EModRet CAwayPingMod::OnPrivMsg(CNick& Nick, CString& sMessage) { Process(Nick, "<" + Nick.GetNick() + "> " + sMessage, "priv"); return CONTINUE; } CModule::EModRet CAwayPingMod::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { Process(Nick, "<" + Nick.GetNick() + ":" + Channel.GetName() + "> " + sMessage, Channel.GetName()); return CONTINUE; } CModule::EModRet CAwayPingMod::OnUserMsg(CString& sTarget, CString& sMessage) { m_tLastReceivedTime = time(NULL); return CONTINUE; } void CAwayPingMod::OnModCommand(const CString& sCommand) { CString sCmdName = sCommand.Token(0); if (sCmdName.Equals("ADD") || sCmdName.Equals("WATCH")) { Watch(sCommand.Token(1), sCommand.Token(2), sCommand.Token(3, true)); } else if (sCmdName.Equals("HELP")) { Help(); } else if (sCmdName.Equals("LIST")) { List(); } else if (sCmdName.Equals("DUMP")) { Dump(); } else if (sCmdName.Equals("ENABLE")) { CString sTok = sCommand.Token(1); if (sTok == "*") { SetDisabled(~0, false); } else { SetDisabled(sTok.ToUInt(), false); } } else if (sCmdName.Equals("DISABLE")) { CString sTok = sCommand.Token(1); if (sTok == "*") { SetDisabled(~0, true); } else { SetDisabled(sTok.ToUInt(), true); } } else if (sCmdName.Equals("SETSOURCES")) { SetSources(sCommand.Token(1).ToUInt(), sCommand.Token(2, true)); } else if (sCmdName.Equals("CLEAR")) { m_lsWatchers.clear(); PutModule("All entries cleared."); Save(); } else if (sCmdName.Equals("BUFFER")) { CString sCount = sCommand.Token(1); if (sCount.size()) { m_Buffer.SetLineCount(sCount.ToUInt()); } PutModule("Buffer count is set to [" + CString(m_Buffer.GetLineCount()) + "]"); } else if (sCmdName.Equals("DEL")) { Remove(sCommand.Token(1).ToUInt()); } else if (sCmdName.Equals("EMAILADDRESS")) { m_sEmailAddress = sCommand.Token(1); PutModule("Regular Email is set to " + sCommand.Token(1)); Save(); } else if (sCmdName.Equals("EMAILTIMER")) { m_tEmailTimer = sCommand.Token(1).ToInt(); PutModule("Email Timer set " + sCommand.Token(1) + " seconds"); Save(); RemTimer("AwayPingTimerEmail"); AddTimer(new CAwayPingTimerEmail(this, m_tEmailTimer, 0, "AwayPingTimerEmail", "Checks for messages to email")); } else if (sCmdName.Equals("SMSADDRESS")) { m_sSmsAddress = sCommand.Token(1); PutModule("SMS Gateway is set to " + sCommand.Token(1)); Save(); } else if (sCmdName.Equals("SMSTHRESHOLD")) { m_tSmsThreshold = sCommand.Token(1).ToInt(); PutModule("SMS Threshold set to " + sCommand.Token(1) + " seconds"); Save(); } else if (sCmdName.Equals("SMSTIMER")) { m_tSmsTimer = sCommand.Token(1).ToInt(); PutModule("SMS Timer set " + sCommand.Token(1) + " seconds"); Save(); RemTimer("AwayPingTimerSms"); AddTimer(new CAwayPingTimerSms(this, m_tSmsTimer, 0, "AwayPingTimerSms", "Checks for messages to Text")); } else if (sCmdName.Equals("TWITTERACCOUNT")) { m_sTwitterAccount = sCommand.Token(1); PutModule("Twitter Account is set to " + sCommand.Token(1)); Save(); } else if (sCmdName.Equals("TWITTERPASSWORD")) { m_sTwitterPassword = sCommand.Token(1); PutModule("Twitter Password is set to " + sCommand.Token(1)); Save(); } else if (sCmdName.Equals("TWITTERPRIVACY")) { m_bTwitterPrivacy = sCommand.Token(1).ToBool(); PutModule("Twitter Privacy is set to " + CString(m_bTwitterPrivacy)); Save(); } else if (sCmdName.Equals("TWITTERTIMER")) { m_tTwitterTimer = sCommand.Token(1).ToInt(); PutModule("Twitter Timer set " + sCommand.Token(1) + " seconds"); Save(); RemTimer("AwayPingTimerTwitter"); AddTimer(new CAwayPingTimerTwitter(this, m_tTwitterTimer, 0, "AwayPingTimerTwitter", "Checks for messages to Tweet")); } else if (sCmdName.Equals("DEBUGATTACH")) { m_bDebugAttach = sCommand.Token(1).ToBool(); PutModule("Debug attach set: " + CString(m_bDebugAttach)); Save(); } else if (sCmdName.Equals("IDLETIMETHRESHOLD")) { m_tIdleTimeThreshold = sCommand.Token(1).ToInt(); PutModule("Idle timeout set: " + CString(m_tIdleTimeThreshold)); Save(); } else if (sCmdName.Equals("ANTIPING")) { m_bAntiPing = sCommand.Token(1).ToBool(); PutModule(" AntiPing set: " + CString(m_bAntiPing)); Save(); } else { PutModule("Unknown command: [" + sCmdName + "]"); } } void CAwayPingMod::Process(const CNick& Nick, const CString& sMessage, const CString& sSource) { bool bPingOnly = false; bool bPingMsg = false; if (GetUser()->IsUserAttached() && !m_bDebugAttach && ((m_tLastReceivedTime + m_tIdleTimeThreshold) >= time(NULL))) return; /* do nothing if user attached */ /* but if they 'ping', request an actual reason */ if (GetAntiPing()) { VCString vsSplitMessage; sMessage.Split(" ", vsSplitMessage); if (((vsSplitMessage.size()) <= 4) && sMessage.AsLower().WildCmp("*ping*")) bPingOnly = true; } for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++) { CAwayPingWatchEntry& WatchEntry = *it; if (WatchEntry.IsMatch(Nick, sMessage, sSource, m_pUser)) { CAwayPingNickEntry NickEntry(Nick.GetNick(), time(NULL), sMessage, false, false); bool bExists = false; for (list::iterator it2 = m_lsNicks.begin(); it2 != m_lsNicks.end(); it2++) { if (*it2 == NickEntry) { bExists = true; it2->SetLastMessage(sMessage); if (time(NULL) > it2->GetTimestamp()+ m_tSmsThreshold) { // if ping only, reset timestamp if (bPingOnly) { it2->SetTimestamp(NULL); bPingMsg = true; } else it2->SetTimestamp(time(NULL)); it2->SetSentSms(false); } it2->SetSentTweet(false); break; } } if (!bExists) { m_lsNicks.push_back(NickEntry); if (bPingOnly) bPingMsg = true; } m_Buffer.AddLine("", " " + m_pUser->AddTimestamp(sMessage)); } } /* educate them about 'ping' */ if (bPingMsg) { PutIRC("PRIVMSG " + Nick.GetNick() + " :" + "(autoreply) user is currently busy, please leave a message"); } } void CAwayPingMod::SetDisabled(unsigned int uIdx, bool bDisabled) { if (uIdx == (unsigned int) ~0) { for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++) { (*it).SetDisabled(bDisabled); } PutModule(((bDisabled) ? "Disabled all entries." : "Enabled all entries.")); Save(); return; } uIdx--; // "convert" index to zero based if (uIdx >= m_lsWatchers.size()) { PutModule("Invalid Id"); return; } list::iterator it = m_lsWatchers.begin(); for (unsigned int a = 0; a < uIdx; a++) it++; (*it).SetDisabled(bDisabled); PutModule("Id " + CString(uIdx +1) + ((bDisabled) ? " Disabled" : " Enabled")); Save(); } void CAwayPingMod::List() { CTable Table; Table.AddColumn("Id"); Table.AddColumn("HostMask"); Table.AddColumn("Target"); Table.AddColumn("Pattern"); Table.AddColumn("Sources"); Table.AddColumn("Off"); unsigned int uIdx = 1; for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++, uIdx++) { CAwayPingWatchEntry& WatchEntry = *it; Table.AddRow(); Table.SetCell("Id", CString(uIdx)); Table.SetCell("HostMask", WatchEntry.GetHostMask()); Table.SetCell("Target", WatchEntry.GetTarget()); Table.SetCell("Pattern", WatchEntry.GetPattern()); Table.SetCell("Sources", WatchEntry.GetSourcesStr()); Table.SetCell("Off", (WatchEntry.IsDisabled()) ? "Off" : ""); } if (Table.size()) { PutModule(Table); } else { PutModule("You have no entries."); } PutModule(""); PutModule("Debug Information:"); PutModule("--- Debug Attach: " + CString(m_bDebugAttach)); PutModule(""); PutModule("Email Information:"); PutModule("--- Address: " + CString(m_sEmailAddress)); PutModule("--- Timer: " + CString(m_tEmailTimer)); PutModule("SMS Information:"); PutModule("--- Address: " + CString(m_sSmsAddress)); PutModule("--- Threshold: " + CString(m_tSmsThreshold)); PutModule("--- Timer: " + CString(m_tSmsTimer)); PutModule("Twitter Information:"); PutModule("--- Account: " + CString(m_sTwitterAccount)); PutModule("--- Password: " + CString(m_sTwitterPassword)); PutModule("--- Privacy: " + CString(m_bTwitterPrivacy)); PutModule("--- Timer: " + CString(m_tTwitterTimer)); PutModule(""); PutModule("Idle Timeout Threshold: " + CString(m_tIdleTimeThreshold)); PutModule("AntiPing Configuration: " + CString(m_bAntiPing)); } void CAwayPingMod::Dump() { if (!m_lsWatchers.size()) { PutModule("You have no entries."); return; } PutModule("---------------"); PutModule("/msg " + GetModNick() + " CLEAR"); unsigned int uIdx = 1; for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++, uIdx++) { CAwayPingWatchEntry& WatchEntry = *it; PutModule("/msg " + GetModNick() + " ADD " + WatchEntry.GetHostMask() + " " + WatchEntry.GetTarget() + " " + WatchEntry.GetPattern()); if (WatchEntry.GetSourcesStr().size()) { PutModule("/msg " + GetModNick() + " SETSOURCES " + CString(uIdx) + " " + WatchEntry.GetSourcesStr()); } if (WatchEntry.IsDisabled()) { PutModule("/msg " + GetModNick() + " DISABLE " + CString(uIdx)); } } PutModule("---------------"); } void CAwayPingMod::SetSources(unsigned int uIdx, const CString& sSources) { uIdx--; // "convert" index to zero based if (uIdx >= m_lsWatchers.size()) { PutModule("Invalid Id"); return; } list::iterator it = m_lsWatchers.begin(); for (unsigned int a = 0; a < uIdx; a++) it++; (*it).SetSources(sSources); PutModule("Sources set for Id " + CString(uIdx +1) + "."); Save(); } void CAwayPingMod::Remove(unsigned int uIdx) { uIdx--; // "convert" index to zero based if (uIdx >= m_lsWatchers.size()) { PutModule("Invalid Id"); return; } list::iterator it = m_lsWatchers.begin(); for (unsigned int a = 0; a < uIdx; a++) it++; m_lsWatchers.erase(it); PutModule("Id " + CString(uIdx +1) + " Removed."); Save(); } void CAwayPingMod::Help() { CTable Table; Table.AddColumn("Command"); Table.AddColumn("Description"); Table.AddRow(); Table.SetCell("Command", "Add [Target] [Pattern]"); Table.SetCell("Description", "Used to add an entry to watch for."); Table.AddRow(); Table.SetCell("Command", "List"); Table.SetCell("Description", "List all entries being watched."); Table.AddRow(); Table.SetCell("Command", "Dump"); Table.SetCell("Description", "Dump a list of all current entries " "to be used later."); Table.AddRow(); Table.SetCell("Command", "Del "); Table.SetCell("Description", "Deletes Id from the list of watched " "entries."); Table.AddRow(); Table.SetCell("Command", "Clear"); Table.SetCell("Description", "Delete all entries."); Table.AddRow(); Table.SetCell("Command", "Enable "); Table.SetCell("Description", "Enable a disabled entry."); Table.AddRow(); Table.SetCell("Command", "Disable "); Table.SetCell("Description", "Disable (but don't delete) an entry."); Table.AddRow(); Table.SetCell("Command", "Buffer [Count]"); Table.SetCell("Description", "Show/Set the amount of buffered lines " "while detached."); Table.AddRow(); Table.SetCell("Command", "SetSources [#chan priv #foo* !#bar]"); Table.SetCell("Description", "Set the source channels that you care " "about."); Table.AddRow(); Table.SetCell("Command", "EmailAddress"); Table.SetCell("Description", "Set the Regular Email Address"); Table.AddRow(); Table.SetCell("Command", "EmailTimer"); Table.SetCell("Description", "Set the Email timer period"); Table.AddRow(); Table.SetCell("Command", "SmsAddress"); Table.SetCell("Description", "Set SMS gateway Email Address"); Table.AddRow(); Table.SetCell("Command", "SmsThreshold"); Table.SetCell("Description", "Set the SMS activity threshold"); Table.AddRow(); Table.SetCell("Command", "SmsTimer"); Table.SetCell("Description", "Set the Sms timer period"); Table.AddRow(); Table.SetCell("Command", "TwitterAccount"); Table.SetCell("Description", "Set the Twitter Account"); Table.AddRow(); Table.SetCell("Command", "TwitterPassword"); Table.SetCell("Description", "Set the Twitter Password"); Table.AddRow(); Table.SetCell("Command", "TwitterPrivacy"); Table.SetCell("Description", "Set the Twitter Privacy"); Table.AddRow(); Table.SetCell("Command", "TwitterTimer"); Table.SetCell("Description", "Set the Twitter Timer"); Table.AddRow(); Table.SetCell("Command", "DebugAttach"); Table.SetCell("Description", "Set the debug attach"); Table.AddRow(); Table.SetCell("Command", "IdleTimeThreshold"); Table.SetCell("Description", "Set the idle timeout threshold"); Table.AddRow(); Table.SetCell("Command", "Help"); Table.SetCell("Description", "This help."); PutModule(Table); } void CAwayPingMod::Watch(const CString& sHostMask, const CString& sTarget, const CString& sPattern, bool bNotice) { CString sMessage; if (sHostMask.size()) { CAwayPingWatchEntry WatchEntry(sHostMask, sTarget, sPattern); bool bExists = false; for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++) { if (*it == WatchEntry) { sMessage = "Entry for [" + WatchEntry.GetHostMask() + "] already exists."; bExists = true; break; } } if (!bExists) { sMessage = "Adding entry: [" + WatchEntry.GetHostMask() + "] watching for " + "[" + WatchEntry.GetPattern() + "] -> [" + WatchEntry.GetTarget() + "]"; m_lsWatchers.push_back(WatchEntry); } } else { sMessage = "Watch: Not enough arguments. Try Help"; } if (bNotice) { PutModNotice(sMessage); } else { PutModule(sMessage); } Save(); } void CAwayPingMod::Save() { ClearNV(false); for (list::iterator it = m_lsWatchers.begin(); it != m_lsWatchers.end(); it++) { CAwayPingWatchEntry& WatchEntry = *it; CString sSave; sSave = WatchEntry.GetHostMask() + "\n"; sSave += WatchEntry.GetTarget() + "\n"; sSave += WatchEntry.GetPattern() + "\n"; sSave += (WatchEntry.IsDisabled() ? "disabled\n" : "enabled\n"); sSave += WatchEntry.GetSourcesStr(); // Without this, loading fails if GetSourcesStr() // returns an empty string sSave += " "; SetNV(sSave, "", false); } SetNV("debug_attach", CString(m_bDebugAttach)); SetNV("email_address", m_sEmailAddress); SetNV("email_timer", CString(m_tEmailTimer)); SetNV("sms_address", m_sSmsAddress); SetNV("sms_threshold", CString(m_tSmsThreshold)); SetNV("sms_timer", CString(m_tSmsTimer)); SetNV("twitter_account", m_sTwitterAccount); SetNV("twitter_password", m_sTwitterPassword); SetNV("twitter_privacy", CString(m_bTwitterPrivacy)); SetNV("twitter_timer", CString(m_tTwitterTimer)); SetNV("idle_time_threshold", CString(m_tIdleTimeThreshold)); SetNV("anti_ping", CString(m_bAntiPing)); SaveRegistry(); } void CAwayPingMod::Load() { // Just to make sure we dont mess up badly m_lsWatchers.clear(); m_lsNicks.clear(); m_bDebugAttach = GetNV("debug_attach").ToBool(); m_sEmailAddress = GetNV("email_address"); m_tEmailTimer = GetNV("email_timer").ToInt(); m_sSmsAddress = GetNV("sms_address"); m_tSmsThreshold = GetNV("sms_threshold").ToInt(); m_tSmsTimer = GetNV("sms_timer").ToInt(); m_sTwitterAccount = GetNV("twitter_account"); m_sTwitterPassword = GetNV("twitter_password"); m_bTwitterPrivacy = GetNV("twitter_privacy").ToBool(); m_tTwitterTimer = GetNV("twitter_timer").ToInt(); m_tIdleTimeThreshold = GetNV("idle_time_threshold").ToInt(); m_bAntiPing = GetNV("anti_ping").ToBool(); for (MCString::iterator it = BeginNV(); it != EndNV(); it++) { VCString vList; it->first.Split("\n", vList); if (vList.size() != 5) { continue; } CAwayPingWatchEntry WatchEntry(vList[0], vList[1], vList[2]); if (vList[3].Equals("disabled")) WatchEntry.SetDisabled(true); else WatchEntry.SetDisabled(false); WatchEntry.SetSources(vList[4]); m_lsWatchers.push_back(WatchEntry); } AddTimer(new CAwayPingTimerEmail(this, m_tEmailTimer, 0, "AwayPingTimerEmail", "Checks for messages to email")); AddTimer(new CAwayPingTimerSms(this, m_tSmsTimer, 0, "AwayPingTimerSms", "Checks for messages to Text")); AddTimer(new CAwayPingTimerTwitter(this, m_tTwitterTimer, 0, "AwayPingTimerTwitter", "Checks for messages to Tweet")); } int CAwayPingTimerEmail::Email(const CString& sEmailAddress, const CString& sEmailSubject, const CString& sEmailBody) { const char *email_address = sEmailAddress.c_str(); const char *email_subject = sEmailSubject.c_str(); const char *email_body = sEmailBody.c_str(); int pid = 0; int pipefd[2]; if (0 != pipe(pipefd)) return 1; pid = fork(); if (0 == pid) { const char *pArgv[] = { "mail", "-s", email_subject, email_address, NULL }; close(pipefd[1]); dup2(pipefd[0],0); execvp("mail", (char * const *) pArgv); return 1; } else { int len = 0; close(pipefd[0]); len = write(pipefd[1], email_body, strlen(email_body)); if ((ssize_t)strlen(email_body) != len) return 1; close(pipefd[1]); waitpid(pid, 0, 0); return 0; } }; void CAwayPingTimerEmail::RunJob() { CString sBufLine; CString sEmailHead; CString sEmailBody; CString sEmailTail; sEmailHead = "---begin log---\n"; sEmailTail = "---end of log---\n"; while (((class CAwayPingMod *)GetModule())->GetBuffer().GetNextLine( GetModule()->GetUser()->GetCurNick(), sBufLine)) { sEmailBody += sBufLine + "\n"; } if ("" != sEmailBody) { Email(((class CAwayPingMod *)GetModule())->GetEmailAddress(), "IRC Alert", sEmailHead + sEmailBody + sEmailTail); GetModule()->PutModule("sending alert email"); } }; void CAwayPingTimerSms::RunJob() { CString sBufLine; for (list::iterator it = ((class CAwayPingMod *)GetModule())->GetNickList().begin(); it != ((class CAwayPingMod *)GetModule())->GetNickList().end(); it++) { if (!it->GetSentSms()) { Email((( class CAwayPingMod *)GetModule())->GetSmsAddress(), "", it->GetLastMessage()); GetModule()->PutModule("sending SMS about the nick: " + it->GetNick()); it->SetSentSms(true); } } }; int CAwayPingTimerTwitter::Tweet(const CString& sAccount, const CString& sPassword, const CString& sTweet) { const char *twitter_account = sAccount.c_str(); const char *twitter_password = sPassword.c_str(); const char *tweet_content = sTweet.c_str(); int pid = 0; int pipefd[2]; if (0 != pipe(pipefd)) return 1; pid = fork(); if (0 == pid) { const char *pArgv[] = { "bti", "--host", "twitter", "--account", twitter_account, "--password", twitter_password, "--shrink-urls", NULL }; close(pipefd[1]); dup2(pipefd[0],0); execvp("bti", (char * const *) pArgv); return 1; } else { int len = 0; close(pipefd[0]); len = write(pipefd[1], tweet_content, strlen(tweet_content)); if ((ssize_t)strlen(tweet_content) != len) return 1; close(pipefd[1]); waitpid(pid, 0, 0); return 0; } }; void CAwayPingTimerTwitter::RunJob() { CString sBufLine; for (list::iterator it = ((class CAwayPingMod *)GetModule())->GetNickList().begin(); it != ((class CAwayPingMod *)GetModule())->GetNickList().end(); it++) { if (!it->GetSentTweet()) { CString tweet = "D " + ((class CAwayPingMod *)GetModule())->GetTwitterAccount() + " IRC: "; if (false == (((class CAwayPingMod *)GetModule())->GetTwitterPrivacy())) { tweet += it->GetLastMessage(); Tweet( ((class CAwayPingMod *)GetModule())->GetTwitterAccount(), ((class CAwayPingMod *)GetModule())->GetTwitterPassword(), tweet); GetModule()->PutModule("sent full tweet"); } else { tweet += "<" + it->GetNick() + ">"; Tweet( ((class CAwayPingMod *)GetModule())->GetTwitterAccount(), ((class CAwayPingMod *)GetModule())->GetTwitterPassword(), tweet); GetModule()->PutModule("sent partial tweet"); } it->SetSentTweet(true); } } }; MODULEDEFS(CAwayPingMod, "Copy activity from a specific user into a separate window")