diff --git a/.gitignore b/.gitignore index cd9fb3fcc..2eb9aa0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -287,5 +287,4 @@ buck-out/ / packages/restapi/yarn.lock yarn.lock -packages/restapi/tests/.env -packages/restapi/.env +*.env diff --git a/package.json b/package.json index 0cf5d27c8..8f82ca243 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "mocha": "^10.2.0", "mocha-typescript": "^1.1.17", "moment": "^2.29.4", + "openpgp": "^5.8.0", "react": "17.0.2", "react-dom": "17.0.2", "react-is": "17.0.2", @@ -47,6 +48,7 @@ "tiny-invariant": "^1.2.0", "ts-mocha": "^10.0.0", "tslib": "^2.3.0", + "unique-names-generator": "^4.7.1", "uuid": "^9.0.0" }, "devDependencies": { @@ -58,6 +60,7 @@ "@nrwl/jest": "14.1.7", "@nrwl/js": "14.1.7", "@nrwl/linter": "14.1.7", + "@nrwl/node": "14.1.7", "@nrwl/react": "14.1.7", "@nrwl/react-native": "14.1.7", "@nrwl/web": "14.1.7", diff --git a/packages/examples/sdk-backend-node/.env.sample b/packages/examples/sdk-backend-node/.env.sample new file mode 100644 index 000000000..ab1cd1e64 --- /dev/null +++ b/packages/examples/sdk-backend-node/.env.sample @@ -0,0 +1,38 @@ +# WALLET PRIVATE KEY +WALLET_PRIVATE_KEY=your_channel_key + +# ENVIRONMENT OF PUSH NETWORK - prod, staging OR dev +PUSH_NODE_NETWORK=staging + +# TO ENABLE API RESPONSES TO BE SHOWN OR HIDDEN - true OR false +SHOW_API_RESPONSE=false + +# NFT CONTRACT ADDRESS +NFT_CONTRACT_ADDRESS_1=your_nft_contract_address + +# NFT CHAIN ID +NFT_CHAIN_ID_1=your_nft_chain_id + +# NFT TOKEN ID +NFT_TOKEN_ID_1=your_nft_token_id + +# NFT HOLDER WALLET PRIVATE KEY +NFT_HOLDER_WALLET_PRIVATE_KEY_1=your_nft_holder_wallet_private_key + +# NFT PROFILE WALLET PASSWORD +NFT_PROFILE_PASSWORD_1=your_nft_profile_password + +# NFT CONTRACT ADDRESS +NFT_CONTRACT_ADDRESS_2=your_nft_contract_address + +# NFT CHAIN ID +NFT_CHAIN_ID_2=your_nft_chain_id + +# NFT TOKEN ID +NFT_TOKEN_ID_2=your_nft_token_id + +# NFT HOLDER WALLET PRIVATE KEY +NFT_HOLDER_WALLET_PRIVATE_KEY_2=your_nft_holder_wallet_private_key + +# NFT PROFILE WALLET PASSWORD +NFT_PROFILE_PASSWORD_2=your_nft_profile_password \ No newline at end of file diff --git a/packages/examples/sdk-backend-node/.eslintrc.json b/packages/examples/sdk-backend-node/.eslintrc.json new file mode 100644 index 000000000..3456be9b9 --- /dev/null +++ b/packages/examples/sdk-backend-node/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/examples/sdk-backend-node/jest.config.ts b/packages/examples/sdk-backend-node/jest.config.ts new file mode 100644 index 000000000..67e5ce7a4 --- /dev/null +++ b/packages/examples/sdk-backend-node/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'examples-sdk-backend-node', + preset: '../../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../../coverage/packages/examples/sdk-backend-node', +}; diff --git a/packages/examples/sdk-backend-node/project.json b/packages/examples/sdk-backend-node/project.json new file mode 100644 index 000000000..9a2dcabd9 --- /dev/null +++ b/packages/examples/sdk-backend-node/project.json @@ -0,0 +1,52 @@ +{ + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/examples/sdk-backend-node/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/node:webpack", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/examples/sdk-backend-node", + "main": "packages/examples/sdk-backend-node/src/main.ts", + "tsConfig": "packages/examples/sdk-backend-node/tsconfig.app.json", + "assets": ["packages/examples/sdk-backend-node/src/assets"] + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false, + "fileReplacements": [ + { + "replace": "packages/examples/sdk-backend-node/src/environments/environment.ts", + "with": "packages/examples/sdk-backend-node/src/environments/environment.prod.ts" + } + ] + } + } + }, + "serve": { + "executor": "@nrwl/node:node", + "options": { + "buildTarget": "examples-sdk-backend-node:build" + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/examples/sdk-backend-node/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/packages/examples/sdk-backend-node"], + "options": { + "jestConfig": "packages/examples/sdk-backend-node/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": [] +} diff --git a/packages/examples/sdk-backend-node/src/app/.gitkeep b/packages/examples/sdk-backend-node/src/app/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/examples/sdk-backend-node/src/assets/.gitkeep b/packages/examples/sdk-backend-node/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/examples/sdk-backend-node/src/environments/environment.prod.ts b/packages/examples/sdk-backend-node/src/environments/environment.prod.ts new file mode 100644 index 000000000..c9669790b --- /dev/null +++ b/packages/examples/sdk-backend-node/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/packages/examples/sdk-backend-node/src/environments/environment.ts b/packages/examples/sdk-backend-node/src/environments/environment.ts new file mode 100644 index 000000000..a20cfe557 --- /dev/null +++ b/packages/examples/sdk-backend-node/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +}; diff --git a/packages/examples/sdk-backend-node/src/main.ts b/packages/examples/sdk-backend-node/src/main.ts new file mode 100644 index 000000000..d50a24ebe --- /dev/null +++ b/packages/examples/sdk-backend-node/src/main.ts @@ -0,0 +1,1544 @@ +import * as PushAPI from '@pushprotocol/restapi'; +import { createSocketConnection, EVENTS } from '@pushprotocol/socket'; +import * as dotenv from 'dotenv'; +import { ethers } from 'ethers'; +import { + adjectives, + animals, + colors, + uniqueNamesGenerator, +} from 'unique-names-generator'; +dotenv.config(); + +enum ENV { + PROD = 'prod', + STAGING = 'staging', + DEV = 'dev', + /** + * **This is for local development only** + */ + LOCAL = 'local', +} + +// CONFIGS +const env = process.env.PUSH_NODE_NETWORK; // choose ENV.STAGING or ENV.PROD +const showAPIResponse = process.env.SHOW_API_RESPONSE === 'true' ? true : false; // choose to show or hide API responses + +// If you own a channel, you can use your channel address as well +const channelPrivateKey: string = process.env.WALLET_PRIVATE_KEY!; +const signerChannel = new ethers.Wallet(`0x${channelPrivateKey}`); +const channelAddress = signerChannel.address; + +// Addresses that will be used to +const signer = ethers.Wallet.createRandom(); +const signerSecondAccount = ethers.Wallet.createRandom(); + +// generate some dummy wallets as well +const randomWallet1 = ethers.Wallet.createRandom().address; +const randomWallet2 = ethers.Wallet.createRandom().address; +const randomWallet3 = ethers.Wallet.createRandom().address; + +// Group Chat Data +const groupName = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); +const groupDescription = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); +const groupImage = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg=='; + +// NFT Chat Data +const nftChainId1 = process.env.NFT_CHAIN_ID_1; +const nftContractAddress1 = process.env.NFT_CONTRACT_ADDRESS_1; +const nftTokenId1 = process.env.NFT_TOKEN_ID_1; +const nftHolderWalletPrivatekey1 = process.env.NFT_HOLDER_WALLET_PRIVATE_KEY_1; +const nftSigner1 = new ethers.Wallet(`0x${nftHolderWalletPrivatekey1}`); +const nftAccount1 = `nft:eip155:${nftChainId1}:${nftContractAddress1}:${nftTokenId1}`; +const nftProfilePassword1 = process.env.NFT_PROFILE_PASSWORD_1; +const nftChainId2 = process.env.NFT_CHAIN_ID_2; +const nftContractAddress2 = process.env.NFT_CONTRACT_ADDRESS_2; +const nftTokenId2 = process.env.NFT_TOKEN_ID_2; +const nftHolderWalletPrivatekey2 = process.env.NFT_HOLDER_WALLET_PRIVATE_KEY_2; +const nftSigner2 = new ethers.Wallet(`0x${nftHolderWalletPrivatekey2}`); +const nftAccount2 = `nft:eip155:${nftChainId2}:${nftContractAddress2}:${nftTokenId2}`; +const nftProfilePassword2 = process.env.NFT_PROFILE_PASSWORD_2; +const nftAccount3 = `nft:eip155:${nftChainId2}:${nftContractAddress2}:10`; + +// NFT Group Chat Data +const nftGroupName = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); +const updatedNftGroupName = uniqueNamesGenerator({ + dictionaries: [adjectives, colors, animals], +}); + +// Push Notification - Run Notifications Use cases +async function runNotificaitonsUseCases() { + console.log(` + ███ ██ ██████ ████████ ██ ███████ ██ ██████ █████ ████████ ██ ██████ ███ ██ ███████ + ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ + ██ ██ ██ ██ ██ ██ ██ █████ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ███████ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ██ ████ ██████ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██████ ██ ████ ███████ +`); + console.log('PushAPI.user.getFeeds'); + await PushAPI_user_getFeeds(); + + console.log('PushAPI.user.getFeeds [Spam]'); + await PushAPI_user_getFeeds__spam(); + + console.log('PushAPI.user.getSubscriptions'); + await PushAPI_user_getSubscriptions(); + + console.log('PushAPI.channels.getChannel()'); + await PushAPI_channels_getChannel(); + + console.log('PushAPI.channels.search()'); + await PushAPI_channels_search(); + + console.log('PushAPI.channels.subscribe()'); + await PushAPI_channels_subscribe(); + + console.log('PushAPI.channels.unsubscribe()'); + await PushAPI_channels_unsubscribe(); + + // IMPORTANT: VARIOUS OTHER NOTIFICATIONS FORMAT SUPPORTED + // EXAMPLES HERE: https://github.com/ethereum-push-notification-service/push-sdk/blob/main/packages/restapi/README.md + console.log( + 'PushAPI.payloads.sendNotification() [Direct Payload, Single Recipient]' + ); + await PushAPI_payloads_sendNotification__direct_payload_single_recipient(); + + console.log( + 'PushAPI.payloads.sendNotification() [Direct Payload, Batch of Recipients (Subset)]' + ); + await PushAPI_payloads_sendNotification__direct_payload_group_of_recipient_subset(); + + console.log( + 'PushAPI.payloads.sendNotification() [Direct Payload, All Recipients (Broadcast)]' + ); + await PushAPI_payloads_sendNotification__direct_payload_all_recipients_brodcast(); + + console.log('PushAPI.channels._getSubscribers()'); + await PushAPI_channels_getSubscribers(); + + console.log('Push Notification - PushSDKSocket()'); + await PushSDKSocket(); +} + +// Push Notification - PushAPI.user.getFeeds +async function PushAPI_user_getFeeds(silent = !showAPIResponse) { + const notifications = await PushAPI.user.getFeeds({ + user: `eip155:5:${signer.address}`, // user address in CAIP + env: env as ENV, + }); + + console.log('PushAPI.user.getFeeds | Response - 200 OK'); + if (!silent) { + console.log(notifications); + } +} + +// Push Notification - PushAPI.user.getFeeds - Spam +async function PushAPI_user_getFeeds__spam(silent = !showAPIResponse) { + const notifications = await PushAPI.user.getFeeds({ + user: `eip155:5:${signer.address}`, // user address in CAIP + spam: true, + env: env as ENV, + }); + + console.log('PushAPI.user.getFeeds [Spam] | Response - 200 OK'); + if (!silent) { + console.log(notifications); + } +} + +// Push Notification - PushAPI.user.getSubscriptions +async function PushAPI_user_getSubscriptions(silent = !showAPIResponse) { + const subscriptions = await PushAPI.user.getSubscriptions({ + user: `eip155:5:${signer.address}`, // user address in CAIP + env: env as ENV, + }); + + console.log('PushAPI.user.getSubscriptions | Response - 200 OK'); + if (!silent) { + console.log(subscriptions); + } +} + +// Push Notification - PushAPI.channels.getChannel +async function PushAPI_channels_getChannel(silent = !showAPIResponse) { + const channelData = await PushAPI.channels.getChannel({ + channel: channelAddress, + env: env as ENV, + }); + + console.log('PushAPI.channels.getChannel | Response - 200 OK'); + if (!silent) { + console.log(channelData); + } +} + +// Push Notification - PushAPI.channels.search +async function PushAPI_channels_search(silent = !showAPIResponse) { + const channelsData = await PushAPI.channels.search({ + query: 'push', // a search query + page: 1, // page index + limit: 20, // no of items per page + env: env as ENV, + }); + + console.log('PushAPI.channels.search | Response - 200 OK'); + if (!silent) { + console.log(channelsData); + } +} + +// Push Notification - PushAPI.channels.subscribe +async function PushAPI_channels_subscribe(silent = !showAPIResponse) { + const response = await PushAPI.channels.subscribe({ + signer: signer, + channelAddress: `eip155:5:${channelAddress}`, // channel address in CAIP + userAddress: `eip155:5:${signer.address}`, // user address in CAIP + onSuccess: () => { + console.log('opt in success'); + }, + onError: () => { + console.error('opt in error'); + }, + env: env as ENV, + }); + + console.log('PushAPI.channels.subscribe | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Notification - PushAPI.channels.unsubscribe +async function PushAPI_channels_unsubscribe(silent = !showAPIResponse) { + const response = await PushAPI.channels.unsubscribe({ + signer: signer, + channelAddress: `eip155:5:${channelAddress}`, // channel address in CAIP + userAddress: `eip155:5:${signer.address}`, // user address in CAIP + onSuccess: () => { + console.log('opt out success'); + }, + onError: () => { + console.error('opt out error'); + }, + env: env as ENV, + }); + + console.log('PushAPI.channels.unsubscribe | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Notification - Send Notifications +// Direct payload for single recipient(target) +// PushAPI.payloads.sendNotification +async function PushAPI_payloads_sendNotification__direct_payload_single_recipient( + silent = !showAPIResponse +) { + const apiResponse = await PushAPI.payloads.sendNotification({ + signer: signerChannel, // Need to resolve to channel address + type: 3, // target + identityType: 2, // direct payload + notification: { + title: `notification TITLE:`, + body: `notification BODY`, + }, + payload: { + title: `payload title`, + body: `sample msg body`, + cta: '', + img: '', + }, + recipients: `eip155:5:${signer.address}`, // recipient address + channel: `eip155:5:${signerChannel.address}`, // your channel address + env: env as ENV, + }); + + console.log('PushAPI.payloads.sendNotification | Response - 204 OK'); + if (!silent) { + console.log(apiResponse); + } +} + +// Push Notification - Direct payload for group of recipients(subset) +// PushAPI.payloads.sendNotification +async function PushAPI_payloads_sendNotification__direct_payload_group_of_recipient_subset( + silent = !showAPIResponse +) { + const apiResponse = await PushAPI.payloads.sendNotification({ + signer: signerChannel, // Need to resolve to channel address + type: 4, // subset + identityType: 2, // direct payload + notification: { + title: `notification TITLE:`, + body: `notification BODY`, + }, + payload: { + title: `payload title`, + body: `sample msg body`, + cta: '', + img: '', + }, + recipients: [ + `eip155:5:${signer.address}`, + `eip155:5:${signerSecondAccount.address}`, + ], // recipient addresses + channel: `eip155:5:${signerChannel.address}`, // your channel address + env: env as ENV, + }); + + console.log('PushAPI.payloads.sendNotification | Response - 204 OK'); + if (!silent) { + console.log(apiResponse); + } +} + +// Push Notification - Direct payload for all recipients(broadcast) +// PushAPI.payloads.sendNotification +async function PushAPI_payloads_sendNotification__direct_payload_all_recipients_brodcast( + silent = !showAPIResponse +) { + const apiResponse = await PushAPI.payloads.sendNotification({ + signer: signerChannel, // Needs to resolve to channel address + type: 1, // broadcast + identityType: 2, // direct payload + notification: { + title: `notification TITLE:`, + body: `notification BODY`, + }, + payload: { + title: `payload title`, + body: `sample msg body`, + cta: '', + img: '', + }, + channel: `eip155:5:${signerChannel.address}`, // your channel address + env: env as ENV, + }); + + console.log('PushAPI.payloads.sendNotification | Response - 204 OK'); + if (!silent) { + console.log(apiResponse); + } +} + +// Push Notification - Get Subscribers list from channels (DEPRECATED) +async function PushAPI_channels_getSubscribers(silent = !showAPIResponse) { + const subscribers = await PushAPI.channels._getSubscribers({ + channel: `eip155:5:${channelAddress}`, // channel address in CAIP + env: env as ENV, + }); + + console.log('PushAPI.channels._getSubscribers | Response - 200 OK'); + if (!silent) { + console.log(subscribers); + } +} + +// Push Notification - Socket Connection +async function PushSDKSocket(silent = !showAPIResponse) { + const pushSDKSocket = createSocketConnection({ + user: `eip155:5:${signer.address}`, // CAIP, see below + socketOptions: { autoConnect: false }, + env: env as ENV, + }); + + if (!pushSDKSocket) { + throw new Error('PushSDKSocket | Socket Connection Failed'); + } + + pushSDKSocket.connect(); + + pushSDKSocket.on(EVENTS.CONNECT, async () => { + console.log('Socket Connected - will disconnect after 4 seconds'); + + // send a notification to see the result + await PushAPI_payloads_sendNotification__direct_payload_single_recipient( + true + ); + }); + + pushSDKSocket.on(EVENTS.DISCONNECT, () => { + console.log('Socket Disconnected'); + }); + + pushSDKSocket.on(EVENTS.USER_FEEDS, (feedItem) => { + // feedItem is the notification data when that notification was received + console.log('Incoming Feed from Socket'); + if (!silent) { + console.log(feedItem); + } + + // disconnect socket after this, not to be done in real implementations + pushSDKSocket.disconnect(); + }); + + const delay = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); + await delay(4000); +} + +// Push Chat - Run Chat Use cases +async function runChatUseCases() { + console.log(` + ██████ ██ ██ █████ ████████ + ██ ██ ██ ██ ██ ██ + ██ ███████ ███████ ██ + ██ ██ ██ ██ ██ ██ + ██████ ██ ██ ██ ██ ██ +`); + console.log('PushAPI.user.create'); + await PushAPI_user_create(); + + console.log('PushAPI.user.get'); + await PushAPI_user_get(); + + console.log('PushAPI_chat_decryptPGPKey'); + await PushAPI_chat_decryptPGPKey(); + + console.log('PushAPI.chat.chats'); + await PushAPI_chat_chats(); + + console.log('PushAPI.chat.requests'); + await PushAPI_chat_requests(); + + console.log('PushAPI.chat.send'); + await PushAPI_chat_send(); + + console.log('PushAPI.chat.approve'); + await PushAPI_chat_approve(); + + console.log('PushAPI.chat.createGroup'); + const chatId = await PushAPI_chat_createGroup(); + + console.log('PushAPI.chat.conversationHash'); + await PushAPI_chat_conversationHash(); + + console.log('PushAPI_chat_history'); + await PushAPI_chat_history(); + + console.log('PushAPI.chat.latest'); + await PushAPI_chat_latest(); + + console.log('PushAPI.chat.updateGroup'); + await PushAPI_chat_updateGroup(chatId); + + console.log('PushAPI.chat.getGroupByName'); + await PushAPI_chat_getGroupByName(); + + console.log('PushAPI.chat.getGroup'); + await PushAPI_chat_getGroup(chatId); + + console.log('PushAPI.chat.decryptConversation'); + await PushAPI_chat_decryptConversation(); + + console.log('Push Chat - PushSDKSocket()'); + await PushChatSDKSocket(); +} + +// Push Chat - PushAPI.user.create +async function PushAPI_user_create(silent = !showAPIResponse) { + const user = await PushAPI.user.create({ + signer: signer, + env: env as ENV, + }); + + const user_2 = await PushAPI.user.create({ + signer: signerSecondAccount, + env: env as ENV, + }); + + console.log('PushAPI_user_create | Response - 200 OK'); + if (!silent) { + console.log(user); + console.log(user_2); + } + + return user; +} + +// Push Chat - PushAPI.user.get +async function PushAPI_user_get(silent = !showAPIResponse) { + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + console.log('PushAPI_user_get | Response - 200 OK'); + + if (!silent) { + console.log(user); + } +} + +// Push Chat - PushAPI.chat.decryptPGPKey +async function PushAPI_chat_decryptPGPKey(silent = !showAPIResponse) { + // get user and derive encrypted PGP key + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // decrypt the PGP Key + const pgpKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + console.log('PushAPI_chat_decryptPGPKey | Response - 200 OK'); + if (!silent) { + console.log(pgpKey); + } +} + +// Push Chat - PushAPI.chat.chats +async function PushAPI_chat_chats(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Actual api + const response = await PushAPI.chat.chats({ + account: `eip155:${signer.address}`, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_chats | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.requests +async function PushAPI_chat_requests(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Actual api + const response = await PushAPI.chat.requests({ + account: `eip155:${signer.address}`, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_requests | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.conversationHash +async function PushAPI_chat_conversationHash(silent = !showAPIResponse) { + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: `eip155:${signer.address}`, + conversationId: `eip155:${signerSecondAccount.address}`, // 2nd address + env: env as ENV, + }); + + console.log('PushAPI_chat_conversationHash | Response - 200 OK'); + if (!silent) { + console.log(conversationHash); + } +} + +// Push Chat - PushAPI.chat.latest +async function PushAPI_chat_latest(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: `eip155:${signer.address}`, + conversationId: `eip155:${signerSecondAccount.address}`, // 2nd address + env: env as ENV, + }); + + // Actual API + const response = await PushAPI.chat.latest({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: `eip155:${signer.address}`, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_latest | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.history +async function PushAPI_chat_history(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: `eip155:${signer.address}`, + conversationId: `eip155:${signerSecondAccount.address}`, // 2nd address + env: env as ENV, + }); + + // Actual API + const response = await PushAPI.chat.history({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: `eip155:${signer.address}`, + limit: 5, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_history | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.send +// // Will send a message to the user or chat request in case user hasn't approved them +async function PushAPI_chat_send(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Actual api + const response = await PushAPI.chat.send({ + messageContent: "Gm gm! It's me... Mario", + messageType: 'Text', // can be "Text" | "Image" | "File" | "GIF" + receiverAddress: signerSecondAccount.address, + + signer: signer, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_send | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - Approve +async function PushAPI_chat_approve(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signerSecondAccount.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signerSecondAccount, + }); + + // Actual api + const approve = await PushAPI.chat.approve({ + status: 'Approved', + senderAddress: signer.address, // receiver's address or chatId of a group + + signer: signerSecondAccount, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_approve | Response - 200 OK'); + if (!silent) { + console.log(approve); + } +} + +// Push Chat - PushAPI.chat.createGroup +async function PushAPI_chat_createGroup( + silent = !showAPIResponse +): Promise { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Actual API + // Convert image to base 64 and pass + const response = await PushAPI.chat.createGroup({ + groupName, + groupDescription, + members: [`eip155:${randomWallet1}`, `eip155:${randomWallet2}`], + groupImage, + admins: [], // takes signer as admin automatically, add more if you want to + isPublic: true, + + signer: signer, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_createGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } + return response.chatId; +} + +// Push Chat - PushAPI.chat.updateGroup +async function PushAPI_chat_updateGroup( + chatId: string, + silent = !showAPIResponse +) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Actual API + // Convert image to base 64 and pass + // This is an idempotent operation, meaning it requires all group info to be passed no matter if only few things change + // Why so? To ensure that verificationProof always is able to replicate the current group info (trustless since signature is stored with the info) + const response = await PushAPI.chat.updateGroup({ + chatId, + groupName, + groupDescription, + members: [ + `eip155:${randomWallet1}`, + `eip155:${randomWallet2}`, + `eip155:${randomWallet3}`, + `eip155:${signer.address}`, + ], + groupImage, + admins: [`eip155:${signer.address}`], // takes signer as admin automatically, add more if you want to + + signer: signer, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_chat_updateGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.getGroupByName +async function PushAPI_chat_getGroupByName(silent = !showAPIResponse) { + const response = await PushAPI.chat.getGroupByName({ + groupName: 'Push Group Chat 3', + env: env as ENV, + }); + + console.log('PushAPI_chat_getGroupByName | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.getGroup +async function PushAPI_chat_getGroup( + chatId: string, + silent = !showAPIResponse +) { + const response = await PushAPI.chat.getGroup({ + chatId: chatId, + env: env as ENV, + }); + + console.log('PushAPI_chat_getGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.decryptConversation +async function PushAPI_chat_decryptConversation(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signer.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signer, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: `eip155:${signer.address}`, + conversationId: `eip155:${signerSecondAccount.address}`, // 2nd address + env: env as ENV, + }); + + // Chat History + const encryptedChats = await PushAPI.chat.history({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: `eip155:${signer.address}`, + limit: 5, + toDecrypt: false, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + // Decrypted Chat + const decryptedChat = await PushAPI.chat.decryptConversation({ + messages: encryptedChats, // array of message object fetched from chat.history method + connectedUser: user, // user meta data object fetched from chat.get method + pgpPrivateKey: pgpDecrpyptedPvtKey, //decrypted private key + env: env as ENV, + }); + + console.log('PushAPI_chat_decryptConversation | Response - 200 OK'); + if (!silent) { + console.log(decryptedChat); + } +} + +// Push Chat - Socket Connection +async function PushChatSDKSocket(silent = !showAPIResponse) { + const pushSDKSocket = createSocketConnection({ + user: `eip155:${signer.address}`, + socketType: 'chat', + socketOptions: { autoConnect: true, reconnectionAttempts: 3 }, + env: env as ENV, + }); + + if (!pushSDKSocket) { + throw new Error('Socket not connected'); + } + + pushSDKSocket.on(EVENTS.CONNECT, async () => { + console.log('Socket Connected - will disconnect after 4 seconds'); + + // send a chat from other wallet to this one to see the result + // Fetch user + const user = await PushAPI.user.get({ + account: `eip155:${signerSecondAccount.address}`, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: signerSecondAccount, + }); + + // Actual api + const response = await PushAPI.chat.send({ + messageContent: "Gm gm! It's me... Mario", + messageType: 'Text', + receiverAddress: `eip155:${signer.address}`, + + signer: signerSecondAccount, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + console.log('PushAPI_chat_send | Response - 200 OK'); + }); + + pushSDKSocket.on(EVENTS.DISCONNECT, () => { + console.log('Socket Disconnected'); + }); + + pushSDKSocket.on(EVENTS.CHAT_RECEIVED_MESSAGE, (message) => { + // feedItem is the notification data when that notification was received + console.log('Incoming Push Chat message from Socket'); + if (!silent) { + console.log(message); + } + + // disconnect socket after this, not to be done in real implementations + pushSDKSocket.disconnect(); + }); + + const delay = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); + await delay(4000); +} + +// Push Chat - Run Chat Use cases +async function runNFTChatUseCases() { + console.log(` + ███ ██ ███████ ████████ ██████ ██ ██ █████ ████████ + ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ██ ██ ██ █████ ██ ██ ███████ ███████ ██ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ██ ████ ██ ██ ██████ ██ ██ ██ ██ ██ + `); + console.log('PushAPI.user.create'); + await PushAPI_nft_user_create(); + + console.log('PushAPI.user.get'); + await PushAPI_nft_user_get(); + + console.log('PushAPI_chat_decryptPGPKey'); + await PushAPI_nft_chat_decryptPGPKey(); + + console.log('PushAPI.chat.chats'); + await PushAPI_nft_chat_chats(); + + console.log('PushAPI.chat.requests'); + await PushAPI_nft_chat_requests(); + + console.log('PushAPI.chat.send'); + await PushAPI_nft_chat_send(); + + console.log('PushAPI.chat.approve'); + await PushAPI_nft_chat_approve(); + + console.log('PushAPI.chat.createGroup'); + const chatId = await PushAPI_nft_chat_createGroup(); + + console.log('PushAPI.chat.conversationHash'); + await PushAPI_nft_chat_conversationHash(); + + console.log('PushAPI_chat_history'); + await PushAPI_nft_chat_history(); + + console.log('PushAPI.chat.latest'); + await PushAPI_nft_chat_latest(); + + console.log('PushAPI.chat.updateGroup'); + await PushAPI_nft_chat_updateGroup(chatId); + + console.log('PushAPI.chat.getGroupByName'); + await PushAPI_nft_chat_getGroupByName(); + + console.log('PushAPI.chat.getGroup'); + await PushAPI_nft_chat_getGroup(chatId); + + console.log('PushAPI.chat.decryptConversation'); + await PushAPI_nft_chat_decryptConversation(); + + console.log('Push Chat - PushSDKSocket()'); + await PushNFTChatSDKSocket(); +} + +// Push Chat - PushAPI.user.create +async function PushAPI_nft_user_create(silent = !showAPIResponse) { + const user1 = await PushAPI.user.create({ + account: nftAccount1, + signer: nftSigner1, + env: env as ENV, + additionalMeta: { NFTPGP_V1: { password: nftProfilePassword1 } }, + }); + + const user2 = await PushAPI.user.create({ + account: nftAccount2, + signer: nftSigner2, + env: env as ENV, + additionalMeta: { NFTPGP_V1: { password: nftProfilePassword2 } }, + }); + + console.log('PushAPI_nft_user_create | Response - 200 OK'); + if (!silent) { + console.log(user1); + console.log(user2); + } +} + +// Push Chat - PushAPI.user.get +async function PushAPI_nft_user_get(silent = !showAPIResponse) { + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + console.log('PushAPI_nft_user_get | Response - 200 OK'); + + if (!silent) { + console.log(user); + } +} + +// Push Chat - PushAPI.chat.decryptPGPKey +async function PushAPI_nft_chat_decryptPGPKey(silent = !showAPIResponse) { + // get user and derive encrypted PGP key + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // decrypt the PGP Key + const pgpKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + account: nftAccount1, + signer: nftSigner1, + }); + + console.log('PushAPI_nft_chat_decryptPGPKey | Response - 200 OK'); + if (!silent) { + console.log(pgpKey); + } +} + +// Push Chat - PushAPI.chat.chats +async function PushAPI_nft_chat_chats(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Actual api + const response = await PushAPI.chat.chats({ + account: nftAccount1, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_chats | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.requests +async function PushAPI_nft_chat_requests(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Actual api + const response = await PushAPI.chat.requests({ + account: nftAccount1, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_requests | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.conversationHash +async function PushAPI_nft_chat_conversationHash(silent = !showAPIResponse) { + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: nftAccount1, + conversationId: nftAccount2, // 2nd address + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_conversationHash | Response - 200 OK'); + if (!silent) { + console.log(conversationHash); + } +} + +// Push Chat - PushAPI.chat.latest +async function PushAPI_nft_chat_latest(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + signer: nftSigner1, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: nftAccount1, + conversationId: nftAccount2, // 2nd address + env: env as ENV, + }); + + // Actual API + const response = await PushAPI.chat.latest({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: nftAccount1, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_latest | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.history +async function PushAPI_nft_chat_history(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + signer: nftSigner1, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: nftAccount1, + conversationId: nftAccount2, // 2nd address + env: env as ENV, + }); + + // Actual API + const response = await PushAPI.chat.history({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: nftAccount1, + limit: 5, + toDecrypt: true, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_history | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.send +// // Will send a message to the user or chat request in case user hasn't approved them +async function PushAPI_nft_chat_send(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + signer: nftSigner1, + }); + + // Actual api + const response = await PushAPI.chat.send({ + messageContent: "Gm gm! It's me... Mario", + messageType: 'Text', // can be "Text" | "Image" | "File" | "GIF" + receiverAddress: nftAccount2, + account: nftAccount1, + signer: signer, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_send | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - Approve +async function PushAPI_nft_chat_approve(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount2, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner2, + }); + + // Actual api + const approve = await PushAPI.chat.approve({ + status: 'Approved', + senderAddress: nftAccount1, // receiver's address or chatId of a group + account: nftAccount2, + signer: nftSigner2, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_approve | Response - 200 OK'); + if (!silent) { + console.log(approve); + } +} + +// Push Chat - PushAPI.chat.createGroup +async function PushAPI_nft_chat_createGroup( + silent = !showAPIResponse +): Promise { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Actual API + // Convert image to base 64 and pass + const response = await PushAPI.chat.createGroup({ + groupName: nftGroupName, + groupDescription, + members: [nftAccount2, nftAccount3], + groupImage, + admins: [], // takes signer as admin automatically, add more if you want to + isPublic: true, + account: nftAccount1, + signer: signer, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_createGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } + return response.chatId; +} + +// Push Chat - PushAPI.chat.updateGroup +async function PushAPI_nft_chat_updateGroup( + chatId: string, + silent = !showAPIResponse +) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Actual API + // Convert image to base 64 and pass + // This is an idempotent operation, meaning it requires all group info to be passed no matter if only few things change + // Why so? To ensure that verificationProof always is able to replicate the current group info (trustless since signature is stored with the info) + const response = await PushAPI.chat.updateGroup({ + chatId, + groupName: updatedNftGroupName, + groupDescription, + members: [nftAccount2, nftAccount3], + groupImage, + admins: [nftAccount2], + account: nftAccount1, + signer: nftSigner1, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_updateGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.getGroupByName +async function PushAPI_nft_chat_getGroupByName(silent = !showAPIResponse) { + const response = await PushAPI.chat.getGroupByName({ + groupName: 'Push Group Chat 3', + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_getGroupByName | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.getGroup +async function PushAPI_nft_chat_getGroup( + chatId: string, + silent = !showAPIResponse +) { + const response = await PushAPI.chat.getGroup({ + chatId: chatId, + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_getGroup | Response - 200 OK'); + if (!silent) { + console.log(response); + } +} + +// Push Chat - PushAPI.chat.decryptConversation +async function PushAPI_nft_chat_decryptConversation(silent = !showAPIResponse) { + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Fetch conversation hash + // conversation hash are also called link inside chat messages + const conversationHash = await PushAPI.chat.conversationHash({ + account: nftAccount1, + conversationId: nftAccount2, // 2nd address + env: env as ENV, + }); + + // Chat History + const encryptedChats = await PushAPI.chat.history({ + threadhash: conversationHash.threadHash, // get conversation hash from conversationHash function and send the response threadhash here + account: nftAccount1, + limit: 5, + toDecrypt: false, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + + // Decrypted Chat + const decryptedChat = await PushAPI.chat.decryptConversation({ + messages: encryptedChats, // array of message object fetched from chat.history method + connectedUser: user, // user meta data object fetched from chat.get method + pgpPrivateKey: pgpDecrpyptedPvtKey, //decrypted private key + env: env as ENV, + }); + + console.log('PushAPI_nft_chat_decryptConversation | Response - 200 OK'); + if (!silent) { + console.log(decryptedChat); + } +} + +// Push Chat - Socket Connection +async function PushNFTChatSDKSocket(silent = !showAPIResponse) { + const pushSDKSocket = createSocketConnection({ + user: nftAccount1, + socketType: 'chat', + socketOptions: { autoConnect: true, reconnectionAttempts: 3 }, + env: env as ENV, + }); + + if (!pushSDKSocket) { + throw new Error('Socket not connected'); + } + + pushSDKSocket.on(EVENTS.CONNECT, async () => { + console.log('Socket Connected - will disconnect after 4 seconds'); + + // send a chat from other wallet to this one to see the result + // Fetch user + const user = await PushAPI.user.get({ + account: nftAccount1, + env: env as ENV, + }); + + // Decrypt PGP Key + const pgpDecrpyptedPvtKey = await PushAPI.chat.decryptPGPKey({ + encryptedPGPPrivateKey: user.encryptedPrivateKey, + + signer: nftSigner1, + }); + + // Actual api + const response = await PushAPI.chat.send({ + messageContent: "Gm gm! It's me... Mario", + messageType: 'Text', + receiverAddress: nftAccount2, + account: nftAccount1, + signer: nftSigner1, + pgpPrivateKey: pgpDecrpyptedPvtKey, + env: env as ENV, + }); + console.log('PushAPI_nft_chat_send | Response - 200 OK'); + }); + + pushSDKSocket.on(EVENTS.DISCONNECT, () => { + console.log('Socket Disconnected'); + }); + + pushSDKSocket.on(EVENTS.CHAT_RECEIVED_MESSAGE, (message) => { + // feedItem is the notification data when that notification was received + console.log('Incoming Push Chat message from Socket'); + if (!silent) { + console.log(message); + } + + // disconnect socket after this, not to be done in real implementations + pushSDKSocket.disconnect(); + }); + + const delay = (ms: number) => + new Promise((resolve) => setTimeout(resolve, ms)); + await delay(4000); +} + +// Use Cases +function start() { + console.log(`${returnHeadingLog()}`); + console.log(`${returnENVLog()}`); + runNotificaitonsUseCases().then(() => { + runChatUseCases().then(() => { + runNFTChatUseCases(); + }); + }); +} + +start(); + +// ----------- +// ----------- +// FUNCTION TO EMIT HEADER +// ----------- +// ----------- +function returnHeadingLog() { + const headingLog = ` + ███████ ██████ ██ ██ ███████ ██ ██ ███ ██ ██████ ████████ ██ ██████ ███ ██ █████ ██ ██ ████████ ██ ██ + ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ + ███████ ██ ██ █████ █████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ████ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ███████ ██████ ██ ██ ██ ██████ ██ ████ ██████ ██ ██ ██████ ██ ████ ██ ██ ███████ ██ ██ ██ + `; + return headingLog; +} + +function returnENVLog() { + let environmentLog = ` + ███████ ████████ █████ ██████ ██ ███ ██ ██████ + ██ ██ ██ ██ ██ ██ ████ ██ ██ + ███████ ██ ███████ ██ ███ ██ ██ ██ ██ ██ ███ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ███████ ██ ██ ██ ██████ ██ ██ ████ ██████ + `; + + if (env === ENV.PROD) { + environmentLog = ` + ██████ ██████ ██████ ██████ ██ ██ ██████ ████████ ██ ██████ ███ ██ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██ + ██████ ██████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ + ██ ██ ██ ██████ ██████ ██████ ██████ ██ ██ ██████ ██ ████ + `; + } else if (env === ENV.DEV) { + environmentLog = ` + ██████ ███████ ██ ██ + ██ ██ ██ ██ ██ + ██ ██ █████ ██ ██ + ██ ██ ██ ██ ██ + ██████ ███████ ████ + `; + } + + return environmentLog; +} diff --git a/packages/examples/sdk-backend-node/tsconfig.app.json b/packages/examples/sdk-backend-node/tsconfig.app.json new file mode 100644 index 000000000..b3cbc3b62 --- /dev/null +++ b/packages/examples/sdk-backend-node/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["node"] + }, + "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/packages/examples/sdk-backend-node/tsconfig.json b/packages/examples/sdk-backend-node/tsconfig.json new file mode 100644 index 000000000..25873177d --- /dev/null +++ b/packages/examples/sdk-backend-node/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/examples/sdk-backend-node/tsconfig.spec.json b/packages/examples/sdk-backend-node/tsconfig.spec.json new file mode 100644 index 000000000..99ef89807 --- /dev/null +++ b/packages/examples/sdk-backend-node/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/workspace.json b/workspace.json index 5f21452c5..a4b43259c 100644 --- a/workspace.json +++ b/workspace.json @@ -4,6 +4,7 @@ "projects": { "demonative": "apps/demonative", "demoreact": "packages/demoreact", + "examples-sdk-backend-node": "packages/examples/sdk-backend-node", "ledgerlive": "packages/ledgerlive", "reactnative": "packages/reactnative", "restapi": "packages/restapi", diff --git a/yarn.lock b/yarn.lock index 76f764929..cdb059894 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,6 +2456,37 @@ tmp "~0.2.1" tslib "^2.3.0" +"@nrwl/node@14.1.7": + version "14.1.7" + resolved "https://registry.yarnpkg.com/@nrwl/node/-/node-14.1.7.tgz#8388c511ffd0dd48fff3ecfadc1d5df15b06b35d" + integrity sha512-mnvS1WyXJgvwY3RKpEYf+i1UqPo3JJFLyzoBog7I9BWY1S5LQBH0UwTbrsb5hO48JRH7lHFSd96QoZT2Bzp+9Q== + dependencies: + "@nrwl/devkit" "14.1.7" + "@nrwl/jest" "14.1.7" + "@nrwl/js" "14.1.7" + "@nrwl/linter" "14.1.7" + "@nrwl/workspace" "14.1.7" + chalk "4.1.0" + copy-webpack-plugin "^9.0.1" + enhanced-resolve "^5.8.3" + fork-ts-checker-webpack-plugin "6.2.10" + fs-extra "^10.1.0" + glob "7.1.4" + license-webpack-plugin "^4.0.2" + rxjs "^6.5.4" + rxjs-for-await "0.0.2" + source-map-support "0.5.19" + terser-webpack-plugin "^5.3.0" + tree-kill "1.2.2" + ts-loader "^9.2.6" + ts-node "~9.1.1" + tsconfig-paths "^3.9.0" + tsconfig-paths-webpack-plugin "3.5.2" + tslib "^2.3.0" + webpack "^5.58.1" + webpack-merge "^5.8.0" + webpack-node-externals "^3.0.0" + "@nrwl/react-native@14.1.7": version "14.1.7" resolved "https://registry.npmjs.org/@nrwl/react-native/-/react-native-14.1.7.tgz" @@ -4483,6 +4514,16 @@ asap@~2.0.3, asap@~2.0.6: resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +asn1.js@^5.0.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -4892,7 +4933,7 @@ blueimp-md5@^2.10.0: resolved "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz" integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== -bn.js@^4.11.9: +bn.js@^4.0.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -11667,6 +11708,13 @@ opener@^1.5.1: resolved "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +openpgp@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-5.8.0.tgz#5e3033e8df59d1626ce65aa01b3cf5abee7ff26b" + integrity sha512-hq4+4s/vpjuwGgZSjplGp4j5FzSz+KwiFRiqMx+ZXr7VCK3CvTkktYilMTZMrf2vHsFH8aQ0596Lmn07HeKRmQ== + dependencies: + asn1.js "^5.0.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" @@ -13359,7 +13407,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.2: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0, safer-buffer@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -14938,6 +14986,11 @@ unique-filename@^1.1.1: dependencies: unique-slug "^2.0.0" +unique-names-generator@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/unique-names-generator/-/unique-names-generator-4.7.1.tgz#966407b12ba97f618928f77322cfac8c80df5597" + integrity sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow== + unique-slug@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz"