Changeset - 384d4bfcd0c1
[Not reviewed]
0 4 0
Jan Kaluza - 10 years ago 2016-01-27 15:01:39
jkaluza@redhat.com
Create main slack channel after registering to Slack
4 files changed with 47 insertions and 9 deletions:
0 comments (0 inline, 0 general)
spectrum/src/frontends/slack/SlackFrontend.cpp
Show inline comments
 
@@ -78,60 +78,60 @@ void SlackFrontend::sendRosterRequest(Swift::RosterPayload::ref payload, Swift::
 
}
 

	
 
void SlackFrontend::sendMessage(boost::shared_ptr<Swift::Message> message) {
 
	return static_cast<SlackUserManager *>(m_userManager)->sendMessage(message);
 
}
 

	
 
void SlackFrontend::sendIQ(boost::shared_ptr<Swift::IQ> iq) {
 
}
 

	
 
boost::shared_ptr<Swift::DiscoInfo> SlackFrontend::sendCapabilitiesRequest(Swift::JID to) {
 

	
 
	return Swift::DiscoInfo::ref();
 
}
 

	
 
void SlackFrontend::reconnectUser(const std::string &user) {
 
	return static_cast<SlackUserManager *>(m_userManager)->reconnectUser(user);
 
}
 

	
 
RosterManager *SlackFrontend::createRosterManager(User *user, Component *component) {
 
	return new SlackRosterManager(user, component);
 
}
 

	
 
User *SlackFrontend::createUser(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) {
 
	SlackUser *user = new SlackUser(jid, userInfo, component, userManager);
 
	user->setReconnectLimit(-1);
 
	return user;
 
}
 

	
 
UserManager *SlackFrontend::createUserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) {
 
	m_userManager = new SlackUserManager(component, userRegistry, storageBackend);
 
	return m_userManager;
 
}
 

	
 

	
 
void SlackFrontend::connectToServer() {
 
	LOG4CXX_INFO(logger, "Started.");
 
	m_transport->handleConnected();
 
}
 

	
 
std::string SlackFrontend::setOAuth2Code(const std::string &code, const std::string &state) {
 
	return static_cast<SlackUserManager *>(m_userManager)->handleOAuth2Code(code, state);
 
}
 

	
 
std::string SlackFrontend::getOAuth2URL(const std::vector<std::string> &args) {
 
	return static_cast<SlackUserManager *>(m_userManager)->getOAuth2URL(args);
 
}
 

	
 
std::string SlackFrontend::getRegistrationFields() {
 
	return "Slack team name\n3rd-party network username\n3rd-party network password";
 
	return "Main Slack channel\n3rd-party network username\n3rd-party network password";
 
}
 

	
 
bool SlackFrontend::handleAdminMessage(Swift::Message::ref message) {
 
	return static_cast<SlackUserManager *>(m_userManager)->handleAdminMessage(message);
 
}
 

	
 
void SlackFrontend::disconnectFromServer() {
 

	
 
}
 

	
 
}
spectrum/src/frontends/slack/SlackSession.cpp
Show inline comments
 
@@ -170,96 +170,130 @@ void SlackSession::joinRoom(std::vector<std::string> args) {
 
// 	else {
 
// 		to =  legacyRoom + "\\40" + legacyServer + "@" + m_component->getJID().toString();
 
// 	}
 

	
 
	m_jid2channel[to] = slackChannel;
 
	m_channel2jid[slackChannel] = to;
 

	
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID("", m_uinfo.jid, "default"));
 
	presence->setTo(Swift::JID(to + "/" + name));
 
	presence->setType(Swift::Presence::Available);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
	m_component->getFrontend()->onPresenceReceived(presence);
 

	
 
	m_onlineBuddiesTimer->start();
 
}
 

	
 
void SlackSession::handleJoinRoomCreate(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data, std::vector<std::string> args) {
 
	std::string channelId = m_api->getChannelId(req, ok, resp, data);
 
	if (channelId.empty()) {
 
		LOG4CXX_INFO(logger, args[1] << ": Error creating channel " << args[5] << ".");
 
		return;
 
	}
 

	
 
	args[5] = channelId;
 
	joinRoom(args);
 
}
 

	
 
void SlackSession::handleJoinRoomList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data, std::vector<std::string> args) {
 
	std::map<std::string, SlackChannelInfo> channels;
 
	SlackAPI::getSlackChannelInfo(req, ok, resp, data, channels);
 

	
 
	if (channels.find(args[5]) != channels.end()) {
 
		LOG4CXX_INFO(logger, args[1] << ": Channel " << args[5] << " already exists. Joining the room.");
 
		args[5] = channels[args[5]].id;
 
		joinRoom(args);
 
	}
 
	else {
 
		LOG4CXX_INFO(logger, args[1] << ": Channel " << args[5] << " does not exit. Creating it.");
 
		m_api->channelsCreate(args[5], boost::bind(&SlackSession::handleJoinRoomCreate, this, _1, _2, _3, _4, args));
 
	}
 
}
 

	
 
void SlackSession::handleJoinMessage(const std::string &message, std::vector<std::string> &args, bool quiet) {
 
	LOG4CXX_INFO(logger, args[1] << ": Going to join the room, checking the ID of channel " << args[5]);
 
	m_api->channelsList(boost::bind(&SlackSession::handleJoinRoomList, this, _1, _2, _3, _4, args));
 
}
 

	
 
void SlackSession::handleSlackChannelCreate(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) {
 
	std::string channelId = m_api->getChannelId(req, ok, resp, data);
 
	if (channelId.empty()) {
 
		LOG4CXX_INFO(logger,"Error creating channel " << m_slackChannel << ".");
 
		return;
 
	}
 

	
 
	m_slackChannel = channelId;
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID("", m_uinfo.jid, "default"));
 
	presence->setTo(m_component->getJID());
 
	presence->setType(Swift::Presence::Available);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
	m_component->getFrontend()->onPresenceReceived(presence);
 
}
 

	
 
void SlackSession::handleSlackChannelList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) {
 
	std::map<std::string, SlackChannelInfo> channels;
 
	SlackAPI::getSlackChannelInfo(req, ok, resp, data, channels);
 

	
 
	if (channels.find(m_slackChannel) != channels.end()) {
 
		m_slackChannel = channels[m_slackChannel].id;
 
		Swift::Presence::ref presence = Swift::Presence::create();
 
		presence->setFrom(Swift::JID("", m_uinfo.jid, "default"));
 
		presence->setTo(m_component->getJID());
 
		presence->setType(Swift::Presence::Available);
 
		presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
		m_component->getFrontend()->onPresenceReceived(presence);
 
	}
 
	else {
 
		m_api->channelsCreate(m_slackChannel, boost::bind(&SlackSession::handleSlackChannelCreate, this, _1, _2, _3, _4));
 
	}
 
}
 

	
 
void SlackSession::handleLeaveMessage(const std::string &message, std::vector<std::string> &args, bool quiet) {
 
	// .spectrum2 leave.room channel
 
	std::string slackChannel = SlackAPI::SlackObjectToPlainText(args[2], true);
 
	std::string to = m_channel2jid[slackChannel];
 
	if (to.empty()) {
 
		m_rtm->sendMessage(m_ownerChannel, "Spectrum 2 is not configured to transport this Slack channel.");
 
		return;
 
	}
 

	
 
	std::string rooms = "";
 
	int type = (int) TYPE_STRING;
 
	m_storageBackend->getUserSetting(m_uinfo.id, "rooms", type, rooms);
 

	
 
	std::vector<std::string> commands;
 
	boost::split(commands, rooms, boost::is_any_of("\n"));
 
	rooms = "";
 

	
 
	BOOST_FOREACH(const std::string &command, commands) {
 
		if (command.size() > 5) {
 
			std::vector<std::string> args2;
 
			boost::split(args2, command, boost::is_any_of(" "));
 
			if (args2.size() == 6) {
 
				if (slackChannel != SlackAPI::SlackObjectToPlainText(args2[5], true)) {
 
					rooms += command + "\n";
 
				}
 
			}
 
		}
 
	}
 

	
 
	m_storageBackend->updateUserSetting(m_uinfo.id, "rooms", rooms);
 

	
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID("", m_uinfo.jid, "default"));
 
	presence->setTo(Swift::JID(to + "/" + m_uinfo.uin));
 
	presence->setType(Swift::Presence::Unavailable);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
	m_component->getFrontend()->onPresenceReceived(presence);
 
}
 

	
 
void SlackSession::handleRegisterMessage(const std::string &message, std::vector<std::string> &args, bool quiet) {
 
	// .spectrum2 register test@xmpp.tld mypassword #slack_channel
 
	m_uinfo.uin = SlackAPI::SlackObjectToPlainText(args[2]);
 
	m_uinfo.password = args[3];
 
	std::string slackChannel = SlackAPI::SlackObjectToPlainText(args[4], true);
 

	
 
	m_storageBackend->setUser(m_uinfo);
 
	int type = (int) TYPE_STRING;
 
	m_storageBackend->getUserSetting(m_uinfo.id, "slack_channel", type, slackChannel);
 
@@ -407,97 +441,92 @@ void SlackSession::setUser(User *user) {
 
}
 

	
 

	
 
void SlackSession::handleConnected() {
 
	if (m_disconnected) {
 
		m_rtm->getAPI()->imOpen(m_ownerId, boost::bind(&SlackSession::handleImOpen, this, _1, _2, _3, _4));
 
		m_disconnected = false;
 
	}
 
}
 

	
 
void SlackSession::handleImOpen(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) {
 
	m_ownerChannel = m_rtm->getAPI()->getChannelId(req, ok, resp, data);
 
	LOG4CXX_INFO(logger, "Opened channel with team owner: " << m_ownerChannel);
 

	
 
	std::string rooms = "";
 
	int type = (int) TYPE_STRING;
 
	m_storageBackend->getUserSetting(m_uinfo.id, "rooms", type, rooms);
 

	
 
	if (!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)) {
 
		if (rooms.empty()) {
 
			std::string msg;
 
			msg =  "Hi, it seems you have enabled Spectrum 2 transport for your Team. As a Team owner, you should now configure it:\\n";
 
			msg += "1. At first, create new channel in which you want this Spectrum 2 transport to send the messages, or choose the existing one.\\n";
 
			msg += "2. Invite this Spectrum 2 bot into this channel.\\n";
 
			msg += "3. Configure the transportation between 3rd-party network and this channel by executing following command in this chat:\\n";
 
			msg += "```.spectrum2 join.room NameOfYourBotIn3rdPartyNetwork #3rdPartyRoom hostname_of_3rd_party_server #SlackChannel```\\n";
 
			msg += "For example to join #test123 channel on Freenode IRC server as MyBot and transport it into #slack_channel, the command would look like this:\\n";
 
			msg += "```.spectrum2 join.room MyBot #test123 adams.freenode.net #slack_channel```\\n";
 
			msg += "To get full list of available commands, executa `.spectrum2 help`\\n";
 
			m_rtm->sendMessage(m_ownerChannel, msg);
 
		}
 
	}
 
	else {
 
		if (m_uinfo.uin.empty()) {
 
			std::string msg;
 
			msg =  "Hi, it seems you have enabled Spectrum 2 transport for your Team. As a Team owner, you should now configure it:\\n";
 
			msg += "1. At first, create new channel in which you want this Spectrum 2 transport to send the messages, or choose the existing one.\\n";
 
			msg += "2. Invite this Spectrum 2 bot into this channel.\\n";
 
			msg += "3. Configure the transportation between 3rd-party network and this channel by executing following command in this chat:\\n";
 
			msg += "```.spectrum2 register 3rdPartyAccount 3rdPartyPassword #SlackChannel```\\n";
 
			msg += "For example to join XMPP account test@xmpp.tld and  transport it into #slack_channel, the command would look like this:\\n";
 
			msg += "```.spectrum2 register test@xmpp.tld mypassword #slack_channel```\\n";
 
			msg += "To get full list of available commands, executa `.spectrum2 help`\\n";
 
			m_rtm->sendMessage(m_ownerChannel, msg);
 
		}
 
		else {
 
			m_storageBackend->getUserSetting(m_uinfo.id, "slack_channel", type, m_slackChannel);
 
			if (!m_slackChannel.empty()) {
 
				Swift::Presence::ref presence = Swift::Presence::create();
 
				presence->setFrom(Swift::JID("", m_uinfo.jid, "default"));
 
				presence->setTo(m_component->getJID());
 
				presence->setType(Swift::Presence::Available);
 
				presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
				m_component->getFrontend()->onPresenceReceived(presence);
 
				m_api->channelsList(boost::bind(&SlackSession::handleSlackChannelList, this, _1, _2, _3, _4));
 
			}
 
			else {
 
				std::string msg;
 
				msg =  "Hi, it seems you have enabled Spectrum 2 transport for your Team. As a Team owner, you should now configure it:\\n";
 
				msg += "1. At first, create new channel in which you want this Spectrum 2 transport to send the messages, or choose the existing one.\\n";
 
				msg += "2. Invite this Spectrum 2 bot into this channel.\\n";
 
				msg += "3. Configure the transportation between 3rd-party network and this channel by executing following command in this chat:\\n";
 
				msg += "```.spectrum2 set_main_channel #SlackChannel```\\n";
 
				msg += "To get full list of available commands, executa `.spectrum2 help`\\n";
 
				m_rtm->sendMessage(m_ownerChannel, msg);
 
			}
 
		}
 
	}
 

	
 
	// Auto-join the rooms configured by the Slack channel owner.
 
	if (!rooms.empty()) {
 
		std::vector<std::string> commands;
 
		boost::split(commands, rooms, boost::is_any_of("\n"));
 

	
 
		BOOST_FOREACH(const std::string &command, commands) {
 
			if (command.size() > 5) {
 
				LOG4CXX_INFO(logger, m_uinfo.jid << ": Sending command from storage: " << command);
 
				handleMessageReceived(m_ownerChannel, "owner", command, "", true);
 
			}
 
		}
 
	}
 
}
 

	
 
void SlackSession::handleRTMStarted() {
 
	std::map<std::string, SlackUserInfo> &users = m_rtm->getUsers();
 
	for (std::map<std::string, SlackUserInfo>::iterator it = users.begin(); it != users.end(); it++) {
 
		SlackUserInfo &info = it->second;
 
		if (info.isPrimaryOwner) {
 
			m_ownerId = it->first;
 
			break;
 
		}
 
	}
 

	
 
	m_rtm->getAPI()->imOpen(m_ownerId, boost::bind(&SlackSession::handleImOpen, this, _1, _2, _3, _4));
 
}
 

	
 

	
 
}
spectrum/src/frontends/slack/SlackSession.h
Show inline comments
 
@@ -28,69 +28,72 @@
 
#include <map>
 

	
 
#include "Swiften/Elements/Message.h"
 
#include "Swiften/Network/Timer.h"
 

	
 
#include <boost/signal.hpp>
 

	
 
namespace Transport {
 

	
 
class Component;
 
class StorageBackend;
 
class HTTPRequest;
 
class SlackRTM;
 
class SlackAPI;
 
class User;
 

	
 
class SlackSession {
 
	public:
 
		SlackSession(Component *component, StorageBackend *storageBackend, UserInfo uinfo);
 

	
 
		virtual ~SlackSession();
 

	
 
		boost::signal<void (const std::string &user)> onInstallationDone;
 

	
 
		void sendMessage(boost::shared_ptr<Swift::Message> message);
 

	
 
		void sendMessageToAll(const std::string &msg);
 

	
 
		void setPurpose(const std::string &purpose, const std::string &channel = "");
 

	
 
		void setUser(User *user);
 

	
 
		void handleDisconnected();
 
		void handleConnected();
 

	
 
		void handleJoinMessage(const std::string &message, std::vector<std::string> &args, bool quiet = false);
 
		void handleLeaveMessage(const std::string &message, std::vector<std::string> &args, bool quiet = false);
 
		void handleRegisterMessage(const std::string &message, std::vector<std::string> &args, bool quiet = false);
 

	
 
	private:
 
		void handleRTMStarted();
 
		void handleMessageReceived(const std::string &channel, const std::string &user, const std::string &message, const std::string &ts, bool quiet);
 
		void handleImOpen(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data);
 

	
 
		void joinRoom(std::vector<std::string> args);
 
		void handleJoinRoomCreate(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data, std::vector<std::string> args);
 
		void handleJoinRoomList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data, std::vector<std::string> args);
 

	
 
		void handleSlackChannelCreate(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data);
 
		void handleSlackChannelList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data);
 

	
 
		void sendOnlineBuddies();
 

	
 
	private:
 
		Component *m_component;
 
		StorageBackend *m_storageBackend;
 
		UserInfo m_uinfo;
 
		std::string m_ownerName;
 
		SlackRTM *m_rtm;
 
		std::string m_ownerChannel;
 
		std::map<std::string, std::string> m_jid2channel;
 
		std::map<std::string, std::string> m_channel2jid;
 
		std::string m_slackChannel;
 
		User *m_user;
 
		Swift::Timer::ref m_onlineBuddiesTimer;
 
		std::map<std::string, std::string> m_onlineBuddies;
 
		bool m_disconnected;
 
		std::string m_ownerId;
 
		SlackAPI *m_api;
 
};
 

	
 
}
spectrum/src/frontends/slack/SlackUserRegistration.cpp
Show inline comments
 
@@ -67,125 +67,131 @@ std::string SlackUserRegistration::createOAuth2URL(const std::vector<std::string
 
						  CONFIG_STRING_DEFAULTED(m_config, "service.client_secret",""),
 
						  "https://slack.com/oauth/authorize",
 
						  "https://slack.com/api/oauth.access",
 
						  redirect_url,
 
						  "channels:read channels:write team:read im:read im:write chat:write:bot bot");
 
	std::string url = oauth2->generateAuthURL();
 

	
 
	m_auths[oauth2->getState()] = oauth2;
 
	m_authsData[oauth2->getState()] = args;
 

	
 
	return url;
 
}
 

	
 
std::string SlackUserRegistration::getTeamDomain(const std::string &token) {
 
	std::string url = "https://slack.com/api/team.info?token=" + Util::urlencode(token);
 

	
 
	rapidjson::Document resp;
 
	HTTPRequest req(HTTPRequest::Get, url);
 
	if (!req.execute(resp)) {
 
		LOG4CXX_ERROR(logger, url);
 
		LOG4CXX_ERROR(logger, req.getError());
 
		return "";
 
	}
 

	
 
	rapidjson::Value &team = resp["team"];
 
	if (!team.IsObject()) {
 
		LOG4CXX_ERROR(logger, "No 'team' object in the reply.");
 
		LOG4CXX_ERROR(logger, url);
 
		LOG4CXX_ERROR(logger, req.getRawData());
 
		return "";
 
	}
 

	
 
	rapidjson::Value &domain = team["domain"];
 
	if (!domain.IsString()) {
 
		LOG4CXX_ERROR(logger, "No 'domain' string in the reply.");
 
		LOG4CXX_ERROR(logger, url);
 
		LOG4CXX_ERROR(logger, req.getRawData());
 
		return "";
 
	}
 

	
 
	return domain.GetString();
 
}
 

	
 
std::string SlackUserRegistration::handleOAuth2Code(const std::string &code, const std::string &state) {
 
	OAuth2 *oauth2 = NULL;
 
	std::string token;
 
	std::string access_token;
 
	std::vector<std::string> data;
 
	std::string value;
 
	int type = (int) TYPE_STRING;
 

	
 
	if (state == "use_bot_token") {
 
		token = code;
 
		access_token = code;
 
	}
 
	else {
 
		if (m_auths.find(state) != m_auths.end()) {
 
			oauth2 = m_auths[state];
 
			data = m_authsData[state];
 
		}
 
		else {
 
			return "Received state code '" + state + "' not found in state codes list.";
 
		}
 

	
 
		std::string error = oauth2->requestToken(code, access_token, token);
 
		if (!error.empty())  {
 
			return error;
 
		}
 

	
 
		if (token.empty()) {
 
			LOG4CXX_INFO(logger, "Using 'token' as 'bot_access_token'");
 
			token = access_token;
 
		}
 
	}
 

	
 
	std::string domain = getTeamDomain(access_token);
 
	if (domain.empty()) {
 
		return "The token you have provided is invalid";
 
	}
 

	
 
	UserInfo user;
 
	user.uin = "";
 
	user.password = "";
 
	user.id = 0;
 
	m_storageBackend->getUser(domain, user);
 

	
 
	value = user.jid;
 
	user.jid = domain;
 
	user.language = "en";
 
	user.encoding = "";
 
	user.vip = 0;
 

	
 
	registerUser(user, true);
 

	
 
	m_storageBackend->getUser(user.jid, user);
 

	
 
	std::string value = token;
 
	int type = (int) TYPE_STRING;
 
	if (!value.empty()) {
 
		m_storageBackend->getUserSetting(user.id, "slack_channel", type, value);
 
	}
 

	
 
	value = token;
 
	m_storageBackend->getUserSetting(user.id, "bot_token", type, value);
 

	
 
	value = access_token;
 
	m_storageBackend->getUserSetting(user.id, "access_token", type, value);
 

	
 
	LOG4CXX_INFO(logger, "Registered Slack user " << user.jid);
 

	
 
	if (oauth2) {
 
		m_auths.erase(state);
 
		delete oauth2;
 

	
 
		m_authsData.erase(state);
 
	}
 

	
 
	m_component->getFrontend()->reconnectUser(user.jid);
 

	
 
	return "Registered as " + domain;
 
}
 

	
 
bool SlackUserRegistration::doUserRegistration(const UserInfo &row) {
 
	return true;
 
}
 

	
 
bool SlackUserRegistration::doUserUnregistration(const UserInfo &row) {
 
	return true;
 
}
 

	
 

	
 

	
 
}
0 comments (0 inline, 0 general)