diff --git a/browser_ext/.editorconfig b/browser_ext/.editorconfig new file mode 100644 index 000000000..37346aa2f --- /dev/null +++ b/browser_ext/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/browser_ext/background.js b/browser_ext/background.js new file mode 100644 index 000000000..0de070118 --- /dev/null +++ b/browser_ext/background.js @@ -0,0 +1,66 @@ +const ignoredPages = { + "settings": true, + "payments": true, + "inventory": true, + "messages": true, + "subscriptions": true, + "friends": true, + "directory": true, +}; + +const appName = "com.chatterino.chatterino"; + +function matchUrl(url) { + if (!url) + return; + + const match = url.match(/^https?:\/\/(www\.)?twitch.tv\/([a-zA-Z0-9]+)\/?$/); + + if (match) { + const channelName = match[2]; + + if (!ignoredPages[channelName]) { + selectChannel(channelName); + } + } +} + +var port = chrome.runtime.connectNative("com.chatterino.chatterino"); + +port.onMessage.addListener(function(msg) { + console.log(msg); +}); +port.onDisconnect.addListener(function() { + console.log("Disconnected"); +}); +port.postMessage({ text: "Hello, my_application" }); + +function selectChannel(channelName) { + console.log(channelName); + + port.postMessage({channelName: channelName}); + + // chrome.runtime.sendNativeMessage(appName, { "xd": true }, (resp) => { + // console.log(resp); + // }) +} + +/// add listeners +chrome.tabs.onActivated.addListener((activeInfo) => { + chrome.tabs.get(activeInfo.tabId, (tab) => { + if (!tab) + return; + + if (!tab.url) + return; + + matchUrl(tab.url); + }); +}); + +chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + if (!tab.highlighted) + return; + + matchUrl(changeInfo.url); +}); diff --git a/browser_ext/icon.png b/browser_ext/icon.png new file mode 100644 index 000000000..9a6f5ba19 Binary files /dev/null and b/browser_ext/icon.png differ diff --git a/browser_ext/manifest.json b/browser_ext/manifest.json new file mode 100644 index 000000000..74d9fae08 --- /dev/null +++ b/browser_ext/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "Chatterino", + "version": "1.0", + "description": "xd", + "permissions": [ + "tabs", "nativeMessaging" + ], + "icons": { + "256": "icon.png" + }, + "manifest_version": 2, + "background": { + "scripts": [ + "background.js" + ], + "persistent": false + }, + "browser_action": { + "default_popup": "popup.html" + } +} diff --git a/browser_ext/popup.html b/browser_ext/popup.html new file mode 100644 index 000000000..e24ef44ab --- /dev/null +++ b/browser_ext/popup.html @@ -0,0 +1,6 @@ + + + + xd + + diff --git a/chatterino.pro b/chatterino.pro index 4ab4b8e7a..0c234fb98 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -103,7 +103,6 @@ SOURCES += \ src/providers/twitch/twitchmessagebuilder.cpp \ src/providers/twitch/twitchserver.cpp \ src/singletons/accountmanager.cpp \ - src/singletons/channelmanager.cpp \ src/singletons/commandmanager.cpp \ src/singletons/emotemanager.cpp \ src/singletons/fontmanager.cpp \ @@ -171,7 +170,8 @@ SOURCES += \ src/providers/twitch/twitchhelpers.cpp \ src/widgets/helper/signallabel.cpp \ src/widgets/helper/debugpopup.cpp \ - src/util/debugcount.cpp + src/util/debugcount.cpp \ + src/singletons/nativemessagingmanager.cpp HEADERS += \ src/precompiled_header.hpp \ @@ -201,7 +201,6 @@ HEADERS += \ src/providers/twitch/twitchmessagebuilder.hpp \ src/providers/twitch/twitchserver.hpp \ src/singletons/accountmanager.hpp \ - src/singletons/channelmanager.hpp \ src/singletons/commandmanager.hpp \ src/singletons/emotemanager.hpp \ src/singletons/fontmanager.hpp \ @@ -212,7 +211,6 @@ HEADERS += \ src/singletons/loggingmanager.hpp \ src/singletons/pathmanager.hpp \ src/singletons/resourcemanager.hpp \ - src/singletons/settingsmanager.hpp \ src/singletons/thememanager.hpp \ src/singletons/windowmanager.hpp \ src/util/benchmark.hpp \ @@ -288,7 +286,9 @@ HEADERS += \ src/providers/twitch/twitchhelpers.hpp \ src/util/debugcount.hpp \ src/widgets/helper/debugpopup.hpp \ - src/version.hpp + src/version.hpp \ + src/singletons/settingsmanager.hpp \ + src/singletons/nativemessagingmanager.hpp RESOURCES += \ resources/resources.qrc diff --git a/src/application.cpp b/src/application.cpp index fbd8dacb7..f29fbb837 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -4,6 +4,7 @@ #include "singletons/commandmanager.hpp" #include "singletons/emotemanager.hpp" #include "singletons/loggingmanager.hpp" +#include "singletons/nativemessagingmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp" #include "singletons/windowmanager.hpp" @@ -17,6 +18,8 @@ namespace chatterino { Application::Application() { + singletons::NativeMessagingManager::getInstance().registerHost(); + singletons::WindowManager::getInstance(); singletons::LoggingManager::getInstance(); diff --git a/src/main.cpp b/src/main.cpp index a170c00e6..2b6b4ed77 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,11 @@ #include #include +#include #include +#include + +#include #include "util/networkmanager.hpp" @@ -11,7 +15,37 @@ #include "util/nativeeventhelper.hpp" #endif +#include "fstream" + +#ifdef Q_OS_WIN +#include "fcntl.h" +#include "io.h" +#include "stdio.h" +#endif + +void runNativeMessagingHost(); +int runGui(int argc, char *argv[]); + int main(int argc, char *argv[]) +{ + // read args + QStringList args; + + for (int i = 1; i < argc; i++) { + args << argv[i]; + } + + // TODO: can be any argument + if (args.size() > 0 && args[0].startsWith("chrome-extension://")) { + runNativeMessagingHost(); + return 0; + } + + // run gui + return runGui(argc, argv); +} + +int runGui(int argc, char *argv[]) { QApplication::setAttribute(Qt::AA_Use96Dpi, true); #ifdef Q_OS_WIN32 @@ -55,3 +89,44 @@ int main(int argc, char *argv[]) _exit(0); } + +void writeByteArray(QByteArray a) +{ + char *data = a.data(); + uint32_t size; + size = a.size(); + std::cout.write(reinterpret_cast(&size), 4); + std::cout.write(data, a.size()); +} + +void runNativeMessagingHost() +{ +#ifdef Q_OS_WIN + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); +#endif + + // std::ofstream xd("C:\\users\\daniel\\desktop\\xd.lmao"); + + while (true) { + char size_c[4]; + std::cin.read(size_c, 4); + + if (std::cin.eof()) { + break; + } + + uint32_t size = *reinterpret_cast(size_c); + + char *b = (char *)malloc(size + 1); + std::cin.read(b, size); + *(b + size) = '\0'; + + // xd << b; + // xd.flush(); + + free(b); + + // writeByteArray(QString("{\"xd\":1}").toUtf8()); + } +} diff --git a/src/providers/twitch/twitchchannel.cpp b/src/providers/twitch/twitchchannel.cpp index 8daa92d75..cdb16af8a 100644 --- a/src/providers/twitch/twitchchannel.cpp +++ b/src/providers/twitch/twitchchannel.cpp @@ -3,7 +3,6 @@ #include "debug/log.hpp" #include "messages/message.hpp" #include "providers/twitch/twitchmessagebuilder.hpp" -#include "singletons/channelmanager.hpp" #include "singletons/emotemanager.hpp" #include "singletons/ircmanager.hpp" #include "singletons/settingsmanager.hpp" diff --git a/src/singletons/channelmanager.cpp b/src/singletons/channelmanager.cpp deleted file mode 100644 index 45b27988f..000000000 --- a/src/singletons/channelmanager.cpp +++ /dev/null @@ -1,149 +0,0 @@ -//#include "singletons/channelmanager.hpp" -//#include "singletons/ircmanager.hpp" - -// namespace chatterino { -// namespace singletons { - -// ChannelManager &ChannelManager::getInstance() -//{ -// static ChannelManager instance; -// return instance; -//} - -// ChannelManager::ChannelManager() -// : whispersChannel(new Channel("/whispers")) -// , mentionsChannel(new Channel("/mentions")) -// , emptyChannel(new Channel("")) -//{ -//} - -// const std::vector ChannelManager::getItems() -//{ -// QMutexLocker locker(&this->channelsMutex); - -// std::vector items; - -// for (auto &item : this->twitchChannels.values()) { -// items.push_back(std::get<0>(item)); -// } - -// return items; -//} - -// ChannelPtr ChannelManager::addTwitchChannel(const QString &rawChannelName) -//{ -// QString channelName = rawChannelName.toLower(); - -// if (channelName.length() > 1 && channelName.at(0) == '/') { -// return this->getTwitchChannel(channelName); -// } - -// if (channelName.length() > 0 && channelName.at(0) == '#') { -// channelName = channelName.mid(1); -// } - -// QMutexLocker locker(&this->channelsMutex); - -// auto it = this->twitchChannels.find(channelName); - -// if (it == this->twitchChannels.end()) { -// auto channel = std::make_shared(channelName); - -// this->twitchChannels.insert(channelName, std::make_tuple(channel, 1)); - -// this->ircJoin.invoke(channelName); - -// return channel; -// } - -// std::get<1>(it.value())++; - -// return std::get<0>(it.value()); -//} - -// ChannelPtr ChannelManager::getTwitchChannel(const QString &channel) -//{ -// QMutexLocker locker(&this->channelsMutex); - -// QString c = channel.toLower(); - -// if (channel.length() > 1 && channel.at(0) == '/') { -// if (c == "/whispers") { -// return whispersChannel; -// } - -// if (c == "/mentions") { -// return mentionsChannel; -// } - -// return emptyChannel; -// } - -// auto a = this->twitchChannels.find(c); - -// if (a == this->twitchChannels.end()) { -// return emptyChannel; -// } - -// return std::get<0>(a.value()); -//} - -// void ChannelManager::removeTwitchChannel(const QString &channel) -//{ -// QMutexLocker locker(&this->channelsMutex); - -// if (channel.length() > 1 && channel.at(0) == '/') { -// return; -// } - -// QString c = channel.toLower(); - -// auto a = this->twitchChannels.find(c); - -// if (a == this->twitchChannels.end()) { -// return; -// } - -// std::get<1>(a.value())--; - -// if (std::get<1>(a.value()) == 0) { -// this->ircPart.invoke(c); -// this->twitchChannels.remove(c); -// } -//} - -// const std::string &ChannelManager::getUserID(const std::string &username) -//{ -// /* TODO: Implement -// auto it = this->usernameToID.find(username); - -// if (it != std::end(this->usernameToID)) { -// return *it; -// } -// */ - -// static std::string temporary = "xd"; -// return temporary; -//} - -// void ChannelManager::doOnAll(std::function func) -//{ -// for (const auto &channel : this->twitchChannels) { -// func(std::get<0>(channel)); -// } - -// func(this->whispersChannel); -// func(this->mentionsChannel); -//} - -//} - -// void ChannelManager::doOnAllNormalChannels(std::function func) -//{ -// for (const auto &channel : this->twitchChannels) { -// func(std::get<0>(channel)); -// } -//} - -//} // namespace chatterino -//} diff --git a/src/singletons/channelmanager.hpp b/src/singletons/channelmanager.hpp deleted file mode 100644 index ff92f56f8..000000000 --- a/src/singletons/channelmanager.hpp +++ /dev/null @@ -1,46 +0,0 @@ -//#pragma once - -//#include "channel.hpp" -//#include "channeldata.hpp" -//#include "providers/twitch/twitchchannel.hpp" - -//#include - -// namespace chatterino { -// namespace singletons { -// class IrcManager; - -// class ChannelManager -//{ -// explicit ChannelManager(); - -// public: -// static ChannelManager &getInstance(); - -// const std::vector getItems(); - -// const std::string &getUserID(const std::string &username); - -// void doOnAll(std::function func); - -// // Special channels -// const ChannelPtr whispersChannel; -// const ChannelPtr mentionsChannel; -// const ChannelPtr emptyChannel; -// void doOnAll(std::function func); -// void doOnAllNormalChannels(std::function func); - -// private: -// std::map usernameToID; -// std::map channelDatas; - -// QMutex channelsMutex; -// QMap, int>> twitchChannels; - -// pajlada::Signals::Signal ircJoin; -// pajlada::Signals::Signal ircPart; - -// friend class singletons::IrcManager; -//}; -//} // namespace singletons -//} // namespace chatterino diff --git a/src/singletons/commandmanager.cpp b/src/singletons/commandmanager.cpp index 3c561ec38..1691de401 100644 --- a/src/singletons/commandmanager.cpp +++ b/src/singletons/commandmanager.cpp @@ -5,6 +5,7 @@ #include "singletons/accountmanager.hpp" #include "singletons/pathmanager.hpp" +#include #include #include @@ -111,7 +112,13 @@ QString CommandManager::execCommand(const QString &text, ChannelPtr channel, boo auto *twitchChannel = dynamic_cast(channel.get()); if (!dryRun && twitchChannel != nullptr) { - if (commandName == "/uptime") { + if (commandName == "/debug-args") { + QString msg = QApplication::instance()->arguments().join(' '); + + channel->addMessage(messages::Message::createSystemMessage(msg)); + + return ""; + } else if (commandName == "/uptime") { const auto &streamStatus = twitchChannel->GetStreamStatus(); QString messageText = diff --git a/src/singletons/ircmanager.cpp b/src/singletons/ircmanager.cpp index 13e9100f8..f7187fc71 100644 --- a/src/singletons/ircmanager.cpp +++ b/src/singletons/ircmanager.cpp @@ -1,28 +1,28 @@ -#include "singletons/ircmanager.hpp" -#include "channel.hpp" -#include "debug/log.hpp" -#include "messages/messageparseargs.hpp" -#include "providers/twitch/twitchaccount.hpp" -#include "providers/twitch/twitchmessagebuilder.hpp" -#include "singletons/accountmanager.hpp" -#include "singletons/channelmanager.hpp" -#include "singletons/emotemanager.hpp" -#include "singletons/resourcemanager.hpp" -#include "singletons/settingsmanager.hpp" -#include "singletons/windowmanager.hpp" -#include "util/posttothread.hpp" -#include "util/urlfetch.hpp" +//#include "singletons/ircmanager.hpp" +//#include "channel.hpp" +//#include "debug/log.hpp" +//#include "messages/messageparseargs.hpp" +//#include "providers/twitch/twitchaccount.hpp" +//#include "providers/twitch/twitchmessagebuilder.hpp" +//#include "singletons/accountmanager.hpp" +//#include "singletons/channelmanager.hpp" +//#include "singletons/emotemanager.hpp" +//#include "singletons/resourcemanager.hpp" +//#include "singletons/settingsmanager.hpp" +//#include "singletons/windowmanager.hpp" +//#include "util/posttothread.hpp" +//#include "util/urlfetch.hpp" -#include -#include -#include -#include -#include -#include +//#include +//#include +//#include +//#include +//#include +//#include -#include +//#include -using namespace chatterino::messages; +//using namespace chatterino::messages; // void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oauthClient, // const QString &oauthToken) diff --git a/src/singletons/nativemessagingmanager.cpp b/src/singletons/nativemessagingmanager.cpp new file mode 100644 index 000000000..4cad0e9dd --- /dev/null +++ b/src/singletons/nativemessagingmanager.cpp @@ -0,0 +1,115 @@ +#include "nativemessagingmanager.hpp" + +#include "singletons/pathmanager.hpp" + +#include +#include +#include +#include +#include + +//#ifdef USEWINSDK +//#include +//#pragma comment(lib, "Advapi32.lib") +//#endif + +#include + +#ifdef Q_OS_WIN +#include +#endif + +#define EXTENSION_ID "aeicjepmjkgmbeohnchmpfjbpchogmjn" +#define MESSAGE_SIZE 1024 + +namespace ipc = boost::interprocess; + +namespace chatterino { +namespace singletons { + +NativeMessagingManager::NativeMessagingManager() +{ +} + +NativeMessagingManager &NativeMessagingManager::getInstance() +{ + static NativeMessagingManager manager; + return manager; +} + +void NativeMessagingManager::registerHost() +{ + // create manifest + QJsonDocument document; + QJsonObject root_obj; + root_obj.insert("name", "com.chatterino.chatterino"); + root_obj.insert("description", "Browser interaction with chatterino."); + root_obj.insert("path", QCoreApplication::applicationFilePath()); + root_obj.insert("type", "stdio"); + + QJsonArray allowed_origins_arr = {"chrome-extension://aeicjepmjkgmbeohnchmpfjbpchogmjn/"}; + root_obj.insert("allowed_origins", allowed_origins_arr); + + // save the manifest + QString manifestPath = + PathManager::getInstance().settingsFolderPath + "/native-messaging-manifest.json"; + + document.setObject(root_obj); + + QFile file(manifestPath); + file.open(QIODevice::WriteOnly | QIODevice::Truncate); + file.write(document.toJson()); + file.flush(); + +#ifdef Q_OS_WIN + // clang-format off + QProcess::execute("REG ADD \"HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.chatterino.chatterino\" /ve /t REG_SZ /d \"" + manifestPath + "\" /f"); +// clang-format on +#endif +} + +void NativeMessagingManager::openGuiMessageQueue() +{ + static ReceiverThread thread; + + if (thread.isRunning()) { + thread.exit(); + } + + thread.start(); +} + +void NativeMessagingManager::sendToGuiProcess(const QByteArray &array) +{ + ipc::message_queue messageQueue(ipc::open_only, "chatterino_gui"); + + try { + messageQueue.try_send(array.data(), array.size(), 1); + } catch (ipc::interprocess_exception &ex) { + // rip + } +} + +void NativeMessagingManager::ReceiverThread::run() +{ + ipc::message_queue::remove("chatterino_gui"); + + ipc::message_queue messageQueue(ipc::create_only, "chatterino_gui", 100, MESSAGE_SIZE); + + while (true) { + try { + char *buf = (char *)malloc(MESSAGE_SIZE); + ipc::message_queue::size_type retSize; + unsigned int priority; + + messageQueue.receive(buf, MESSAGE_SIZE, retSize, priority); + + QString text = QString::fromUtf8(buf, retSize); + qDebug() << text; + } catch (ipc::interprocess_exception &ex) { + // rip + } + } +} +} // namespace singletons +} // namespace chatterino diff --git a/src/singletons/nativemessagingmanager.hpp b/src/singletons/nativemessagingmanager.hpp new file mode 100644 index 000000000..be010b9ca --- /dev/null +++ b/src/singletons/nativemessagingmanager.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace chatterino { +namespace singletons { + +class NativeMessagingManager +{ + NativeMessagingManager(); + +public: + static NativeMessagingManager &getInstance(); + + class ReceiverThread : public QThread + { + public: + void run() override; + }; + + void registerHost(); + void openGuiMessageQueue(); + void sendToGuiProcess(const QByteArray &array); +}; + +} // namespace singletons +} // namespace chatterino diff --git a/src/util/completionmodel.cpp b/src/util/completionmodel.cpp index a183c04a0..1fa78bcf5 100644 --- a/src/util/completionmodel.cpp +++ b/src/util/completionmodel.cpp @@ -2,7 +2,6 @@ #include "common.hpp" #include "debug/log.hpp" -#include "singletons/channelmanager.hpp" #include "singletons/emotemanager.hpp" #include diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 07999172d..0ebe9f55c 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -4,7 +4,6 @@ #include "messages/limitedqueuesnapshot.hpp" #include "messages/message.hpp" #include "providers/twitch/twitchserver.hpp" -#include "singletons/channelmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp" #include "singletons/windowmanager.hpp" diff --git a/src/widgets/split.cpp b/src/widgets/split.cpp index 0e9061817..371221cee 100644 --- a/src/widgets/split.cpp +++ b/src/widgets/split.cpp @@ -3,7 +3,6 @@ #include "providers/twitch/twitchchannel.hpp" #include "providers/twitch/twitchmessagebuilder.hpp" #include "providers/twitch/twitchserver.hpp" -#include "singletons/channelmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp" #include "singletons/windowmanager.hpp" diff --git a/src/widgets/window.cpp b/src/widgets/window.cpp index 837555840..439bd3df3 100644 --- a/src/widgets/window.cpp +++ b/src/widgets/window.cpp @@ -1,6 +1,5 @@ #include "widgets/window.hpp" #include "singletons/accountmanager.hpp" -#include "singletons/channelmanager.hpp" #include "singletons/ircmanager.hpp" #include "singletons/settingsmanager.hpp" #include "singletons/thememanager.hpp"