<commit_before>#include "discord.h"
#include "../logger.h"
#include "../util.h"
#include "../config.h"
#include <sleepy_discord/sleepy_discord.h>

namespace shanghai {
namespace system {

namespace {

struct DiscordConfig {
	std::string DefaultReply = "";
};

}	// namespace

class Discord::MyClient : public SleepyDiscord::DiscordClient {
public:
	// コンストラクタ
	MyClient(const DiscordConfig &conf,
		const std::string &token, char numOfThreads)
		: SleepyDiscord::DiscordClient(token, numOfThreads),
		m_conf(conf)
	{}
	virtual ~MyClient() = default;

private:
	DiscordConfig m_conf;

protected:
	void onReady(SleepyDiscord::Ready ready) override
	{
		logger.Log(LogLevel::Info, "[Discord] Ready");
		{
			const SleepyDiscord::User &user = ready.user;
			const std::string &id = user.ID;
			logger.Log(LogLevel::Info, "[Discord] user %s %s bot:%s",
				id.c_str(), user.username.c_str(),
				user.bot ? "Yes" : "No");
		}
	}

	void onMessage(SleepyDiscord::Message message) override
	{
		// ミラーマッチ対策として bot には反応しないようにする
		if (message.author.bot) {
			return;
		}

		logger.Log(LogLevel::Info, "[Discord] Message");
		logger.Log(LogLevel::Info, "[Discord] %s", message.content.c_str());
		// メンション時のみでフィルタ
		if (message.isMentioned(getID())) {
			// 半角スペースで区切ってメンションを削除
			// 例: <@!1<PASSWORD>>
			std::vector<std::string> tokens = util::Split(
				message.content, ' ', true);
			auto result = std::remove_if(tokens.begin(), tokens.end(),
				[](const std::string &s) {
					return s.find("<") == 0 && s.rfind(">") == s.size() - 1;
				});
			tokens.erase(result, tokens.end());

			sendMessage(message.channelID, m_conf.DefaultReply);
		}
	}

	void onError(SleepyDiscord::ErrorCode errorCode,
		const std::string errorMessage) override
	{
		logger.Log(LogLevel::Error, "[Discord] %s", errorMessage.c_str());<commit_msg>/server コマンド<commit_after>#include "discord.h"
#include "../logger.h"
#include "../util.h"
#include "../config.h"
#include <sleepy_discord/sleepy_discord.h>

namespace shanghai {
namespace system {

namespace {

const std::string HELP_TEXT =
R"(/help
    Show this help
/server
    Show server list
)";

struct DiscordConfig {
	std::string DefaultReply = "";
};

}	// namespace

class Discord::MyClient : public SleepyDiscord::DiscordClient {
public:
	// コンストラクタ
	MyClient(const DiscordConfig &conf,
		const std::string &token, char numOfThreads)
		: SleepyDiscord::DiscordClient(token, numOfThreads),
		m_conf(conf)
	{}
	virtual ~MyClient() = default;

private:
	DiscordConfig m_conf;

	bool ExecuteCommand(SleepyDiscord::Snowflake<SleepyDiscord::Channel> ch,
		std::vector<std::string> args)
	{
		if (args.size() == 0) {
			return false;
		}
		if (args.at(0) == "/help") {
			sendMessage(ch, HELP_TEXT);
			return true;
		}
		else if (args.at(0) == "/server") {
			std::vector<SleepyDiscord::Server> resp = getServers();
			std::string msg = util::Format("{0} Server(s)",
				{std::to_string(resp.size())});
			for (const auto &server : resp) {
				msg += '\n';
				msg += server.ID;
				msg += ' ';
				msg += server.name;
			}
			sendMessage(ch, msg);
			return true;
		}
		return false;
	}

protected:
	void onReady(SleepyDiscord::Ready ready) override
	{
		logger.Log(LogLevel::Info, "[Discord] Ready");
		{
			const SleepyDiscord::User &user = ready.user;
			const std::string &id = user.ID;
			logger.Log(LogLevel::Info, "[Discord] user %s %s bot:%s",
				id.c_str(), user.username.c_str(),
				user.bot ? "Yes" : "No");
		}
	}

	void onMessage(SleepyDiscord::Message message) override
	{
		// ミラーマッチ対策として bot には反応しないようにする
		if (message.author.bot) {
			return;
		}

		logger.Log(LogLevel::Info, "[Discord] Message");
		logger.Log(LogLevel::Info, "[Discord] %s", message.content.c_str());
		// メンション時のみでフィルタ
		if (message.isMentioned(getID())) {
			// 半角スペースで区切ってメンションを削除
			// 例: <@!123456789>
			std::vector<std::string> tokens = util::Split(
				message.content, ' ', true);
			auto result = std::remove_if(tokens.begin(), tokens.end(),
				[](const std::string &s) {
					return s.find("<") == 0 && s.rfind(">") == s.size() - 1;
				});
			tokens.erase(result, tokens.end());

			// コマンドとして実行
			// できなかったらデフォルト返信
			if (!ExecuteCommand(message.channelID, tokens)) {
				sendMessage(message.channelID, m_conf.DefaultReply);
			}
		}
	}

	void onError(SleepyDiscord::ErrorCode errorCode,
		const std::string errorMessage) override
	{
		logger.Log(LogLevel::Error, "[Discord] %s", errorMessage.c_str());
{'fraction_non_alphanumeric': 0.17351083032490974, 'fraction_numerical': 0.004061371841155234, 'mean_word_length': 4.08955223880597, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '1187127', 'n_tokens_mistral': 1892, 'n_tokens_neox': 1701, 'n_words': 315}