2018-06-26 14:09:39 +02:00
|
|
|
#include "TwitchServer.hpp"
|
2018-02-05 15:11:50 +01:00
|
|
|
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "Application.hpp"
|
2018-07-07 11:41:01 +02:00
|
|
|
#include "common/Common.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "controllers/accounts/AccountController.hpp"
|
|
|
|
#include "controllers/highlights/HighlightController.hpp"
|
|
|
|
#include "providers/twitch/IrcMessageHandler.hpp"
|
2018-07-07 11:41:01 +02:00
|
|
|
#include "providers/twitch/PubsubClient.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "providers/twitch/TwitchAccount.hpp"
|
2018-08-11 22:23:06 +02:00
|
|
|
#include "providers/twitch/TwitchChannel.hpp"
|
2018-06-26 14:09:39 +02:00
|
|
|
#include "providers/twitch/TwitchHelpers.hpp"
|
|
|
|
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
|
|
|
#include "util/PostToThread.hpp"
|
2018-02-05 15:11:50 +01:00
|
|
|
|
2018-06-04 21:05:18 +02:00
|
|
|
#include <IrcCommand>
|
2018-02-05 15:11:50 +01:00
|
|
|
#include <cassert>
|
|
|
|
|
2018-06-04 21:05:18 +02:00
|
|
|
// using namespace Communi;
|
2018-06-24 12:59:47 +02:00
|
|
|
using namespace std::chrono_literals;
|
2018-02-05 15:11:50 +01:00
|
|
|
|
|
|
|
namespace chatterino {
|
2018-03-25 11:37:57 +02:00
|
|
|
|
2018-02-05 15:11:50 +01:00
|
|
|
TwitchServer::TwitchServer()
|
2018-07-06 17:30:12 +02:00
|
|
|
: whispersChannel(new Channel("/whispers", Channel::Type::TwitchWhispers))
|
|
|
|
, mentionsChannel(new Channel("/mentions", Channel::Type::TwitchMentions))
|
|
|
|
, watchingChannel(Channel::getEmpty(), Channel::Type::TwitchWatching)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-04-26 18:10:26 +02:00
|
|
|
qDebug() << "init TwitchServer";
|
2018-07-07 11:41:01 +02:00
|
|
|
|
|
|
|
this->pubsub = new PubSub;
|
2018-07-16 17:23:41 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
// getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) {
|
|
|
|
// this->connect(); },
|
|
|
|
// this->signalHolder_,
|
|
|
|
// false);
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
|
2018-08-02 14:23:27 +02:00
|
|
|
void TwitchServer::initialize(Settings &settings, Paths &paths)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-08-02 14:23:27 +02:00
|
|
|
getApp()->accounts->twitch.currentUserChanged.connect(
|
2018-06-26 17:06:17 +02:00
|
|
|
[this]() { postToThread([this] { this->connect(); }); });
|
2018-08-12 00:01:37 +02:00
|
|
|
|
2018-08-14 17:45:17 +02:00
|
|
|
this->twitchBadges.loadTwitchBadges();
|
2018-08-12 00:01:37 +02:00
|
|
|
this->bttv.loadEmotes();
|
|
|
|
this->ffz.loadEmotes();
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead,
|
|
|
|
bool isWrite)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-07-16 17:23:41 +02:00
|
|
|
this->singleConnection_ = isRead == isWrite;
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
std::shared_ptr<TwitchAccount> account =
|
|
|
|
getApp()->accounts->twitch.getCurrent();
|
2018-02-05 15:11:50 +01:00
|
|
|
|
2018-04-26 18:10:26 +02:00
|
|
|
qDebug() << "logging in as" << account->getUserName();
|
|
|
|
|
2018-02-05 15:11:50 +01:00
|
|
|
QString username = account->getUserName();
|
|
|
|
QString oauthToken = account->getOAuthToken();
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!oauthToken.startsWith("oauth:"))
|
|
|
|
{
|
2018-02-05 15:11:50 +01:00
|
|
|
oauthToken.prepend("oauth:");
|
|
|
|
}
|
|
|
|
|
|
|
|
connection->setUserName(username);
|
|
|
|
connection->setNickName(username);
|
|
|
|
connection->setRealName(username);
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!account->isAnon())
|
|
|
|
{
|
2018-02-05 15:11:50 +01:00
|
|
|
connection->setPassword(oauthToken);
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
connection->sendCommand(
|
|
|
|
Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership"));
|
|
|
|
connection->sendCommand(
|
|
|
|
Communi::IrcCommand::createCapability("REQ", "twitch.tv/commands"));
|
|
|
|
connection->sendCommand(
|
|
|
|
Communi::IrcCommand::createCapability("REQ", "twitch.tv/tags"));
|
2018-02-05 15:11:50 +01:00
|
|
|
|
|
|
|
connection->setHost("irc.chat.twitch.tv");
|
|
|
|
connection->setPort(6667);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Channel> TwitchServer::createChannel(const QString &channelName)
|
|
|
|
{
|
2018-08-14 17:45:17 +02:00
|
|
|
auto channel = std::shared_ptr<TwitchChannel>(new TwitchChannel(
|
|
|
|
channelName, this->twitchBadges, this->bttv, this->ffz));
|
2018-08-13 13:54:39 +02:00
|
|
|
channel->initialize();
|
2018-02-05 15:11:50 +01:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
channel->sendMessageSignal.connect(
|
2018-08-11 17:15:17 +02:00
|
|
|
[this, channel = channel.get()](auto &chan, auto &msg, bool &sent) {
|
2018-08-06 21:17:03 +02:00
|
|
|
this->onMessageSendRequested(channel, msg, sent);
|
|
|
|
});
|
2018-02-05 15:11:50 +01:00
|
|
|
|
|
|
|
return std::shared_ptr<Channel>(channel);
|
|
|
|
}
|
|
|
|
|
2018-06-04 21:05:18 +02:00
|
|
|
void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-06-04 12:23:23 +02:00
|
|
|
IrcMessageHandler::getInstance().handlePrivMessage(message, *this);
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
|
2018-06-04 21:05:18 +02:00
|
|
|
void TwitchServer::messageReceived(Communi::IrcMessage *message)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
|
|
|
// this->readConnection
|
2018-10-21 13:43:02 +02:00
|
|
|
if (message->type() == Communi::IrcMessage::Type::Private)
|
|
|
|
{
|
2018-02-05 15:11:50 +01:00
|
|
|
// We already have a handler for private messages
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString &command = message->command();
|
|
|
|
|
2018-05-26 18:06:55 +02:00
|
|
|
auto &handler = IrcMessageHandler::getInstance();
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (command == "ROOMSTATE")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleRoomStateMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "CLEARCHAT")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleClearChatMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "USERSTATE")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleUserStateMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "WHISPER")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleWhisperMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "USERNOTICE")
|
|
|
|
{
|
2018-06-04 12:23:23 +02:00
|
|
|
handler.handleUserNoticeMessage(message, *this);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "MODE")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleModeMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "NOTICE")
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
handler.handleNoticeMessage(
|
|
|
|
static_cast<Communi::IrcNoticeMessage *>(message));
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "JOIN")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handleJoinMessage(message);
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
else if (command == "PART")
|
|
|
|
{
|
2018-05-26 18:06:55 +02:00
|
|
|
handler.handlePartMessage(message);
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-04 21:05:18 +02:00
|
|
|
void TwitchServer::writeConnectionMessageReceived(Communi::IrcMessage *message)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-10-21 13:43:02 +02:00
|
|
|
switch (message->type())
|
|
|
|
{
|
|
|
|
case Communi::IrcMessage::Type::Notice:
|
|
|
|
{
|
2018-02-05 15:11:50 +01:00
|
|
|
IrcMessageHandler::getInstance().handleWriteConnectionNoticeMessage(
|
2018-06-04 21:05:18 +02:00
|
|
|
static_cast<Communi::IrcNoticeMessage *>(message));
|
2018-10-21 13:43:02 +02:00
|
|
|
}
|
|
|
|
break;
|
2018-06-04 21:05:18 +02:00
|
|
|
|
|
|
|
default:;
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
std::shared_ptr<Channel> TwitchServer::getCustomChannel(
|
|
|
|
const QString &channelName)
|
2018-02-05 15:11:50 +01:00
|
|
|
{
|
2018-10-21 13:43:02 +02:00
|
|
|
if (channelName == "/whispers")
|
|
|
|
{
|
2018-06-24 15:14:05 +02:00
|
|
|
return this->whispersChannel;
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (channelName == "/mentions")
|
|
|
|
{
|
2018-06-24 15:14:05 +02:00
|
|
|
return this->mentionsChannel;
|
2018-02-05 15:11:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-02-05 21:20:38 +01:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void TwitchServer::forEachChannelAndSpecialChannels(
|
|
|
|
std::function<void(ChannelPtr)> func)
|
2018-02-05 21:20:38 +01:00
|
|
|
{
|
2018-06-22 23:24:45 +02:00
|
|
|
this->forEachChannel(func);
|
2018-02-05 21:20:38 +01:00
|
|
|
|
|
|
|
func(this->whispersChannel);
|
|
|
|
func(this->mentionsChannel);
|
|
|
|
}
|
2018-03-25 11:37:57 +02:00
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
std::shared_ptr<Channel> TwitchServer::getChannelOrEmptyByID(
|
|
|
|
const QString &channelId)
|
2018-04-22 15:36:01 +02:00
|
|
|
{
|
2018-06-22 23:28:20 +02:00
|
|
|
std::lock_guard<std::mutex> lock(this->channelMutex);
|
2018-06-22 23:24:45 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
for (const auto &weakChannel : this->channels)
|
|
|
|
{
|
2018-06-22 23:28:20 +02:00
|
|
|
auto channel = weakChannel.lock();
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!channel)
|
|
|
|
continue;
|
2018-04-22 15:36:01 +02:00
|
|
|
|
2018-06-22 23:28:20 +02:00
|
|
|
auto twitchChannel = std::dynamic_pointer_cast<TwitchChannel>(channel);
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!twitchChannel)
|
|
|
|
continue;
|
2018-04-22 15:36:01 +02:00
|
|
|
|
2018-10-21 13:43:02 +02:00
|
|
|
if (twitchChannel->roomId() == channelId)
|
|
|
|
{
|
2018-06-22 23:28:20 +02:00
|
|
|
return twitchChannel;
|
2018-04-22 15:36:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Channel::getEmpty();
|
|
|
|
}
|
|
|
|
|
2018-04-21 00:40:17 +02:00
|
|
|
QString TwitchServer::cleanChannelName(const QString &dirtyChannelName)
|
2018-04-01 15:10:15 +02:00
|
|
|
{
|
|
|
|
return dirtyChannelName.toLower();
|
|
|
|
}
|
|
|
|
|
2018-07-16 17:23:41 +02:00
|
|
|
bool TwitchServer::hasSeparateWriteConnection() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
// return getSettings()->twitchSeperateWriteConnection;
|
|
|
|
}
|
|
|
|
|
2018-08-06 21:17:03 +02:00
|
|
|
void TwitchServer::onMessageSendRequested(TwitchChannel *channel,
|
|
|
|
const QString &message, bool &sent)
|
2018-06-24 12:59:47 +02:00
|
|
|
{
|
|
|
|
sent = false;
|
|
|
|
|
|
|
|
{
|
2018-06-24 13:06:11 +02:00
|
|
|
std::lock_guard<std::mutex> guard(this->lastMessageMutex_);
|
2018-06-24 12:59:47 +02:00
|
|
|
|
|
|
|
// std::queue<std::chrono::steady_clock::time_point>
|
2018-08-06 21:17:03 +02:00
|
|
|
auto &lastMessage = channel->hasModRights() ? this->lastMessageMod_
|
|
|
|
: this->lastMessagePleb_;
|
2018-06-24 12:59:47 +02:00
|
|
|
size_t maxMessageCount = channel->hasModRights() ? 99 : 19;
|
|
|
|
auto minMessageOffset = (channel->hasModRights() ? 100ms : 1100ms);
|
|
|
|
|
|
|
|
auto now = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
// check if you are sending messages too fast
|
2018-10-21 13:43:02 +02:00
|
|
|
if (!lastMessage.empty() && lastMessage.back() + minMessageOffset > now)
|
|
|
|
{
|
|
|
|
if (this->lastErrorTimeSpeed_ + 30s < now)
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
auto errorMessage =
|
2018-08-07 01:35:24 +02:00
|
|
|
makeSystemMessage("sending messages too fast");
|
2018-06-24 12:59:47 +02:00
|
|
|
|
|
|
|
channel->addMessage(errorMessage);
|
|
|
|
|
2018-06-24 15:14:05 +02:00
|
|
|
this->lastErrorTimeSpeed_ = now;
|
2018-06-24 12:59:47 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove messages older than 30 seconds
|
2018-10-21 13:43:02 +02:00
|
|
|
while (!lastMessage.empty() && lastMessage.front() + 32s < now)
|
|
|
|
{
|
2018-06-24 12:59:47 +02:00
|
|
|
lastMessage.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if you are sending too many messages
|
2018-10-21 13:43:02 +02:00
|
|
|
if (lastMessage.size() >= maxMessageCount)
|
|
|
|
{
|
|
|
|
if (this->lastErrorTimeAmount_ + 30s < now)
|
|
|
|
{
|
2018-08-06 21:17:03 +02:00
|
|
|
auto errorMessage =
|
2018-08-07 01:35:24 +02:00
|
|
|
makeSystemMessage("sending too many messages");
|
2018-06-24 12:59:47 +02:00
|
|
|
|
|
|
|
channel->addMessage(errorMessage);
|
|
|
|
|
2018-06-24 15:14:05 +02:00
|
|
|
this->lastErrorTimeAmount_ = now;
|
2018-06-24 12:59:47 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
lastMessage.push(now);
|
|
|
|
}
|
|
|
|
|
2018-08-02 14:23:27 +02:00
|
|
|
this->sendMessage(channel->getName(), message);
|
2018-06-24 12:59:47 +02:00
|
|
|
sent = true;
|
|
|
|
}
|
|
|
|
|
2018-10-25 21:51:55 +02:00
|
|
|
const BttvEmotes &TwitchServer::getBttvEmotes() const
|
|
|
|
{
|
|
|
|
return this->bttv;
|
|
|
|
}
|
|
|
|
const FfzEmotes &TwitchServer::getFfzEmotes() const
|
|
|
|
{
|
|
|
|
return this->ffz;
|
|
|
|
}
|
|
|
|
|
2018-02-05 15:11:50 +01:00
|
|
|
} // namespace chatterino
|