First half-working version of IRC transport for Slack

This commit is contained in:
Jan Kaluza 2015-11-25 16:02:23 +01:00
parent 3df6d53f23
commit 05bf03704a
12 changed files with 162 additions and 35 deletions

View file

@ -84,6 +84,12 @@ std::string SlackAPI::getChannelId(HTTPRequest *req, bool ok, rapidjson::Documen
return id.GetString();
}
void SlackAPI::channelsCreate(const std::string &name, HTTPRequest::Callback callback) {
std::string url = "https://slack.com/api/channels.create?name=" + Util::urlencode(name) + "&token=" + Util::urlencode(m_token);
HTTPRequest *req = new HTTPRequest(THREAD_POOL(m_component), HTTPRequest::Get, url, callback);
queueRequest(req);
}
void SlackAPI::imOpen(const std::string &uid, HTTPRequest::Callback callback) {
std::string url = "https://slack.com/api/im.open?user=" + Util::urlencode(uid) + "&token=" + Util::urlencode(m_token);
HTTPRequest *req = new HTTPRequest(THREAD_POOL(m_component), HTTPRequest::Get, url, callback);
@ -183,7 +189,7 @@ void SlackAPI::getSlackChannelInfo(HTTPRequest *req, bool ok, rapidjson::Documen
info.members.push_back(members[i].GetString());
}
ret[info.id] = info;
ret[info.name] = info;
}
return;

View file

@ -75,6 +75,7 @@ class SlackAPI : public HTTPRequestQueue {
void usersList(HTTPRequest::Callback callback);
std::string getOwnerId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data);
void channelsCreate(const std::string &name, HTTPRequest::Callback callback);
void imOpen(const std::string &uid, HTTPRequest::Callback callback);
std::string getChannelId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data);

View file

@ -78,6 +78,7 @@ 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) {

View file

@ -26,9 +26,15 @@
#include "transport/Transport.h"
#include "transport/HTTPRequest.h"
#include "transport/Util.h"
#include "transport/Buddy.h"
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "Swiften/Elements/MUCPayload.h"
#include <map>
#include <iterator>
@ -43,15 +49,85 @@ SlackInstallation::SlackInstallation(Component *component, StorageBackend *stora
m_rtm = new SlackRTM(component, storageBackend, uinfo);
m_rtm->onRTMStarted.connect(boost::bind(&SlackInstallation::handleRTMStarted, this));
m_rtm->onMessageReceived.connect(boost::bind(&SlackInstallation::handleMessageReceived, this, _1, _2, _3));
// m_api = new SlackAPI(component, m_uinfo.encoding);
}
SlackInstallation::~SlackInstallation() {
delete m_rtm;
// delete m_api;
}
void SlackInstallation::sendMessage(boost::shared_ptr<Swift::Message> message) {
LOG4CXX_INFO(logger, "SEND MESSAGE");
if (message->getFrom().getResource() == "myfavouritebot") {
return;
}
m_rtm->getAPI()->sendMessage(message->getFrom().getResource(), m_jid2channel[message->getFrom().toBare().toString()], message->getBody());
}
void SlackInstallation::handleMessageReceived(const std::string &channel, const std::string &user, const std::string &message) {
if (m_ownerChannel == channel) {
LOG4CXX_INFO(logger, "Owner message received " << channel << " " << user << " " << message);
if (m_ownerChannel != channel) {
std::string to = m_channel2jid[channel];
if (!to.empty()) {
boost::shared_ptr<Swift::Message> msg(new Swift::Message());
msg->setType(Swift::Message::Groupchat);
msg->setTo(to);
msg->setFrom(Swift::JID("", "spectrum2", "default"));
msg->setBody("<" + user + "> " + message);
m_component->getFrontend()->onMessageReceived(msg);
}
return;
}
std::vector<std::string> args;
boost::split(args, message, boost::is_any_of(" "));
if (args.size() < 2 || args[0] != ".spectrum2") {
m_rtm->sendMessage(m_ownerChannel, "Unknown command. Use \".spectrum2 help\" for help.");
return;
}
if (args[1] == "join") {
// .spectrum2 join BotName #room irc.freenode.net channel
LOG4CXX_INFO(logger, "Received JOIN request" << args.size());
if (args.size() == 6) {
LOG4CXX_INFO(logger, "Received JOIN request");
if (args[4][0] == '<') {
args[4] = args[4].substr(1, args[4].size() - 2);
args[4] = args[4].substr(args[4].find("|") + 1);
LOG4CXX_INFO(logger, args[4]);
}
if (args[5][0] == '<') {
args[5] = args[5].substr(2, args[5].size() - 3);
}
m_uinfo.uin = args[2];
m_storageBackend->setUser(m_uinfo);
std::string to = args[3] + "%" + args[4] + "@localhost";
m_jid2channel[to] = args[5];
m_channel2jid[args[5]] = to;
LOG4CXX_INFO(logger, "Setting transport between " << to << " and " << args[5]);
// int type = (int) TYPE_STRING;
// m_storageBackend->getUserSetting(user.id, "room", type, to);
Swift::Presence::ref presence = Swift::Presence::create();
presence->setFrom(Swift::JID("", "spectrum2", "default"));
presence->setTo(Swift::JID(to + "/" + args[2]));
presence->setType(Swift::Presence::Available);
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
m_component->getFrontend()->onPresenceReceived(presence);
}
}
else if (args[1] == "help") {
}
else {
m_rtm->sendMessage(m_ownerChannel, "Unknown command. Use \".spectrum2 help\" for help.");
}
}

View file

@ -27,6 +27,8 @@
#include <algorithm>
#include <map>
#include "Swiften/Elements/Message.h"
#include <boost/signal.hpp>
namespace Transport {
@ -35,6 +37,7 @@ class Component;
class StorageBackend;
class HTTPRequest;
class SlackRTM;
class SlackAPI;
class SlackInstallation {
public:
@ -44,6 +47,8 @@ class SlackInstallation {
boost::signal<void (const std::string &user)> onInstallationDone;
void sendMessage(boost::shared_ptr<Swift::Message> message);
private:
void handleRTMStarted();
void handleMessageReceived(const std::string &channel, const std::string &user, const std::string &message);
@ -56,6 +61,9 @@ class SlackInstallation {
std::string m_ownerName;
SlackRTM *m_rtm;
std::string m_ownerChannel;
SlackAPI *m_api;
std::map<std::string, std::string> m_jid2channel;
std::map<std::string, std::string> m_channel2jid;
};
}

View file

@ -72,6 +72,10 @@ class SlackRTM {
return m_users;
}
std::map<std::string, SlackChannelInfo> &getChannels() {
return m_channels;
}
SlackAPI *getAPI() {
return m_api;
}

View file

@ -49,27 +49,32 @@ void SlackUserManager::reconnectUser(const std::string &user) {
return;
}
if (!uinfo.uin.empty()) {
LOG4CXX_INFO(logger, "Reconnecting user " << user);
Swift::Presence::ref response = Swift::Presence::create();
response->setTo(m_component->getJID());
response->setFrom(user + "@" + m_component->getJID().toString());
response->setType(Swift::Presence::Available);
}
else {
// if (!uinfo.uin.empty()) {
// LOG4CXX_INFO(logger, "Reconnecting user " << user);
// Swift::Presence::ref response = Swift::Presence::create();
// response->setTo(m_component->getJID());
// response->setFrom(user + "@" + m_component->getJID().toString());
// response->setType(Swift::Presence::Available);
// }
// else {
LOG4CXX_INFO(logger, "Cannot reconnect user " << user << ","
"because he does not have legacy network configured. "
"Continuing in Installation mode for this user until "
"he configures the legacy network.");
m_installations[user] = new SlackInstallation(m_component, m_storageBackend, uinfo);
m_installations[user]->onInstallationDone.connect(boost::bind(&SlackUserManager::reconnectUser, this, _1));
}
// }
}
void SlackUserManager::sendVCard(unsigned int id, Swift::VCard::ref vcard) {
}
void SlackUserManager::sendMessage(boost::shared_ptr<Swift::Message> message) {
LOG4CXX_INFO(logger, message->getTo().toBare().toString());
m_installations[message->getTo().toBare().toString()]->sendMessage(message);
}
UserRegistration *SlackUserManager::getUserRegistration() {
return m_userRegistration;

View file

@ -57,6 +57,8 @@ class SlackUserManager : public UserManager {
std::string getOAuth2URL(const std::vector<std::string> &args);
void sendMessage(boost::shared_ptr<Swift::Message> message);
private:
Component *m_component;
UserRegistration *m_userRegistration;

View file

@ -104,19 +104,26 @@ std::string SlackUserRegistration::getTeamDomain(const std::string &token) {
std::string SlackUserRegistration::handleOAuth2Code(const std::string &code, const std::string &state) {
OAuth2 *oauth2 = NULL;
std::string token;
std::vector<std::string> data;
if (m_auths.find(state) != m_auths.end()) {
oauth2 = m_auths[state];
data = m_authsData[state];
if (state == "use_bot_token") {
token = code;
}
else {
return "Received state code '" + state + "' not found in state codes list.";
}
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 token;
std::string error = oauth2->requestToken(code, token);
if (!error.empty()) {
return error;
std::string token;
std::string error = oauth2->requestToken(code, token);
if (!error.empty()) {
return error;
}
}
UserInfo user;
@ -131,16 +138,25 @@ std::string SlackUserRegistration::handleOAuth2Code(const std::string &code, con
m_storageBackend->getUser(user.jid, user);
std::string value = data[2];
int type = (int) TYPE_STRING;
m_storageBackend->getUserSetting(user.id, "bot_token", type, value);
if (oauth2) {
std::string value = data[2];
int type = (int) TYPE_STRING;
m_storageBackend->getUserSetting(user.id, "bot_token", type, value);
}
else {
std::string value = token;
int type = (int) TYPE_STRING;
m_storageBackend->getUserSetting(user.id, "bot_token", type, value);
}
LOG4CXX_INFO(logger, "Registered Slack user " << user.jid);
m_auths.erase(state);
delete oauth2;
if (oauth2) {
m_auths.erase(state);
delete oauth2;
m_authsData.erase(state);
m_authsData.erase(state);
}
m_component->getFrontend()->reconnectUser(user.jid);

View file

@ -27,6 +27,7 @@
#include "transport/RosterManager.h"
#include "transport/Frontend.h"
#include "transport/Config.h"
#include "transport/Logging.h"
#include "Swiften/Elements/MUCItem.h"
#include "Swiften/Elements/MUCOccupant.h"
@ -35,6 +36,8 @@
#include "Swiften/Elements/MUCPayload.h"
namespace Transport {
DEFINE_LOGGER(logger, "Conversation");
Conversation::Conversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMUC) : m_conversationManager(conversationManager) {
m_legacyName = legacyName;
@ -195,6 +198,7 @@ void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, con
}
message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n));
LOG4CXX_INFO(logger, "MSG FROM " << message->getFrom().toString());
}
handleRawMessage(message);

View file

@ -661,8 +661,10 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
}
User *user = m_userManager->getUser(payload.username());
if (!user)
if (!user) {
LOG4CXX_ERROR(logger, "handleConvMessagePayload: unknown username " << payload.username());
return;
}
// Message from legacy network triggers network acticity
user->updateLastActivity();
@ -697,6 +699,7 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
// We can't create Conversation for payload with nickname, because this means the message is from room,
// but this user is not in any room, so it's OK to just reject this message
if (!conv && !payload.nickname().empty()) {
LOG4CXX_WARN(logger, "handleConvMessagePayload: No conversation with name " << payload.buddyname());
return;
}

View file

@ -120,7 +120,7 @@ bool SQLite3Backend::connect() {
if (createDatabase() == false)
return false;
PREP_STMT(m_setUser, "INSERT OR REPLACE INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, DATETIME('NOW'), ?)");
PREP_STMT(m_setUser, "INSERT OR REPLACE INTO " + m_prefix + "users (id, jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, ?, DATETIME('NOW'), ?)");
PREP_STMT(m_getUser, "SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid=?");
PREP_STMT(m_removeUser, "DELETE FROM " + m_prefix + "users WHERE id=?");
@ -223,12 +223,13 @@ bool SQLite3Backend::exec(const std::string &query) {
void SQLite3Backend::setUser(const UserInfo &user) {
sqlite3_reset(m_setUser);
sqlite3_bind_text(m_setUser, 1, user.jid.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 2, user.uin.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 3, user.password.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 4, user.language.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 5, user.encoding.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int (m_setUser, 6, user.vip);
sqlite3_bind_int(m_setUser, 1, user.id);
sqlite3_bind_text(m_setUser, 2, user.jid.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 3, user.uin.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 4, user.password.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 5, user.language.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_text(m_setUser, 6, user.encoding.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int (m_setUser, 7, user.vip);
if(sqlite3_step(m_setUser) != SQLITE_DONE) {
LOG4CXX_ERROR(logger, "setUser query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db)));