spectrum2/backends/twitter/TwitterPlugin.cpp

909 lines
34 KiB
C++
Raw Permalink Normal View History

2015-10-15 15:41:16 +03:00
#include "TwitterPlugin.h"
#include "Requests/StatusUpdateRequest.h"
#include "Requests/DirectMessageRequest.h"
#include "Requests/TimelineRequest.h"
#include "Requests/FetchFriends.h"
#include "Requests/HelpMessageRequest.h"
#include "Requests/PINExchangeProcess.h"
#include "Requests/OAuthFlow.h"
#include "Requests/CreateFriendRequest.h"
#include "Requests/DestroyFriendRequest.h"
#include "Requests/RetweetRequest.h"
#include "Requests/ProfileImageRequest.h"
#include "Swiften/StringCodecs/Hexify.h"
DEFINE_LOGGER(logger, "Twitter Backend");
TwitterPlugin *np = NULL;
Swift::SimpleEventLoop *loop_; // Event Loop
const std::string OLD_APP_KEY = "PCWAdQpyyR12ezp2fVwEhw";
const std::string OLD_APP_SECRET = "EveLmCXJIg2R7BTCpm6OWV8YyX49nI0pxnYXh7JMvDg";
#define abs(x) ((x)<0?-(x):(x))
#define SHA(x) (Swift::Hexify::hexify(Swift::SHA1::getHash(Swift::createByteArray((x)))))
2017-06-12 00:41:35 +02:00
//Compares two +ve intergers 'a' and 'b' represented as strings
2015-10-15 15:41:16 +03:00
static int cmp(std::string a, std::string b)
{
int diff = abs((int)a.size() - (int)b.size());
if(a.size() < b.size()) a = std::string(diff,'0') + a;
else b = std::string(diff,'0') + b;
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(a == b) return 0;
if(a < b) return -1;
return 1;
}
2017-06-12 00:41:35 +02:00
TwitterPlugin::TwitterPlugin(Config *config, Swift::SimpleEventLoop *loop, StorageBackend *storagebackend, const std::string &host, int port) : NetworkPlugin()
2015-10-15 15:41:16 +03:00
{
this->config = config;
this->storagebackend = storagebackend;
this->m_firstPing = true;
if (CONFIG_HAS_KEY(config, "twitter.consumer_key") == false) {
consumerKey = "5mFePMiJi0KpeURONkelg";
}
else {
consumerKey = CONFIG_STRING(config, "twitter.consumer_key");
}
if (CONFIG_HAS_KEY(config, "twitter.consumer_secret") == false) {
consumerSecret = "YFZCDJwRhbkccXEnaYr1waCQejTJcOY8F7l5Wim3FA";
}
else {
consumerSecret = CONFIG_STRING(config, "twitter.consumer_secret");
}
if (consumerSecret.empty() || consumerKey.empty()) {
LOG4CXX_ERROR(logger, "Consumer key and Consumer secret can't be empty.");
exit(1);
}
2017-06-12 00:41:35 +02:00
adminLegacyName = "twitter.com";
adminChatRoom = "#twitter";
adminNickName = "twitter";
2015-10-15 15:41:16 +03:00
adminAlias = "twitter";
OAUTH_KEY = "twitter_oauth_token";
OAUTH_SECRET = "twitter_oauth_secret";
MODE = "mode";
m_factories = new Swift::BoostNetworkFactories(loop);
m_conn = m_factories->getConnectionFactory()->createConnection();
m_conn->onDataRead.connect(boost::bind(&TwitterPlugin::_handleDataRead, this, _1));
2017-06-09 21:34:39 +03:00
m_conn->connect(Swift::HostAddressPort(SWIFT_HOSTADDRESS(host), port));
2015-10-15 15:41:16 +03:00
tp = new ThreadPool(loop_, 10);
2015-12-27 16:31:00 +01:00
LOG4CXX_INFO(logger, "Fetch timeout is set to " << CONFIG_INT_DEFAULTED(config, "twitter.fetch_timeout", 90000));
tweet_timer = m_factories->getTimerFactory()->createTimer(CONFIG_INT_DEFAULTED(config, "twitter.fetch_timeout", 90000));
message_timer = m_factories->getTimerFactory()->createTimer(CONFIG_INT_DEFAULTED(config, "twitter.fetch_timeout", 90000));
2015-10-15 15:41:16 +03:00
tweet_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForTweets, this));
message_timer->onTick.connect(boost::bind(&TwitterPlugin::pollForDirectMessages, this));
tweet_timer->start();
message_timer->start();
#if HAVE_SWIFTEN_3
cryptoProvider = SWIFTEN_SHRPTR_NAMESPACE::shared_ptr<Swift::CryptoProvider>(Swift::PlatformCryptoProvider::create());
#endif
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
LOG4CXX_INFO(logger, "Starting the plugin.");
}
2017-06-12 00:41:35 +02:00
TwitterPlugin::~TwitterPlugin()
2015-10-15 15:41:16 +03:00
{
delete storagebackend;
std::set<std::string>::iterator it;
for(it = onlineUsers.begin() ; it != onlineUsers.end() ; it++) delete userdb[*it].sessions;
delete tp;
}
// Send data to NetworkPlugin server
2017-06-12 00:41:35 +02:00
void TwitterPlugin::sendData(const std::string &string)
2015-10-15 15:41:16 +03:00
{
m_conn->write(Swift::createSafeByteArray(string));
}
// Receive date from the NetworkPlugin server and invoke the appropirate payload handler (implement in the NetworkPlugin class)
void TwitterPlugin::_handleDataRead(SWIFTEN_SHRPTR_NAMESPACE::shared_ptr<Swift::SafeByteArray> data)
2015-10-15 15:41:16 +03:00
{
if (m_firstPing) {
m_firstPing = false;
// Users can join the network without registering if we allow
// one user to connect multiple IRC networks.
NetworkPlugin::PluginConfig cfg;
cfg.setNeedPassword(false);
sendConfig(cfg);
}
std::string d(data->begin(), data->end());
handleDataRead(d);
}
// User trying to login into his twitter account
2017-06-12 00:41:35 +02:00
void TwitterPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password)
2015-10-15 15:41:16 +03:00
{
2017-06-12 00:41:35 +02:00
if(userdb.count(user) && (userdb[user].connectionState == NEW ||
userdb[user].connectionState == CONNECTED ||
2015-10-15 15:41:16 +03:00
userdb[user].connectionState == WAITING_FOR_PIN)) {
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, std::string("A session corresponding to ") + user + std::string(" is already active"));
2015-10-15 15:41:16 +03:00
return;
}
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, std::string("Received login request for ") + user);
2015-10-15 15:41:16 +03:00
initUserSession(user, legacyName, password);
handleConnected(user);
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "SPECTRUM 1 USER? - " << (userdb[user].spectrum1User? "true" : "false"));
LOG4CXX_INFO(logger, user << ": Adding Buddy " << adminLegacyName << " " << adminAlias);
2015-10-15 15:41:16 +03:00
handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
userdb[user].nickName = "";
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "Querying database for usersettings of " << user);
2015-10-15 15:41:16 +03:00
std::string key, secret;
getUserOAuthKeyAndSecret(user, key, secret);
2017-06-12 00:41:35 +02:00
if(key == "" || secret == "") {
LOG4CXX_INFO(logger, "Intiating OAuth Flow for user " << user);
2015-10-15 15:41:16 +03:00
setTwitterMode(user, 0);
tp->runAsThread(new OAuthFlow(np, userdb[user].sessions, user, userdb[user].sessions->getTwitterUsername()));
} else {
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " is already registerd. Using the stored oauth key and secret");
LOG4CXX_INFO(logger, key << " " << secret);
2015-10-15 15:41:16 +03:00
pinExchangeComplete(user, key, secret);
}
}
// User logging out
2017-06-12 00:41:35 +02:00
void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName)
2015-10-15 15:41:16 +03:00
{
if (userdb.count(user)) {
delete userdb[user].sessions;
userdb[user].sessions = NULL;
userdb[user].connectionState = DISCONNECTED;
}
if(onlineUsers.count(user)) {
onlineUsers.erase(user);
}
}
// User joining a Chatroom
void TwitterPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password)
{
2017-06-12 00:41:35 +02:00
if(room == adminChatRoom) {
LOG4CXX_INFO(logger, "Received Join Twitter room request for " << user << " '" << nickname << "'");
2015-10-15 15:41:16 +03:00
setTwitterMode(user, 2);
handleParticipantChanged(user, nickname, room, 0, pbnetwork::STATUS_ONLINE);
2015-10-15 15:41:16 +03:00
handleParticipantChanged(user, adminNickName, room, 0, pbnetwork::STATUS_ONLINE);
userdb[user].nickName = nickname;
handleMessage(user, adminChatRoom, "Connected to Twitter room! Populating your followers list", adminNickName);
tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
} else {
setTwitterMode(user, 0);
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Couldn't connect to chatroom - " << room <<"! Try twitter-chatroom as the chatroom to access Twitter account");
2015-10-15 15:41:16 +03:00
handleMessage(user, adminLegacyName, "Couldn't connect to chatroom! Try twitter-chatroom as the chatroom to access Twitter account");
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
}
// User leaving a Chatroom
void TwitterPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room)
{
if(room == adminChatRoom && onlineUsers.count(user)) {
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "Leaving chatroom! Switching back to default mode 0");
2015-10-15 15:41:16 +03:00
setTwitterMode(user, 0);
handleBuddyChanged(user, adminLegacyName, adminAlias, std::vector<std::string>(), pbnetwork::STATUS_ONLINE);
}
}
2017-06-12 00:41:35 +02:00
// Messages to be sent to Twitter
void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &/*id*/)
2015-10-15 15:41:16 +03:00
{
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "Received " << user << " --> " << legacyName << " - " << message);
2015-10-15 15:41:16 +03:00
if(legacyName == adminLegacyName || legacyName == adminChatRoom) {
std::string cmd = "", data = "";
2017-06-12 00:41:35 +02:00
/** Parsing the message - Assuming message format to be <cmd>[ ]*<data>**/
2015-10-15 15:41:16 +03:00
int i;
for(i=0 ; i<message.size() && message[i] != ' '; i++) cmd += message[i];
while(i<message.size() && message[i] == ' ') i++;
data = message.substr(i);
/***********************************************************************/
2017-06-12 00:41:35 +02:00
if(cmd == "#pin")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data));
2017-06-12 00:41:35 +02:00
else if(cmd == "#help")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2)));
else if(cmd[0] == '@') {
2017-06-12 00:41:35 +02:00
std::string username = cmd.substr(1);
2015-10-15 15:41:16 +03:00
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data,
boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
}
2017-06-12 00:41:35 +02:00
else if(cmd == "#status")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, data,
boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
2017-06-12 00:41:35 +02:00
else if(cmd == "#timeline")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, data, "",
boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
2017-06-12 00:41:35 +02:00
else if(cmd == "#friends")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new FetchFriends(userdb[user].sessions, user,
boost::bind(&TwitterPlugin::displayFriendlist, this, _1, _2, _3, _4)));
2017-06-12 00:41:35 +02:00
else if(cmd == "#follow")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
2017-06-12 00:41:35 +02:00
else if(cmd == "#unfollow")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, data.substr(0,data.find('@')),
boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
2017-06-12 00:41:35 +02:00
else if(cmd == "#retweet")
2015-10-15 15:41:16 +03:00
tp->runAsThread(new RetweetRequest(userdb[user].sessions, user, data,
boost::bind(&TwitterPlugin::RetweetResponse, this, _1, _2)));
else if(cmd == "#mode") {
int m = 0;
m = atoi(data.c_str());
mode prevm = userdb[user].twitterMode;
if((mode)m == userdb[user].twitterMode) return; //If same as current mode return
2016-01-02 19:49:30 +01:00
if(m < 0 || m > 1) { // Invalid modes
handleMessage(user, adminLegacyName, std::string("Error! Unknown mode ") + data + ". Allowed values 0 or 1." );
2015-10-15 15:41:16 +03:00
return;
}
setTwitterMode(user, m);
if((userdb[user].twitterMode == SINGLECONTACT || userdb[user].twitterMode == CHATROOM) && prevm == MULTIPLECONTACT) clearRoster(user);
2017-06-12 00:41:35 +02:00
else if(userdb[user].twitterMode == MULTIPLECONTACT)
2015-10-15 15:41:16 +03:00
tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("Changed mode to ") + data, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << ": Changed mode to " << data << " <" << (userdb[user].twitterMode == CHATROOM ? adminNickName : "") << ">" );
2015-10-15 15:41:16 +03:00
}
else if(userdb[user].twitterMode == CHATROOM) {
std::string buddy = message.substr(0, message.find(":"));
if(userdb[user].buddies.count(buddy) == 0) {
tp->runAsThread(new StatusUpdateRequest(userdb[user].sessions, user, message,
boost::bind(&TwitterPlugin::statusUpdateResponse, this, _1, _2)));
} else {
data = message.substr(message.find(":")+1); //Can parse better??:P
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, data,
boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
}
}
else handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
"Unknown command! Type #help for a list of available commands.", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
2017-06-12 00:41:35 +02:00
else {
2015-10-15 15:41:16 +03:00
std::string buddy = legacyName;
if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1);
if(legacyName != "twitter") {
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message,
boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
}
}
}
2017-06-12 00:41:35 +02:00
void TwitterPlugin::handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups)
2015-10-15 15:41:16 +03:00
{
if(userdb[user].connectionState != CONNECTED) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, user << " is not connected to twitter!");
2015-10-15 15:41:16 +03:00
return;
}
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " - Adding Twitter contact " << buddyName);
tp->runAsThread(new CreateFriendRequest(userdb[user].sessions, user, buddyName,
2015-10-15 15:41:16 +03:00
boost::bind(&TwitterPlugin::createFriendResponse, this, _1, _2, _3, _4)));
}
2017-06-12 00:41:35 +02:00
void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups)
2015-10-15 15:41:16 +03:00
{
if(userdb[user].connectionState != CONNECTED) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, user << " is not connected to twitter!");
2015-10-15 15:41:16 +03:00
return;
}
if (getTwitterMode(user) == MULTIPLECONTACT) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, user << " not removing Twitter contact " << buddyName << ", because the mode is not MULTIPLECONTACT");
return;
}
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " - Removing Twitter contact " << buddyName);
tp->runAsThread(new DestroyFriendRequest(userdb[user].sessions, user, buddyName,
2015-10-15 15:41:16 +03:00
boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3)));
}
void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id)
{
if(userdb[user].connectionState != CONNECTED) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, user << " is not connected to twitter!");
2015-10-15 15:41:16 +03:00
return;
}
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << userdb[user].buddiesInfo[legacyName].getProfileImgURL());
if(getTwitterMode(user) != SINGLECONTACT && userdb[user].buddies.count(legacyName)
2015-10-15 15:41:16 +03:00
&& userdb[user].buddiesInfo[legacyName].getProfileImgURL().length()) {
if(userdb[user].buddiesImgs.count(legacyName) == 0) {
tp->runAsThread(new ProfileImageRequest(config, user, legacyName, userdb[user].buddiesInfo[legacyName].getProfileImgURL(), id,
boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5)));
}
handleVCard(user, id, legacyName, legacyName, "", userdb[user].buddiesImgs[legacyName]);
}
}
void TwitterPlugin::pollForTweets()
{
boost::mutex::scoped_lock lock(userlock);
std::set<std::string>::iterator it = onlineUsers.begin();
while(it != onlineUsers.end()) {
std::string user = *it;
tp->runAsThread(new TimelineRequest(userdb[user].sessions, user, "", getMostRecentTweetIDUnsafe(user),
boost::bind(&TwitterPlugin::displayTweets, this, _1, _2, _3, _4)));
it++;
}
tweet_timer->start();
}
void TwitterPlugin::pollForDirectMessages()
{
boost::mutex::scoped_lock lock(userlock);
std::set<std::string>::iterator it = onlineUsers.begin();
while(it != onlineUsers.end()) {
std::string user = *it;
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, "", getMostRecentDMIDUnsafe(user),
boost::bind(&TwitterPlugin::directMessageResponse, this, _1, _2, _3, _4)));
it++;
}
message_timer->start();
}
2017-06-12 00:41:35 +02:00
bool TwitterPlugin::getUserOAuthKeyAndSecret(const std::string user, std::string &key, std::string &secret)
2015-10-15 15:41:16 +03:00
{
boost::mutex::scoped_lock lock(dblock);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return false;
}
key="", secret=""; int type = TYPE_STRING;;
2015-10-15 15:41:16 +03:00
storagebackend->getUserSetting((long)info.id, OAUTH_KEY, type, key);
storagebackend->getUserSetting((long)info.id, OAUTH_SECRET, type, secret);
return true;
}
2017-06-12 00:41:35 +02:00
bool TwitterPlugin::checkSpectrum1User(const std::string user)
2015-10-15 15:41:16 +03:00
{
boost::mutex::scoped_lock lock(dblock);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return false;
}
std::string first_synchronization_done = "";
int type = TYPE_STRING;
2015-10-15 15:41:16 +03:00
storagebackend->getUserSetting((long)info.id, "first_synchronization_done", type, first_synchronization_done);
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "first_synchronization_done: " << first_synchronization_done);
2015-10-15 15:41:16 +03:00
if(first_synchronization_done.length()) return true;
return false;
}
2017-06-12 00:41:35 +02:00
int TwitterPlugin::getTwitterMode(const std::string user)
2015-10-15 15:41:16 +03:00
{
boost::mutex::scoped_lock lock(dblock);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return -1;
}
int type = TYPE_STRING; int m;
2015-10-15 15:41:16 +03:00
std::string s_m;
storagebackend->getUserSetting((long)info.id, MODE, type, s_m);
if(s_m == "") {
s_m = "0";
storagebackend->updateUserSetting((long)info.id, MODE, s_m);
}
m = atoi(s_m.c_str());
return m;
}
2017-06-12 00:41:35 +02:00
bool TwitterPlugin::setTwitterMode(const std::string user, int m)
2015-10-15 15:41:16 +03:00
{
boost::mutex::scoped_lock lock(dblock);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return false;
}
if(m < 0 || m > 2) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Unknown mode " << m <<". Using default mode 0");
2015-10-15 15:41:16 +03:00
m = 0;
}
userdb[user].twitterMode = (mode)m;
//int type;
std::string s_m = std::string(1,m+'0');
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, "Storing mode " << m <<" for user " << user);
2015-10-15 15:41:16 +03:00
storagebackend->updateUserSetting((long)info.id, MODE, s_m);
return true;
}
2017-06-12 00:41:35 +02:00
bool TwitterPlugin::storeUserOAuthKeyAndSecret(const std::string user, const std::string OAuthKey, const std::string OAuthSecret)
2015-10-15 15:41:16 +03:00
{
boost::mutex::scoped_lock lock(dblock);
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return false;
}
2017-06-12 00:41:35 +02:00
storagebackend->updateUserSetting((long)info.id, OAUTH_KEY, OAuthKey);
2015-10-15 15:41:16 +03:00
storagebackend->updateUserSetting((long)info.id, OAUTH_SECRET, OAuthSecret);
return true;
}
void TwitterPlugin::initUserSession(const std::string user, const std::string legacyName, const std::string password)
{
boost::mutex::scoped_lock lock(userlock);
std::string username = legacyName;
std::string passwd = password;
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, username + " " + passwd);
2015-10-15 15:41:16 +03:00
2017-06-12 00:41:35 +02:00
userdb[user].sessions = new twitCurl();
if(CONFIG_HAS_KEY(config,"proxy.server")) {
2015-10-15 15:41:16 +03:00
std::string ip = CONFIG_STRING(config,"proxy.server");
2017-06-12 00:41:35 +02:00
std::ostringstream out;
2015-10-15 15:41:16 +03:00
out << CONFIG_INT(config,"proxy.port");
std::string port = out.str();
std::string puser = CONFIG_STRING(config,"proxy.user");
std::string ppasswd = CONFIG_STRING(config,"proxy.password");
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, ip << " " << port << " " << puser << " " << ppasswd);
2015-10-15 15:41:16 +03:00
if(ip != "localhost" && port != "0") {
userdb[user].sessions->setProxyServerIp(ip);
userdb[user].sessions->setProxyServerPort(port);
userdb[user].sessions->setProxyUserName(puser);
userdb[user].sessions->setProxyPassword(ppasswd);
}
}
//Check if the user is spectrum1 user
userdb[user].spectrum1User = checkSpectrum1User(user);
userdb[user].connectionState = NEW;
2017-06-12 00:41:35 +02:00
userdb[user].legacyName = username;
2015-10-15 15:41:16 +03:00
userdb[user].sessions->setTwitterUsername(username);
userdb[user].sessions->setTwitterPassword(passwd);
if(!userdb[user].spectrum1User) {
userdb[user].sessions->getOAuth().setConsumerKey(consumerKey);
userdb[user].sessions->getOAuth().setConsumerSecret(consumerSecret);
} else {
userdb[user].sessions->getOAuth().setConsumerKey(OLD_APP_KEY);
userdb[user].sessions->getOAuth().setConsumerSecret(OLD_APP_SECRET);
}
}
2017-06-12 00:41:35 +02:00
void TwitterPlugin::OAuthFlowComplete(const std::string user, twitCurl *obj)
2015-10-15 15:41:16 +03:00
{
2017-06-12 00:41:35 +02:00
boost::mutex::scoped_lock lock(userlock);
2015-10-15 15:41:16 +03:00
delete userdb[user].sessions;
2017-06-12 00:41:35 +02:00
userdb[user].sessions = obj->clone();
2015-10-15 15:41:16 +03:00
userdb[user].connectionState = WAITING_FOR_PIN;
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
2017-06-12 00:41:35 +02:00
void TwitterPlugin::pinExchangeComplete(const std::string user, const std::string OAuthAccessTokenKey, const std::string OAuthAccessTokenSecret)
2015-10-15 15:41:16 +03:00
{
2017-06-12 00:41:35 +02:00
boost::mutex::scoped_lock lock(userlock);
2015-10-15 15:41:16 +03:00
userdb[user].sessions->getOAuth().setOAuthTokenKey( OAuthAccessTokenKey );
userdb[user].sessions->getOAuth().setOAuthTokenSecret( OAuthAccessTokenSecret );
userdb[user].connectionState = CONNECTED;
userdb[user].twitterMode = (mode)getTwitterMode(user);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == MULTIPLECONTACT) {
tp->runAsThread(new FetchFriends(userdb[user].sessions, user, boost::bind(&TwitterPlugin::populateRoster, this, _1, _2, _3, _4)));
}
onlineUsers.insert(user);
userdb[user].mostRecentTweetID = "";
userdb[user].mostRecentDirectMessageID = "";
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
void TwitterPlugin::updateLastTweetID(const std::string user, const std::string ID)
{
2017-06-12 00:41:35 +02:00
boost::mutex::scoped_lock lock(userlock);
2015-10-15 15:41:16 +03:00
userdb[user].mostRecentTweetID = ID;
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return;
}
storagebackend->updateUserSetting((long)info.id, "twitter_last_tweet", ID);
}
std::string TwitterPlugin::getMostRecentTweetIDUnsafe(const std::string user)
2017-06-12 00:41:35 +02:00
{
2015-10-15 15:41:16 +03:00
std::string ID = "";
if(onlineUsers.count(user)) {
ID = userdb[user].mostRecentTweetID;
if (ID.empty()) {
int type = TYPE_STRING;
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
}
else {
storagebackend->getUserSetting(info.id, "twitter_last_tweet", type, ID);
}
}
}
return ID;
}
std::string TwitterPlugin::getMostRecentTweetID(const std::string user)
2017-06-12 00:41:35 +02:00
{
2015-10-15 15:41:16 +03:00
boost::mutex::scoped_lock lock(userlock);
return getMostRecentTweetIDUnsafe(user);
}
void TwitterPlugin::updateLastDMID(const std::string user, const std::string ID)
{
2017-06-12 00:41:35 +02:00
boost::mutex::scoped_lock lock(userlock);
2015-10-15 15:41:16 +03:00
userdb[user].mostRecentDirectMessageID = ID;
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
return;
}
storagebackend->updateUserSetting((long)info.id, "twitter_last_dm", ID);
}
std::string TwitterPlugin::getMostRecentDMIDUnsafe(const std::string user) {
std::string ID = "";
if(onlineUsers.count(user)) {
ID = userdb[user].mostRecentDirectMessageID;
if (ID.empty()) {
int type = TYPE_STRING;
2015-10-15 15:41:16 +03:00
UserInfo info;
if(storagebackend->getUser(user, info) == false) {
2017-06-12 00:41:35 +02:00
LOG4CXX_ERROR(logger, "Didn't find entry for " << user << " in the database!");
2015-10-15 15:41:16 +03:00
}
else {
storagebackend->getUserSetting(info.id, "twitter_last_dm", type, ID);
}
}
}
return ID;
}
std::string TwitterPlugin::getMostRecentDMID(const std::string user)
{
2017-06-12 00:41:35 +02:00
boost::mutex::scoped_lock lock(userlock);
2015-10-15 15:41:16 +03:00
return getMostRecentDMIDUnsafe(user);
}
/************************************** Twitter response functions **********************************/
void TwitterPlugin::statusUpdateResponse(std::string &user, Error &errMsg)
{
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
} else {
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
"Status Update successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
}
}
void TwitterPlugin::helpMessageResponse(std::string &user, std::string &msg)
{
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
msg, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
}
void TwitterPlugin::clearRoster(const std::string user)
{
if(userdb[user].buddies.size() == 0) return;
std::set<std::string>::iterator it = userdb[user].buddies.begin();
while(it != userdb[user].buddies.end()) {
handleBuddyRemoved(user, *it);
it++;
}
userdb[user].buddies.clear();
}
2017-06-12 00:41:35 +02:00
void TwitterPlugin::populateRoster(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg)
2015-10-15 15:41:16 +03:00
{
2017-06-12 00:41:35 +02:00
if(errMsg.getMessage().length() == 0)
2015-10-15 15:41:16 +03:00
{
for(int i=0 ; i<friends.size() ; i++) {
userdb[user].buddies.insert(friends[i].getScreenName());
userdb[user].buddiesInfo[friends[i].getScreenName()] = friends[i];
userdb[user].buddiesImgs[friends[i].getScreenName()] = friendAvatars[i];
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == MULTIPLECONTACT) {
std::string lastTweet = friends[i].getLastStatus().getTweet();
//LOG4CXX_INFO(logger, user << " - " << SHA(friendAvatars[i]))
2017-06-12 00:41:35 +02:00
handleBuddyChanged(user, friends[i].getScreenName(), friends[i].getUserName(), std::vector<std::string>(),
2015-10-15 15:41:16 +03:00
#if HAVE_SWIFTEN_3
pbnetwork::STATUS_ONLINE, lastTweet, Swift::Hexify::hexify(cryptoProvider->getSHA1Hash(Swift::createByteArray(friendAvatars[i]))));
2015-10-15 15:41:16 +03:00
#else
pbnetwork::STATUS_ONLINE, lastTweet, SHA(friendAvatars[i]));
#endif
}
else if(userdb[user].twitterMode == CHATROOM)
handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
/*handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
2017-06-12 00:41:35 +02:00
friends[i].getScreenName() + " - " + friends[i].getLastStatus().getTweet(),
2015-10-15 15:41:16 +03:00
userdb[user].twitterMode == CHATROOM ? adminNickName : "");*/
}
} else {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("Error populating roster - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
}
if(userdb[user].twitterMode == CHATROOM) handleParticipantChanged(user, userdb[user].nickName, adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
}
void TwitterPlugin::displayFriendlist(std::string &user, std::vector<User> &friends, std::vector<std::string> &friendAvatars, Error &errMsg)
{
2017-06-12 00:41:35 +02:00
if(errMsg.getMessage().length() == 0)
2015-10-15 15:41:16 +03:00
{
std::string userlist = "\n***************USER LIST****************\n";
for(int i=0 ; i < friends.size() ; i++) {
userlist += " - " + friends[i].getUserName() + " (" + friends[i].getScreenName() + ")\n";
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
userlist += "***************************************\n";
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
2017-06-12 00:41:35 +02:00
userlist, userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2015-10-15 15:41:16 +03:00
} else {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
2017-06-12 00:41:35 +02:00
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2015-10-15 15:41:16 +03:00
}
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
}
void TwitterPlugin::displayTweets(std::string &user, std::string &userRequested, std::vector<Status> &tweets , Error &errMsg)
{
if(errMsg.getMessage().length() == 0) {
std::map<std::string, int> lastTweet;
std::map<std::string, int>::iterator it;
for(int i = tweets.size() - 1 ; i >= 0 ; i--) {
if(userdb[user].twitterMode != CHATROOM) {
std::string m = " - " + tweets[i].getUserData().getScreenName() + ": " + tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")\n";
handleMessage(user, adminLegacyName, m, "", "", tweets[i].getCreationTime(), true);
std::string scrname = tweets[i].getUserData().getScreenName();
if(lastTweet.count(scrname) == 0 || cmp(tweets[lastTweet[scrname]].getID(), tweets[i].getID()) <= 0) lastTweet[scrname] = i;
} else {
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
tweets[i].getTweet() + " (MsgId: " + (tweets[i].getRetweetID().empty() ? tweets[i].getID() : tweets[i].getRetweetID()) + ")", tweets[i].getUserData().getScreenName(), "", tweets[i].getCreationTime(), true);
}
}
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == MULTIPLECONTACT) {
//Set as status user's last tweet
for(it=lastTweet.begin() ; it!=lastTweet.end() ; it++) {
int t = it->second;
if (userdb[user].buddies.count(tweets[t].getUserData().getScreenName()) != 0) {
2017-06-12 00:41:35 +02:00
handleBuddyChanged(user, tweets[t].getUserData().getScreenName(), tweets[t].getUserData().getUserName(),
std::vector<std::string>(), pbnetwork::STATUS_ONLINE, tweets[t].getTweet());
}
2015-10-15 15:41:16 +03:00
}
}
if((userRequested == "" || userRequested == user) && tweets.size()) {
std::string tweetID = getMostRecentTweetID(user);
if(tweetID != tweets[0].getID()) updateLastTweetID(user, tweets[0].getID());
}
} else {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
2017-06-12 00:41:35 +02:00
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2015-10-15 15:41:16 +03:00
}
}
void TwitterPlugin::directMessageResponse(std::string &user, std::string &username, std::vector<DirectMessage> &messages, Error &errMsg)
{
if(errMsg.getCode() == "93") //Permission Denied
return;
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
if(username != "")
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("Error while sending direct message! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
else
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("Error while fetching direct messages! - ") + errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
return;
}
if(username != "") {
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
2017-06-12 00:41:35 +02:00
"Message delivered!", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2015-10-15 15:41:16 +03:00
return;
}
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(!messages.size()) return;
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == SINGLECONTACT) {
std::string msglist = "";
std::string msgID = getMostRecentDMID(user);
std::string maxID = msgID;
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
for(int i=0 ; i < messages.size() ; i++) {
if(cmp(msgID, messages[i].getID()) == -1) {
msglist += " - " + messages[i].getSenderData().getScreenName() + ": " + messages[i].getMessage() + "\n";
if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
}
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
2017-06-12 00:41:35 +02:00
if(msglist.length()) handleMessage(user, adminLegacyName, msglist, "");
2015-10-15 15:41:16 +03:00
updateLastDMID(user, maxID);
} else {
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
std::string msgID = getMostRecentDMID(user);
std::string maxID = msgID;
for(int i=0 ; i < messages.size() ; i++) {
if(cmp(msgID, messages[i].getID()) == -1) {
if(userdb[user].twitterMode == MULTIPLECONTACT)
handleMessage(user, messages[i].getSenderData().getScreenName(), messages[i].getMessage(), "");
else
handleMessage(user, adminChatRoom, messages[i].getMessage() + " - <Direct Message>", messages[i].getSenderData().getScreenName());
if(cmp(maxID, messages[i].getID()) == -1) maxID = messages[i].getID();
}
2017-06-12 00:41:35 +02:00
}
if(maxID == getMostRecentDMID(user)) LOG4CXX_INFO(logger, "No new direct messages for " << user);
2015-10-15 15:41:16 +03:00
updateLastDMID(user, maxID);
}
}
void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &img, Error &errMsg)
{
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("You are now following ") + frnd.getScreenName(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
userdb[user].buddies.insert(frnd.getScreenName());
userdb[user].buddiesInfo[frnd.getScreenName()] = frnd;
userdb[user].buddiesImgs[frnd.getScreenName()] = img;
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL());
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == MULTIPLECONTACT) {
#if HAVE_SWIFTEN_3
handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", Swift::byteArrayToString(cryptoProvider->getSHA1Hash(Swift::createByteArray(img))));
#else
handleBuddyChanged(user, frnd.getScreenName(), frnd.getUserName(), std::vector<std::string>(), pbnetwork::STATUS_ONLINE, "", SHA(img));
#endif
} else if(userdb[user].twitterMode == CHATROOM) {
handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE);
}
}
void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, Error &errMsg)
{
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
2017-06-12 00:41:35 +02:00
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
2015-10-15 15:41:16 +03:00
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
return;
2017-06-12 00:41:35 +02:00
}
LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << frnd.getProfileImgURL());
2015-10-15 15:41:16 +03:00
userdb[user].buddies.erase(frnd.getScreenName());
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
std::string("You are not following ") + frnd.getScreenName() + " anymore", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if (userdb[user].twitterMode == CHATROOM) {
handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE);
}
2017-06-12 00:41:35 +02:00
2015-10-15 15:41:16 +03:00
if(userdb[user].twitterMode == MULTIPLECONTACT) {
handleBuddyRemoved(user, frnd.getScreenName());
2017-06-12 00:41:35 +02:00
}
2015-10-15 15:41:16 +03:00
}
void TwitterPlugin::RetweetResponse(std::string &user, Error &errMsg)
{
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
} else {
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
"Retweet successful", userdb[user].twitterMode == CHATROOM ? adminNickName : "");
}
}
void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, Error &errMsg)
{
if(errMsg.getMessage().length()) {
if (errMsg.isCurlError()) {
handleDisconnected(user, 3, errMsg.getMessage());
return;
}
handleMessage(user, userdb[user].twitterMode == CHATROOM ? adminChatRoom : adminLegacyName,
errMsg.getMessage(), userdb[user].twitterMode == CHATROOM ? adminNickName : "");
} else {
2017-06-12 00:41:35 +02:00
LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy);
2015-10-15 15:41:16 +03:00
handleVCard(user, reqID, buddy, buddy, "", img);
}
}