#include "../include/Channel.hpp" #include #include #include #include using namespace std; namespace dchat { static Channel *currentChannel; Channel::Channel(const string &_name, const odhtdb::DatabaseNode &_databaseNodeInfo, User *_localUser, odhtdb::Database *_database) : database(_database), databaseNodeInfo(_databaseNodeInfo), name(_name), messageBoard(this), localUser(_localUser ? _localUser : new OfflineUser("You")) { bridgeServices.push_back(new DiscordService()); addUserLocally(localUser); //addLocalMessage(u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif) deaf [emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)", &systemUser, 0, odhtdb::Hash()); //addLocalMessage(u8"[emoji](https://discordemoji.com/assets/emoji/PepeDab.gif)[emoji](https://discordemoji.com/assets/emoji/COGGERS.gif)", &systemUser, 0, odhtdb::Hash()); //addLocalMessage(u8"pepedab https://discordemoji.com/assets/emoji/PepeDab.gif coggers https://discordemoji.com/assets/emoji/COGGERS.gif check out this url http://www.grandtournation.com/6808/start-date-of-the-grand-tour-season-3-confirmed-mark-your-calendars/ owo", &systemUser, 0, odhtdb::Hash()); //addLocalMessage(u8"ht clic", &systemUser, 0, odhtdb::Hash()); auto binds = Chatbar::getBinds(); vector suggestionsStr; suggestionsStr.reserve(binds.size()); for(auto &bind : binds) { string suggestion = bind.first; suggestion += " "; suggestion += bind.second; suggestionsStr.emplace_back(move(suggestion)); } suggestions.show(suggestionsStr); if(database) { database->seed(databaseNodeInfo, odhtdb::DatabaseFetchOrder::NEWEST_FIRST); pingKey = odhtdb::DhtKey(*databaseNodeInfo.getRequestHash()).getPingKey(); // TODO: Ban peers that spam this key (take in account that creator of packets can be forged) pingListener = database->receiveCustomMessage(pingKey, [this](const void *data, usize size) { sibs::SafeSerializer result; try { sibs::SafeDeserializer deserializer((const u8*)data, size); u8 userPublicKeyRaw[odhtdb::PUBLIC_KEY_NUM_BYTES]; deserializer.extract(userPublicKeyRaw, odhtdb::PUBLIC_KEY_NUM_BYTES); odhtdb::Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, odhtdb::PUBLIC_KEY_NUM_BYTES); auto user = getUserByPublicKey(userPublicKey); if(!user) { // TODO: Ban peer if this happens too often return result; } string unsignedData = userPublicKey.unsign(odhtdb::DataView((void*)deserializer.getBuffer(), deserializer.getSize())); sibs::SafeDeserializer unsignedDeserializer((const u8*)unsignedData.data(), unsignedData.size()); u32 pingTimestampSec = unsignedDeserializer.extract(); if(pingTimestampSec > user->pingTimestampSec) { user->pingTimestampSec = pingTimestampSec; } } catch(std::exception &e) { fprintf(stderr, "Failed while deseralizing ping\n"); } return result; }); sendPing(database->getSyncedTimestampUtc().seconds); } } Channel::~Channel() { for(BridgeService *bridgeService : bridgeServices) { delete bridgeService; } if(database) { database->cancelNodeListener(pingListener); database->stopSeeding(*databaseNodeInfo.getRequestHash()); sendPing(0); } for(User *user : users) { delete user; } for(auto &discordUserIt : discordUserById) { delete discordUserIt.second; } } User* Channel::getLocalUser() { return localUser; } SystemUser* Channel::getSystemUser() { return &systemUser; } MessageBoard& Channel::getMessageBoard() { return messageBoard; } const string& Channel::getName() const { return name; } const vector& Channel::getUsers() const { return users; } OnlineUser* Channel::getUserByPublicKey(const odhtdb::Signature::PublicKey &publicKey) { auto userIt = publicKeyOnlineUsersMap.find(publicKey); if(userIt != publicKeyOnlineUsersMap.end()) return userIt->second; return nullptr; } const odhtdb::DatabaseNode& Channel::getNodeInfo() const { return databaseNodeInfo; } Message* Channel::getLatestMessage() { return messageBoard.getLatestMessage(); } void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds) { addLocalMessage(msg, owner, timestampSeconds, odhtdb::Hash()); } void Channel::addLocalMessage(const string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id) { assert(owner); if(timestampSeconds == 0) { timestampSeconds = time(NULL); } messageBoard.addMessage(new Message(owner, msg, timestampSeconds), id); } void Channel::addLocalDiscordMessage(const string &discordUserName, u64 discordUserId, const string &msg, User *owner, u64 timestampSeconds, const odhtdb::Hash &id) { assert(owner); if(timestampSeconds == 0) { timestampSeconds = time(NULL); } OnlineDiscordUser *discordUser = nullptr; auto discordUserIt = discordUserById.find(discordUserId); if(discordUserIt == discordUserById.end()) { discordUser = new OnlineDiscordUser(discordUserName, discordUserId, owner); discordUserById[discordUserId] = discordUser; } else { // TODO: What if several users bridge same chat? the same discord user id could belong to different owners. // Dchat channels should only allow one user to bridge data from one discord channel. Bridging data between multiple discord channels to // one dchat channel should be allowed. discordUser = discordUserIt->second; } messageBoard.addMessage(new Message(discordUser, msg, timestampSeconds), id); } void Channel::addSystemMessage(const string &msg, bool plainText) { u64 timestampSeconds = time(NULL); messageBoard.addMessage(new Message(&systemUser, msg, timestampSeconds, plainText), odhtdb::Hash()); } void Channel::addMessage(const string &msg) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::ADD_MESSAGE); serializer.add((const u8*)msg.data(), msg.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } else addLocalMessage(msg, localUser, 0, odhtdb::Hash()); } void Channel::addDiscordMessage(const string &discordUserName, u64 discordUserId, const string &msg) { assert(database && localUser->type == User::Type::ONLINE_LOCAL_USER); auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::ADD_DISCORD_MESSAGE); serializer.add(discordUserId); serializer.add((u8)discordUserName.size()); serializer.add((const u8*)discordUserName.data(), discordUserName.size()); serializer.add((const u8*)msg.data(), msg.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } void Channel::deleteLocalMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser) { messageBoard.deleteMessage(id, requestedByUser); } void Channel::deleteMessage(const odhtdb::Hash &id, const odhtdb::Signature::PublicKey &requestedByUser) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::DELETE_MESSAGE); serializer.add((const u8*)id.getData(), odhtdb::HASH_BYTE_SIZE); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } else deleteLocalMessage(id, requestedByUser); } void Channel::addUserLocally(User *user) { users.push_back(user); user->avatarUrl = "https://discordemoji.com/assets/emoji/HanekawaSmug.gif"; if(user->isOnlineUser()) { auto onlineUser = static_cast(user); publicKeyOnlineUsersMap[onlineUser->getPublicKey()] = onlineUser; } } bool Channel::addUser(const odhtdb::Signature::PublicKey &userId, const odhtdb::DataView &groupId) { assert(database); if(!database || localUser->type != User::Type::ONLINE_LOCAL_USER) return false; if(groupId.size != odhtdb::GROUP_ID_LENGTH) { fprintf(stderr, "Group id is wrong size. Expected to be %u bytes, was %u byte(s)\n", odhtdb::GROUP_ID_LENGTH, groupId.size); return false; } auto onlineLocalUser = static_cast(localUser); try { database->addUser(databaseNodeInfo, onlineLocalUser->keyPair, userId, groupId); return true; } catch(std::exception &e) { fprintf(stderr, "Group with id %s does not exist in channel %s or you do not have permission to add user to that group\nError: %s\n", odhtdb::bin2hex((const char*)groupId.data, groupId.size).c_str(), databaseNodeInfo.getRequestHash()->toString().c_str(), e.what()); return false; } } void Channel::replaceLocalUser(OnlineLocalUser *newOnlineLocalUser) { for(vector::iterator it = users.begin(); it != users.end(); ++it) { if(*it == localUser) { users.erase(it); delete localUser; break; } } localUser = newOnlineLocalUser; addUserLocally(newOnlineLocalUser); } void Channel::changeNick(const string &newNick) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::NICKNAME_CHANGE); serializer.add((u8)newNick.size()); serializer.add((const u8*)newNick.data(), newNick.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } } void Channel::setAvatar(const string &newAvatarUrl) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::CHANGE_AVATAR); serializer.add((u16)newAvatarUrl.size()); serializer.add((const u8*)newAvatarUrl.data(), newAvatarUrl.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } } void Channel::setNameLocally(const string &name) { this->name = name; } void Channel::setName(const string &name) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add(ChannelDataType::CHANGE_CHANNEL_NAME); serializer.add((u16)name.size()); serializer.add((const u8*)name.data(), name.size()); database->addData(databaseNodeInfo, onlineLocalUser->keyPair, odhtdb::DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); } } int Channel::getUserLowestPermissionLevel(OnlineUser *user) const { if(!database) return -1; return database->getUserLowestPermissionLevel(*databaseNodeInfo.getRequestHash(), user->getPublicKey()); } void Channel::processEvent(const sf::Event &event, Cache &cache) { chatbar.processEvent(event, cache, this); messageBoard.processEvent(event, cache); } void Channel::draw(sf::RenderWindow &window, Cache &cache) { messageBoard.draw(window, cache); chatbar.draw(window, cache); //suggestions.draw(window, cache); } void Channel::update() { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER && pingTimer.getElapsedTime().asMilliseconds() > 5000) { pingTimer.restart(); sendPing(database->getSyncedTimestampUtc().seconds); } } void Channel::sendPing(u32 pingTimestampSec) { if(database && localUser->type == User::Type::ONLINE_LOCAL_USER) { //printf("Sending ping, counter: %u\n", pingCounter); auto onlineLocalUser = static_cast(localUser); sibs::SafeSerializer serializer; serializer.add((const u8*)onlineLocalUser->getPublicKey().getData(), onlineLocalUser->getPublicKey().getSize()); sibs::SafeSerializer signedSerializer; signedSerializer.add(pingTimestampSec); string signedData = onlineLocalUser->keyPair.getPrivateKey().sign(odhtdb::DataView(signedSerializer.getBuffer().data(), signedSerializer.getBuffer().size())); serializer.add((const u8*)signedData.data(), signedData.size()); database->sendCustomMessage(pingKey, serializer.getBuffer().data(), serializer.getBuffer().size()); } } u32 Channel::getSyncedTimestampUtcInSec() { if(!database) return 0; return database->getSyncedTimestampUtc().seconds; } const vector& Channel::getBridgeServices() const { return bridgeServices; } DiscordService* Channel::getDiscordService() { return (DiscordService*)bridgeServices[0]; } void Channel::setCurrent(Channel *channel) { currentChannel = channel; } Channel* Channel::getCurrent() { return currentChannel; } }