diff --git a/include/pro/pro.hpp b/include/pro/pro.hpp new file mode 100644 index 0000000..4336bce --- /dev/null +++ b/include/pro/pro.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include "../utilities.hpp" +#include "oxen/log.hpp" +#include "pro/types.hpp" +#include "session/attachments.hpp" +#include "session/config/user_profile.hpp" +#include "session/random.hpp" +#include "session/session_protocol.hpp" + +namespace session::nodeapi { + +class ProWrapper : public Napi::ObjectWrap { + public: + ProWrapper(const Napi::CallbackInfo& info) : Napi::ObjectWrap{info} { + throw std::invalid_argument("ProWrapper is static and doesn't need to be constructed"); + } + + static void Init(Napi::Env env, Napi::Object exports) { + MetaBaseWrapper::NoBaseClassInitHelper( + env, + exports, + "ProWrapperNode", + { + // Pro features + StaticMethod<&ProWrapper::proFeaturesForMessage>( + "proFeaturesForMessage", + static_cast( + napi_writable | napi_configurable)), + }); + } + + private: + static Napi::Value proFeaturesForMessage(const Napi::CallbackInfo& info) { + return wrapResult(info, [&] { + // we expect two arguments that match: + // first: { + // "utf16": string, + // "proFeatures": Array, + // } + + assertInfoLength(info, 1); + assertIsObject(info[0]); + + auto first = info[0].As(); + + if (first.IsEmpty()) + throw std::invalid_argument("proFeaturesForMessage first received empty"); + + assertIsArray(first.Get("proFeatures"), "proFeaturesForMessage.proFeatures"); + auto proFeaturesJS = first.Get("proFeatures").As(); + std::vector proFeatures; + proFeatures.reserve(proFeaturesJS.Length()); + for (uint32_t i = 0; i < proFeaturesJS.Length(); i++) { + auto itemValue = proFeaturesJS.Get(i); + assertIsString(itemValue, "proFeaturesForMessage.proFeatures.itemValue"); + std::string item = + toCppString(itemValue, "proFeaturesForMessage.proFeatures.itemValue"); + proFeatures.push_back(item); + } + + SESSION_PROTOCOL_PRO_EXTRA_FEATURES flags; + for (std::string& feature : proFeatures) { + if (feature == "10K_CHARACTER_LIMIT") { + flags |= SESSION_PROTOCOL_PRO_FEATURES_10K_CHARACTER_LIMIT; + } else if (feature == "PRO_BADGE") { + flags |= SESSION_PROTOCOL_PRO_FEATURES_PRO_BADGE; + } else if (feature == "ANIMATED_AVATAR") { + flags |= SESSION_PROTOCOL_PRO_FEATURES_ANIMATED_AVATAR; + } + } + assertIsString(first.Get("utf16"), "proFeaturesForMessage.utf16"); + std::u16string utf16 = first.Get("utf16").As().Utf16Value(); + return session::pro_features_for_utf16((utf16.data()), utf16.length(), flags); + }); + }; +}; +}; // namespace session::nodeapi diff --git a/include/pro/types.hpp b/include/pro/types.hpp index c31875b..6b09df7 100644 --- a/include/pro/types.hpp +++ b/include/pro/types.hpp @@ -116,4 +116,19 @@ struct toJs_impl { } }; +template <> +struct toJs_impl { + Napi::Object operator()( + const Napi::Env& env, const session::ProFeaturesForMsg pro_features_msg) { + auto obj = Napi::Object::New(env); + + obj["success"] = toJs(env, pro_features_msg.success); + obj["error"] = toJs(env, pro_features_msg.error); + obj["codepointCount"] = toJs(env, pro_features_msg.codepoint_count); + obj["proFeatures"] = toJs(env, pro_features_msg.features); + + return obj; + } +}; + }; // namespace session::nodeapi \ No newline at end of file diff --git a/libsession-util b/libsession-util index 9023fcb..33c6d71 160000 --- a/libsession-util +++ b/libsession-util @@ -1 +1 @@ -Subproject commit 9023fcbf55d7faec6c45e7c636d097afeac4729f +Subproject commit 33c6d71d071f8df10a44baabca045d7774b4a105 diff --git a/src/addon.cpp b/src/addon.cpp index f5ead93..4d38671 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -8,6 +8,7 @@ #include "convo_info_volatile_config.hpp" #include "groups/meta_group_wrapper.hpp" #include "multi_encrypt/multi_encrypt.hpp" +#include "pro/pro.hpp" #include "user_config.hpp" #include "user_groups_config.hpp" @@ -52,6 +53,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) { // Fully static wrappers init session::nodeapi::MultiEncryptWrapper::Init(env, exports); + session::nodeapi::ProWrapper::Init(env, exports); session::nodeapi::BlindingWrapper::Init(env, exports); return exports; diff --git a/types/index.d.ts b/types/index.d.ts index 2762af0..75303fd 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2,5 +2,6 @@ /// /// /// +/// /// /// diff --git a/types/pro/pro.d.ts b/types/pro/pro.d.ts index f417716..f560303 100644 --- a/types/pro/pro.d.ts +++ b/types/pro/pro.d.ts @@ -64,4 +64,32 @@ declare module 'libsession_util_nodejs' { pro_access_not_found: string; support_url: string; }; + + type ProWrapper = { + proFeaturesForMessage: (args: { + utf16: string; + /** + * If the utf16 requires 10K_CHARACTER_LIMIT to be set, it will be set in the return. + * If provided (here) as an input, it will be ignored. + */ + proFeatures: ProFeatures; + }) => WithProFeatures & { success: boolean; error: string | null; codePointCount: number }; + }; + + export type ProActionsCalls = MakeWrapperActionCalls; + + + /** + * To be used inside the web worker only (calls are synchronous and won't work asynchronously) + */ + export class ProWrapperNode { + public static proFeaturesForMessage: ProWrapper['proFeaturesForMessage']; + } + + /** + * Those actions are used internally for the web worker communication. + * You should never need to import them in Session directly + * You will need to add an entry here if you add a new function + */ + export type ProActionsType = MakeActionCall; }