<commit_before>/*
 * Copyright (C) 2004-2011  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.
 */

#include "Chan.h"
#include "Modules.h"

class CChanAttach : public CModule {
	void HandleAdd(const CString& sLine) {
		CString sChan = sLine.Token(1);

		if (AlreadyAdded(sChan)) {
			PutModule(sChan + " is already added");
		} else if (Add(sChan)) {
			PutModule("Added " + sChan + " to list");
		} else {
			PutModule("Usage: Add [!]<#chan>");
		}
	}

	void HandleDel(const CString& sLine) {
		CString sChan = sLine.Token(1);

		if (Del(sChan)) {
			PutModule("Removed " + sChan + " from list");
		} else {
			PutModule("Usage: Del [!]<#chan>");
		}
	}

	void HandleList(const CString& sLine) {
		CTable Table;
		Table.AddColumn("Chan");

		for (unsigned int a = 0; a < m_vsChans.size(); a++) {
			Table.AddRow();
			Table.SetCell("Chan", m_vsChans[a]);
		}

		for (unsigned int b = 0; b < m_vsNegChans.size(); b++) {
			Table.AddRow();
			Table.SetCell("Chan", "!" + m_vsNegChans[b]);
		}

		if (Table.size()) {
			PutModule(Table);
		} else {
			PutModule("You have no entries.");
		}
	}

public:
	MODCONSTRUCTOR(CChanAttach) {
		AddHelpCommand();
		AddCommand("Add",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleAdd),
			"[!]<#chan>", "Add an entry, use !#chan to negate and * for wildcards");
		AddCommand("Del",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleDel),
			"[!]<#chan>", "Remove an entry, needs to be an exact match");
		AddCommand("List",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleList),
			"",           "List all entries");
	}

	virtual ~CChanAttach() {
	}

	virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
		VCString vsChans;
		sArgs.Split(" ", vsChans, false);

		for (VCString::const_iterator it = vsChans.begin(); it != vsChans.end(); ++it) {
			if (!Add(*it)) {
				PutModule("Unable to add [" + *it + "]");
			}
		}

		// Load our saved settings, ignore errors
		MCString::iterator it;
		for (it = BeginNV(); it != EndNV(); ++it) {
			Add(it->first);
		}

		return true;
	}

	void TryAttach(CChan& Channel) {
		const CString& sChan = Channel.GetName();

		if (Channel.IsDetached() && IsAutoAttach(sChan)) {
			Channel.JoinUser();
		}
	}

	virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Channel);
		return CONTINUE;
	}

	virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Channel);
		return CONTINUE;
	}

	virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Channel);
		return CONTINUE;
	}

	bool AlreadyAdded(const CString& sInput) {
		vector<CString>::iterator it;

		if (sInput.Left(1) == "!") {
			CString sChan = sInput.substr(1);
			for (it = m_vsNegChans.begin(); it != m_vsNegChans.end();
					++it) {
				if (*it == sChan)
					return true;
			}
		} else {
			for (it = m_vsChans.begin(); it != m_vsChans.end(); ++it) {
				if (*it == sInput)
					return true;
			}
		}
		return false;
	}

	bool Add(const CString& sChan) {
		if (sChan.empty() || sChan == "!") {
			return false;
		}

		if (sChan.Left(1) == "!") {
			m_vsNegChans.push_back(sChan.substr(1));
		} else {
			m_vsChans.push_back(sChan);
		}

		// Also save it for next module load
		SetNV(sChan, "");

		return true;
	}

	bool Del(const CString& sChan) {
		vector<CString>::iterator it, end;

		if (sChan.empty() || sChan == "!")
			return false;

		if (sChan.Left(1) == "!") {
			CString sTmp = sChan.substr(1);
			it = m_vsNegChans.begin();
			end = m_vsNegChans.end();

			for (; it != end; ++it)
				if (*it == sTmp)
					break;

			if (it == end)
				return false;

			m_vsNegChans.erase(it);
		} else {
			it = m_vsChans.begin();
			end = m_vsChans.end();

			for (; it != end; ++it)
				if (*it == sChan)
					break;

			if (it == end)
				return false;

			m_vsChans.erase(it);
		}

		DelNV(sChan);

		return true;
	}

	bool IsAutoAttach(const CString& sChan) {
		for (unsigned int a = 0; a < m_vsNegChans.size(); a++) {
			if (sChan.WildCmp(m_vsNegChans[a])) {
				return false;
			}
		}

		for (unsigned int b = 0; b < m_vsChans.size(); b++) {
			if (sChan.WildCmp(m_vsChans[b])) {
				return true;
			}
		}

		return false;
	}
private:
	vector<CString> m_vsChans;
	vector<CString> m_vsNegChans;
};

MODULEDEFS(CChanAttach, "Reattaches you to channels on activity.")<commit_msg>autoattach: Allow limiting by hostmask<commit_after>/*
 * Copyright (C) 2004-2011  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.
 */

#include "Chan.h"
#include "Modules.h"

class CAttachMatch {
public:
	CAttachMatch(const CString& sChannels, const CString& sHostmasks, bool bNegated)
	{
		m_sChannelWildcard = sChannels;
		m_sHostmaskWildcard = sHostmasks;
		m_bNegated = bNegated;

		if (m_sChannelWildcard.empty())
			m_sChannelWildcard = "*";
		if (m_sHostmaskWildcard.empty())
			m_sHostmaskWildcard = "*!*@*";
	}

	bool IsMatch(const CString& sChan, const CString& sHost) const {
		if (!sHost.WildCmp(m_sHostmaskWildcard))
			return false;
		if (!sChan.WildCmp(m_sChannelWildcard))
			return false;
		return true;
	}

	bool IsNegated() const {
		return m_bNegated;
	}

	const CString& GetHostMask() const {
		return m_sHostmaskWildcard;
	}

	const CString& GetChans() const {
		return m_sChannelWildcard;
	}

	CString ToString() {
		CString sRes;
		if (m_bNegated)
			sRes += "!";
		sRes += m_sChannelWildcard;
		sRes += " ";
		sRes += m_sHostmaskWildcard;
		return sRes;
	}

private:
	bool m_bNegated;
	CString m_sChannelWildcard;
	CString m_sHostmaskWildcard;
};

class CChanAttach : public CModule {
public:
	typedef vector<CAttachMatch> VAttachMatch;
	typedef VAttachMatch::iterator VAttachIter;

private:
	void HandleAdd(const CString& sLine) {
		CString sMsg = sLine.Token(1, true);
		bool bHelp = false;
		bool bNegated = sMsg.TrimPrefix("!");
		CString sChan = sMsg.Token(0);
		CString sHost = sMsg.Token(1, true);

		if (sChan.empty()) {
			bHelp = true;
		} else if (Add(bNegated, sChan, sHost)) {
			PutModule("Added to list");
		} else {
			PutModule(sLine.Token(1, true) + " is already added");
			bHelp = true;
		}
		if (bHelp) {
			PutModule("Usage: Add [!]<#chan> <host>");
			PutModule("Wildcards are allowed");
		}
	}

	void HandleDel(const CString& sLine) {
		CString sMsg  = sLine.Token(1, true);
		bool bNegated = sMsg.TrimPrefix("!");
		CString sChan = sMsg.Token(0);
		CString sHost = sMsg.Token(1, true);

		if (Del(bNegated, sChan, sHost)) {
			PutModule("Removed " + sChan + " from list");
		} else {
			PutModule("Usage: Del [!]<#chan> <host>");
		}
	}

	void HandleList(const CString& sLine) {
		CTable Table;
		Table.AddColumn("Neg");
		Table.AddColumn("Chan");
		Table.AddColumn("Host");

		VAttachIter it = m_vMatches.begin();
		for (; it != m_vMatches.end(); ++it) {
			Table.AddRow();
			Table.SetCell("Neg", it->IsNegated() ? "!" : "");
			Table.SetCell("Chan", it->GetChans());
			Table.SetCell("Host", it->GetHostMask());
		}

		if (Table.size()) {
			PutModule(Table);
		} else {
			PutModule("You have no entries.");
		}
	}

public:
	MODCONSTRUCTOR(CChanAttach) {
		AddHelpCommand();
		AddCommand("Add",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleAdd),
			"[!]<#chan> <host>", "Add an entry, use !#chan to negate and * for wildcards");
		AddCommand("Del",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleDel),
			"[!]<#chan> <host>", "Remove an entry, needs to be an exact match");
		AddCommand("List",    static_cast<CModCommand::ModCmdFunc>(&CChanAttach::HandleList),
			"",           "List all entries");
	}

	virtual ~CChanAttach() {
	}

	virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
		VCString vsChans;
		sArgs.Split(" ", vsChans, false);

		for (VCString::const_iterator it = vsChans.begin(); it != vsChans.end(); ++it) {
			CString sAdd = *it;
			bool bNegated = sAdd.TrimPrefix("!");
			CString sChan = sAdd.Token(0);
			CString sHost = sAdd.Token(1, true);

			if (!Add(bNegated, sChan, sHost)) {
				PutModule("Unable to add [" + *it + "]");
			}
		}

		// Load our saved settings, ignore errors
		MCString::iterator it;
		for (it = BeginNV(); it != EndNV(); ++it) {
			CString sAdd = it->first;
			bool bNegated = sAdd.TrimPrefix("!");
			CString sChan = sAdd.Token(0);
			CString sHost = sAdd.Token(1, true);

			Add(bNegated, sChan, sHost);
		}

		return true;
	}

	void TryAttach(const CNick& Nick, CChan& Channel) {
		const CString& sChan = Channel.GetName();
		const CString& sHost = Nick.GetHostMask();
		VAttachIter it;

		if (!Channel.IsDetached())
			return;

		// Any negated match?
		for (it = m_vMatches.begin(); it != m_vMatches.end(); ++it) {
			if (it->IsNegated() && it->IsMatch(sChan, sHost))
				return;
		}

		// Now check for a positive match
		for (it = m_vMatches.begin(); it != m_vMatches.end(); ++it) {
			if (!it->IsNegated() && it->IsMatch(sChan, sHost)) {
				Channel.JoinUser();
				return;
			}
		}
	}

	virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Nick, Channel);
		return CONTINUE;
	}

	virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Nick, Channel);
		return CONTINUE;
	}

	virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
		TryAttach(Nick, Channel);
		return CONTINUE;
	}

	VAttachIter FindEntry(const CString& sChan, const CString& sHost) {
		VAttachIter it = m_vMatches.begin();
		for (; it != m_vMatches.end(); ++it) {
			if (sHost.empty() || it->GetHostMask() != sHost)
				continue;
			if (sChan.empty() || it->GetChans() != sChan)
				continue;
			return it;
		}
		return m_vMatches.end();
	}

	bool Add(bool bNegated, const CString& sChan, const CString& sHost) {
		CAttachMatch attach(sChan, sHost, bNegated);

		// Check for duplicates
		VAttachIter it = m_vMatches.begin();
		for (; it != m_vMatches.end(); ++it) {
			if (it->GetHostMask() == attach.GetHostMask()
					&& it->GetChans() == attach.GetChans())
				return false;
		}

		m_vMatches.push_back(attach);

		// Also save it for next module load
		SetNV(attach.ToString(), "");

		return true;
	}

	bool Del(bool bNegated, const CString& sChan, const CString& sHost) {
		VAttachIter it = FindEntry(sChan, sHost);
		if (it == m_vMatches.end() || it->IsNegated() != bNegated)
			return false;

		DelNV(it->ToString());
		m_vMatches.erase(it);

		return true;
	}
private:
	VAttachMatch m_vMatches;
};

MODULEDEFS(CChanAttach, "Reattaches you to channels on activity.")
{'fraction_non_alphanumeric': 0.15826006478482185, 'fraction_numerical': 0.003794539565016196, 'mean_word_length': 3.7003044802087866, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3911998', 'n_tokens_mistral': 4668, 'n_tokens_neox': 4045, 'n_words': 955}