qortal-mobile/src/background.ts

3894 lines
118 KiB
TypeScript
Raw Normal View History

2024-04-14 14:57:30 +03:00
// @ts-nocheck
2024-09-09 20:36:39 +03:00
// import { encryptAndPublishSymmetricKeyGroupChat } from "./backgroundFunctions/encryption";
import {
decryptGroupEncryption,
encryptAndPublishSymmetricKeyGroupChat,
publishGroupEncryptedResource,
uint8ArrayToObject,
} from "./backgroundFunctions/encryption";
import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes";
import { QORT_DECIMALS } from "./constants/constants";
2024-04-14 14:57:30 +03:00
import Base58 from "./deps/Base58";
2024-09-09 20:36:39 +03:00
import {
base64ToUint8Array,
decryptSingle,
encryptSingle,
objectToBase64,
} from "./qdn/encryption/group-encryption";
import { reusableGet } from "./qdn/publish/pubish";
2024-07-12 23:44:21 -04:00
import { signChat } from "./transactions/signChat";
2024-04-14 14:57:30 +03:00
import { createTransaction } from "./transactions/transactions";
2024-07-04 20:14:32 +03:00
import { decryptChatMessage } from "./utils/decryptChatMessage";
2024-04-14 14:57:30 +03:00
import { decryptStoredWallet } from "./utils/decryptWallet";
import PhraseWallet from "./utils/generateWallet/phrase-wallet";
import { RequestQueueWithPromise } from "./utils/queue/queue";
2024-04-14 14:57:30 +03:00
import { validateAddress } from "./utils/validateAddress";
2024-09-09 20:36:39 +03:00
import { Sha256 } from "asmcrypto.js";
let lastGroupNotification
export const groupApi = "https://ext-node.qortal.link"
export const groupApiSocket = "wss://ext-node.qortal.link"
export const groupApiLocal = "http://127.0.0.1:12391"
export const groupApiSocketLocal = "ws://127.0.0.1:12391"
const timeDifferenceForNotificationChatsBackground = 600000
const requestQueueAnnouncements = new RequestQueueWithPromise(1)
2024-09-09 20:36:39 +03:00
const allQueues = {
requestQueueAnnouncements: requestQueueAnnouncements,
}
const controlAllQueues = (action) => {
Object.keys(allQueues).forEach((key) => {
const val = allQueues[key];
try {
if (typeof val[action] === 'function') {
val[action]();
}
} catch (error) {
console.error(error);
}
});
};
export const clearAllQueues = () => {
Object.keys(allQueues).forEach((key) => {
const val = allQueues[key];
try {
val.clear();
} catch (error) {
console.error(error);
}
});
}
const pauseAllQueues = () => controlAllQueues('pause');
const resumeAllQueues = () => controlAllQueues('resume');
2024-09-09 20:36:39 +03:00
const checkDifference = (createdTimestamp)=> {
return (Date.now() - createdTimestamp) < timeDifferenceForNotificationChatsBackground
}
const getApiKeyFromStorage = async () => {
return new Promise((resolve, reject) => {
chrome.storage.local.get('apiKey', (result) => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(result.apiKey || null); // Return null if apiKey isn't found
});
});
};
export const getBaseApi = async (customApi?: string) => {
if (customApi) {
return customApi;
}
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
if (apiKey) {
return groupApiLocal;
} else {
return groupApi;
}
};
export const createEndpointSocket = async (endpoint) => {
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
if (apiKey) {
return `${groupApiSocketLocal}${endpoint}`;
} else {
return `${groupApiSocket}${endpoint}`;
}
};
export const createEndpoint = async (endpoint, customApi) => {
if (customApi) {
return `${customApi}${endpoint}`;
}
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
if (apiKey) {
// Check if the endpoint already contains a query string
const separator = endpoint.includes('?') ? '&' : '?';
return `${groupApiLocal}${endpoint}${separator}apiKey=${apiKey}`;
} else {
return `${groupApi}${endpoint}`;
}
};
2024-04-14 14:57:30 +03:00
export const walletVersion = 2;
// List of your API endpoints
const apiEndpoints = [
"https://api.qortal.org",
2024-07-10 05:58:38 +03:00
"https://api2.qortal.org",
"https://appnode.qortal.org",
2024-04-14 14:57:30 +03:00
"https://apinode.qortalnodes.live",
"https://apinode1.qortalnodes.live",
"https://apinode2.qortalnodes.live",
"https://apinode3.qortalnodes.live",
"https://apinode4.qortalnodes.live",
];
2024-09-09 20:36:39 +03:00
const buyTradeNodeBaseUrl = "https://appnode.qortal.org";
const proxyAccountAddress = "QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku";
const proxyAccountPublicKey = "5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7";
2024-04-14 14:57:30 +03:00
const pendingResponses = new Map();
2024-09-09 20:36:39 +03:00
let groups = null
let socket
let timeoutId;
let groupSocketTimeout;
let socketTimeout: any;
let interval
let intervalThreads
2024-04-14 14:57:30 +03:00
// Function to check each API endpoint
2024-09-09 20:36:39 +03:00
export async function findUsableApi() {
2024-04-14 14:57:30 +03:00
for (const endpoint of apiEndpoints) {
try {
const response = await fetch(`${endpoint}/admin/status`);
if (!response.ok) throw new Error("Failed to fetch");
const data = await response.json();
if (data.isSynchronizing === false && data.syncPercent === 100) {
console.log(`Usable API found: ${endpoint}`);
return endpoint;
} else {
console.log(`API not ready: ${endpoint}`);
}
} catch (error) {
console.error(`Error checking API ${endpoint}:`, error);
}
}
throw new Error("No usable API found");
}
2024-09-09 20:36:39 +03:00
async function checkWebviewFocus() {
return new Promise((resolve) => {
// Set a timeout for 1 second
const timeout = setTimeout(() => {
resolve(false); // No response within 1 second, assume not focused
}, 1000);
// Send message to the content script to check focus
chrome.runtime.sendMessage({ action: 'CHECK_FOCUS' }, (response) => {
clearTimeout(timeout); // Clear the timeout if we get a response
if (chrome.runtime.lastError) {
resolve(false); // Error occurred, assume not focused
} else {
resolve(response); // Resolve based on the response
}
});
});
}
function playNotificationSound() {
chrome.runtime.sendMessage({ action: 'PLAY_NOTIFICATION_SOUND' });
}
const handleNotificationDirect = async (directs)=> {
let isFocused
const wallet = await getSaveWallet();
const address = wallet.address0;
const dataDirects = directs.filter((direct)=> direct?.sender !== address)
try {
isFocused = await checkWebviewFocus()
if(isFocused){
throw new Error('isFocused')
}
const newActiveChats= dataDirects
const oldActiveChats = await getChatHeadsDirect()
if(newActiveChats?.length === 0) return
let newestLatestTimestamp
let oldestLatestTimestamp
// Find the latest timestamp from newActiveChats
newActiveChats?.forEach(newChat => {
if (!newestLatestTimestamp || newChat?.timestamp > newestLatestTimestamp?.timestamp) {
newestLatestTimestamp = newChat;
}
});
// Find the latest timestamp from oldActiveChats
oldActiveChats?.forEach(oldChat => {
if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timestamp) {
oldestLatestTimestamp = oldChat;
}
});
if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){
const notificationId = 'chat_notification_' + Date.now() + '_type=direct' + `_from=${newestLatestTimestamp.address}`;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: `New Direct message! ${newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}`}`,
message: 'You have received a new direct message',
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
// chrome.runtime.sendMessage(
// {
// action: "notification",
// payload: {
// },
// }
// )
// audio.play();
playNotificationSound()
}
} catch (error) {
if(!isFocused){
chrome.runtime.sendMessage(
{
action: "notification",
payload: {
},
},
(response) => {
if (!response?.error) {
}
}
);
const notificationId = 'chat_notification_' + Date.now()
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: `New Direct message!`,
message: 'You have received a new direct message',
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
playNotificationSound()
// audio.play();
// }
}
} finally {
setChatHeadsDirect(dataDirects)
// chrome.runtime.sendMessage(
// {
// action: "setChatHeads",
// payload: {
// data,
// },
// }
// );
}
}
async function getThreadActivity(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `threadactivity-${address}`
const res = await chrome.storage.local.get([key]);
if (res?.[key]) {
const parsedData = JSON.parse(res[key])
return parsedData;
} else {
return null
}
}
async function updateThreadActivity({threadId, qortalName, groupId, thread}) {
const wallet = await getSaveWallet();
const address = wallet.address0;
const ONE_WEEK_IN_MS = 7 * 24 * 60 * 60 * 1000; // One week in milliseconds
let lastResetTime = 0
// Retrieve the last reset timestamp from storage
const key = `threadactivity-${address}`
chrome.storage.local.get([key], (data) => {
let threads
if (!data[key] || Object.keys(data?.[key]?.length === 0)) {
threads = { createdThreads: [], mostVisitedThreads: [], recentThreads: [] };
} else {
threads = JSON.parse(data[key])
}
if(threads?.lastResetTime){
lastResetTime = threads.lastResetTime
}
const currentTime = Date.now();
// Check if a week has passed since the last reset
if (!lastResetTime || currentTime - lastResetTime > ONE_WEEK_IN_MS) {
// Reset the visit counts for all most visited threads
threads.mostVisitedThreads.forEach(thread => thread.visitCount = 0);
lastResetTime = currentTime; // Update the last reset time
threads.lastResetTime = lastResetTime
}
// Update the recent threads list
threads.recentThreads = threads.recentThreads.filter(t => t.threadId !== threadId);
threads.recentThreads.unshift({ threadId, qortalName, groupId, thread, visitCount: 1, lastVisited: Date.now() });
// Sort the recent threads by lastVisited time (descending)
threads.recentThreads.sort((a, b) => b.lastVisited - a.lastVisited);
// Limit the recent threads list to 2 items
threads.recentThreads = threads.recentThreads.slice(0, 2);
// Update the most visited threads list
const existingThread = threads.mostVisitedThreads.find(t => t.threadId === threadId);
if (existingThread) {
existingThread.visitCount += 1;
existingThread.lastVisited = Date.now(); // Update the last visited time as well
} else {
threads.mostVisitedThreads.push({ threadId, qortalName, groupId, thread, visitCount: 1, lastVisited: Date.now() });
}
// Sort the most visited threads by visitCount (descending)
threads.mostVisitedThreads.sort((a, b) => b.visitCount - a.visitCount);
// Limit the most visited threads list to 2 items
threads.mostVisitedThreads = threads.mostVisitedThreads.slice(0, 2);
// Store the updated thread information and last reset time
// chrome.storage.local.set({ threads, lastResetTime });
const dataString = JSON.stringify(threads);
chrome.storage.local.set({ [`threadactivity-${address}`]: dataString })
});
}
const handleNotification = async (groups)=> {
const wallet = await getSaveWallet();
const address = wallet.address0;
let isFocused
const data = groups.filter((group)=> group?.sender !== address)
try {
if(!data || data?.length === 0) return
isFocused = await checkWebviewFocus()
if(isFocused){
throw new Error('isFocused')
}
const newActiveChats= data
const oldActiveChats = await getChatHeads()
let results = []
let newestLatestTimestamp
let oldestLatestTimestamp
// Find the latest timestamp from newActiveChats
newActiveChats?.forEach(newChat => {
if (!newestLatestTimestamp || newChat?.timestamp > newestLatestTimestamp?.timestamp) {
newestLatestTimestamp = newChat;
}
});
// Find the latest timestamp from oldActiveChats
oldActiveChats?.forEach(oldChat => {
if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timestamp) {
oldestLatestTimestamp = oldChat;
}
});
if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){
if (!lastGroupNotification || ((Date.now() - lastGroupNotification) >= 120000)) {
const notificationId = 'chat_notification_' + Date.now() + '_type=group' + `_from=${newestLatestTimestamp.groupId}`;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: 'New Group Message!',
message: `You have received a new message from ${newestLatestTimestamp?.groupName}`,
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
// chrome.runtime.sendMessage(
// {
// action: "notification",
// payload: {
// },
// }
// )
// audio.play();
playNotificationSound()
lastGroupNotification = Date.now()
}
}
} catch (error) {
if(!isFocused){
chrome.runtime.sendMessage(
{
action: "notification",
payload: {
},
},
(response) => {
if (!response?.error) {
}
}
);
const notificationId = 'chat_notification_' + Date.now();
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: 'New Group Message!',
message: 'You have received a new message from one of your groups',
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
playNotificationSound()
// audio.play();
lastGroupNotification = Date.now()
// }
}
} finally {
if(!data || data?.length === 0) return
setChatHeads(data)
// chrome.runtime.sendMessage(
// {
// action: "setChatHeads",
// payload: {
// data,
// },
// }
// );
}
}
const checkThreads = async (bringBack) => {
try {
let myName = ""
const userData = await getUserInfo()
if(userData?.name){
myName = userData.name
}
let newAnnouncements = []
let dataToBringBack = []
const threadActivity = await getThreadActivity()
if(!threadActivity) return null
const selectedThreads = [
...threadActivity.createdThreads.slice(0, 2),
...threadActivity.mostVisitedThreads.slice(0, 2),
...threadActivity.recentThreads.slice(0, 2),
]
if(selectedThreads?.length === 0) return null
const tempData = {
}
for (const thread of selectedThreads){
try {
const identifier = `thmsg-${thread?.threadId}`
const name = thread?.qortalName
const url = await createEndpoint(`/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true`);
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
const responseData = await response.json()
const latestMessage = responseData.filter((pub)=> pub?.name !== myName)[0]
// const latestMessage = responseData[0]
if (!latestMessage) {
continue
}
if(checkDifference(latestMessage.created) && latestMessage.created > thread?.lastVisited && (!thread?.lastNotified || thread?.lastNotified < thread?.created)){
tempData[thread.threadId] = latestMessage.created
newAnnouncements.push(thread)
}
if(latestMessage.created > thread?.lastVisited){
dataToBringBack.push(thread)
}
} catch (error) {
conosle.log({error})
}
}
if(bringBack){
return dataToBringBack
}
const updateThreadWithLastNotified = {
...threadActivity,
createdThreads: (threadActivity?.createdThreads || [])?.map((item)=> {
if(tempData[item.threadId]){
return {
...item,
lastNotified: tempData[item.threadId]
}
} else {
return item
}
}),
mostVisitedThreads: (threadActivity?.mostVisitedThreads || [])?.map((item)=> {
if(tempData[item.threadId]){
return {
...item,
lastNotified: tempData[item.threadId]
}
} else {
return item
}
}),
recentThreads: (threadActivity?.recentThreads || [])?.map((item)=> {
if(tempData[item.threadId]){
return {
...item,
lastNotified: tempData[item.threadId]
}
} else {
return item
}
}),
}
const wallet = await getSaveWallet();
const address = wallet.address0;
const dataString = JSON.stringify(updateThreadWithLastNotified);
chrome.storage.local.set({ [`threadactivity-${address}`]: dataString })
if(newAnnouncements.length > 0){
const notificationId = 'chat_notification_' + Date.now() + '_type=thread-post' + `_data=${JSON.stringify(newAnnouncements[0])}`;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: `New thread post!`,
message: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`,
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
playNotificationSound()
}
const savedtimestampAfter = await getTimestampGroupAnnouncement()
chrome.runtime.sendMessage({
action: "SET_GROUP_ANNOUNCEMENTS",
payload: savedtimestampAfter,
});
} catch (error) {
} finally {
}
}
const checkNewMessages =
async () => {
try {
let myName = ""
const userData = await getUserInfo()
if(userData?.name){
myName = userData.name
}
let newAnnouncements = []
const activeData = await getStoredData('active-groups-directs') || { groups: [], directs: [] };
const groups = activeData?.groups
if(!groups || groups?.length === 0) return
const savedtimestamp = await getTimestampGroupAnnouncement()
await Promise.all(groups.map(async (group) => {
2024-09-09 20:36:39 +03:00
try {
const identifier = `grp-${group.groupId}-anc-`;
const url = await createEndpoint(`/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=0&reverse=true&prefix=true`);
const response = await requestQueueAnnouncements.enqueue(()=> {
return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
})
const responseData = await response.json();
const latestMessage = responseData.filter((pub) => pub?.name !== myName)[0];
2024-09-09 20:36:39 +03:00
if (!latestMessage) {
return; // continue to the next group
2024-09-09 20:36:39 +03:00
}
if (checkDifference(latestMessage.created) && (!savedtimestamp[group.groupId] || latestMessage.created > savedtimestamp?.[group.groupId]?.notification)) {
newAnnouncements.push(group);
await addTimestampGroupAnnouncement({ groupId: group.groupId, timestamp: Date.now() });
// save new timestamp
2024-09-09 20:36:39 +03:00
}
} catch (error) {
console.error(error); // Handle error if needed
2024-09-09 20:36:39 +03:00
}
}));
2024-09-09 20:36:39 +03:00
if(newAnnouncements.length > 0){
const notificationId = 'chat_notification_' + Date.now() + '_type=group-announcement' + `_from=${newAnnouncements[0]?.groupId}`;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: `New group announcement!`,
message: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`,
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 7000);
playNotificationSound()
}
const savedtimestampAfter = await getTimestampGroupAnnouncement()
chrome.runtime.sendMessage({
action: "SET_GROUP_ANNOUNCEMENTS",
payload: savedtimestampAfter,
});
} catch (error) {
} finally {
}
}
const listenForNewGroupAnnouncements = async ()=> {
try {
setTimeout(() => {
checkNewMessages()
}, 500);
if(interval){
clearInterval(interval)
}
let isCalling = false
interval = setInterval(async () => {
if (isCalling) return
isCalling = true
const res = await checkNewMessages()
isCalling = false
}, 180000)
} catch (error) {
}
}
const listenForThreadUpdates = async ()=> {
try {
setTimeout(() => {
checkThreads()
}, 500);
if(intervalThreads){
clearInterval(intervalThreads)
}
let isCalling = false
intervalThreads = setInterval(async () => {
if (isCalling) return
isCalling = true
const res = await checkThreads()
isCalling = false
}, 60000)
} catch (error) {
}
}
const forceCloseWebSocket = () => {
if (socket) {
clearTimeout(timeoutId);
clearTimeout(groupSocketTimeout);
clearTimeout(socketTimeout);
timeoutId = null
groupSocketTimeout = null
socket.close(1000, 'forced')
socket = null
}
};
2024-04-14 14:57:30 +03:00
async function getNameInfo() {
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(validApi + "/names/address/" + address);
const nameData = await response.json();
if (nameData?.length > 0) {
return nameData[0].name;
} else {
return "";
}
}
async function getAddressInfo(address) {
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(validApi + "/addresses/" + address);
const data = await response.json();
if (!response?.ok && data?.error !== 124)
throw new Error("Cannot fetch address info");
if (data?.error === 124) {
return {
address,
};
}
return data;
}
2024-07-09 00:44:02 +03:00
async function getKeyPair() {
const res = await chrome.storage.local.get(["keyPair"]);
if (res?.keyPair) {
return res.keyPair;
} else {
throw new Error("Wallet not authenticated");
}
}
2024-04-14 14:57:30 +03:00
async function getSaveWallet() {
const res = await chrome.storage.local.get(["walletInfo"]);
if (res?.walletInfo) {
return res.walletInfo;
} else {
throw new Error("No wallet saved");
}
}
async function getUserInfo() {
const wallet = await getSaveWallet();
const address = wallet.address0;
const addressInfo = await getAddressInfo(address);
const name = await getNameInfo();
return {
name,
2024-07-11 03:44:45 +03:00
publicKey: wallet.publicKey,
2024-04-14 14:57:30 +03:00
...addressInfo,
};
}
async function connection(hostname) {
const isConnected = chrome.storage.local.get([hostname]);
return isConnected;
}
2024-07-12 23:44:21 -04:00
async function getTradeInfo(qortalAtAddress) {
2024-09-09 20:36:39 +03:00
const response = await fetch(
buyTradeNodeBaseUrl + "/crosschain/trade/" + qortalAtAddress
);
2024-07-12 23:44:21 -04:00
if (!response?.ok) throw new Error("Cannot crosschain trade information");
const data = await response.json();
return data;
}
2024-04-14 14:57:30 +03:00
async function getBalanceInfo() {
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(validApi + "/addresses/balance/" + address);
if (!response?.ok) throw new Error("Cannot fetch balance");
const data = await response.json();
return data;
}
2024-07-17 17:00:43 -04:00
async function getLTCBalance() {
const wallet = await getSaveWallet();
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
2024-09-09 20:36:39 +03:00
const keyPair = await getKeyPair();
const parsedKeyPair = JSON.parse(keyPair);
let _body = parsedKeyPair.ltcPublicKey;
2024-07-17 17:00:43 -04:00
const response = await fetch(_url, {
2024-09-09 20:36:39 +03:00
method: "POST",
2024-07-17 17:00:43 -04:00
headers: {
2024-09-09 20:36:39 +03:00
"Content-Type": "application/json",
2024-07-17 17:00:43 -04:00
},
2024-09-09 20:36:39 +03:00
body: _body,
});
if (response?.ok) {
const data = await response.text();
const dataLTCBalance = (Number(data) / 1e8).toFixed(8);
return +dataLTCBalance;
} else throw new Error("Onable to get LTC balance");
2024-07-17 17:00:43 -04:00
}
2024-04-14 14:57:30 +03:00
2024-09-09 20:36:39 +03:00
const processTransactionVersion2Chat = async (body: any, customApi) => {
2024-07-12 23:44:21 -04:00
// const validApi = await findUsableApi();
2024-09-09 20:36:39 +03:00
const url = await createEndpoint("/transactions/process?apiVersion=2", customApi);
2024-07-12 23:44:21 -04:00
return fetch(url, {
method: "POST",
headers: {},
body: Base58.encode(body),
}).then(async (response) => {
try {
const json = await response.clone().json();
return json;
} catch (e) {
return await response.text();
}
});
};
2024-09-09 20:36:39 +03:00
const processTransactionVersion2 = async (body: any) => {
const url = await createEndpoint(`/transactions/process?apiVersion=2`);
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json", // Ensure the body is correctly parsed
},
body, // Convert body to JSON string
});
// if (!response.ok) {
// // If the response is not successful (status code is not 2xx)
// throw new Error(`HTTP error! Status: ${response.status}`);
// }
2024-07-12 23:44:21 -04:00
2024-04-14 14:57:30 +03:00
try {
const json = await response.clone().json();
return json;
2024-09-09 20:36:39 +03:00
} catch (jsonError) {
try {
const text = await response.text();
return text
} catch (textError) {
throw new Error(`Failed to parse response as both JSON and text.`);
}
2024-04-14 14:57:30 +03:00
}
2024-09-09 20:36:39 +03:00
} catch (error) {
console.error("Error processing transaction:", error);
throw error; // Re-throw the error after logging it
}
2024-04-14 14:57:30 +03:00
};
2024-09-09 20:36:39 +03:00
2024-07-04 20:14:32 +03:00
const transaction = async (
{ type, params, apiVersion, keyPair }: any,
validApi
) => {
2024-04-14 14:57:30 +03:00
const tx = createTransaction(type, keyPair, params);
let res;
if (apiVersion && apiVersion === 2) {
const signedBytes = Base58.encode(tx.signedBytes);
2024-04-14 18:03:30 +03:00
res = await processTransactionVersion2(signedBytes, validApi);
2024-04-14 14:57:30 +03:00
}
2024-09-09 20:36:39 +03:00
let success = true
if(res?.error){
success = false
}
2024-04-14 14:57:30 +03:00
return {
2024-09-09 20:36:39 +03:00
success,
2024-04-14 14:57:30 +03:00
data: res,
};
};
const makeTransactionRequest = async (
receiver,
lastRef,
amount,
fee,
2024-04-14 18:03:30 +03:00
keyPair,
validApi
2024-04-14 14:57:30 +03:00
) => {
2024-07-04 20:14:32 +03:00
const myTxnrequest = await transaction(
{
nonce: 0,
type: 2,
params: {
recipient: receiver,
// recipientName: recipientName,
amount: amount,
lastReference: lastRef,
fee: fee,
},
apiVersion: 2,
keyPair,
2024-04-14 14:57:30 +03:00
},
2024-07-04 20:14:32 +03:00
validApi
);
2024-04-14 14:57:30 +03:00
return myTxnrequest;
};
const getLastRef = async () => {
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(
validApi + "/addresses/lastreference/" + address
);
if (!response?.ok) throw new Error("Cannot fetch balance");
const data = await response.text();
return data;
};
const sendQortFee = async () => {
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(
validApi + "/transactions/unitfee?txType=PAYMENT"
);
if (!response.ok) {
throw new Error("Error when fetching join fee");
}
const data = await response.json();
const qortFee = (Number(data) / 1e8).toFixed(8);
return qortFee;
};
2024-09-09 20:36:39 +03:00
2024-04-14 14:57:30 +03:00
async function getNameOrAddress(receiver) {
try {
const isAddress = validateAddress(receiver);
if (isAddress) {
return receiver;
}
2024-09-09 20:36:39 +03:00
const validApi = await getBaseApi()
2024-04-14 14:57:30 +03:00
const response = await fetch(validApi + "/names/" + receiver);
const data = await response.json();
if (data?.owner) return data.owner;
if (data?.error) {
throw new Error("Name does not exist");
}
if (!response?.ok) throw new Error("Cannot fetch name");
return { error: "cannot validate address or name" };
2024-04-16 13:25:36 +03:00
} catch (error) {
2024-07-04 20:14:32 +03:00
throw new Error(error?.message || "cannot validate address or name");
2024-04-16 13:25:36 +03:00
}
2024-04-14 14:57:30 +03:00
}
2024-07-09 00:44:02 +03:00
2024-09-09 20:36:39 +03:00
async function getPublicKey(receiver) {
try {
const validApi = await getBaseApi()
const response = await fetch(validApi + "/addresses/publickey/" + receiver);
if (!response?.ok) throw new Error("Cannot fetch recipient's public key");
const data = await response.text();
if (!data?.error && data !== 'false') return data;
if (data?.error) {
throw new Error("Cannot fetch recipient's public key");
}
throw new Error("Cannot fetch recipient's public key");
} catch (error) {
throw new Error(error?.message || "cannot validate address or name");
}
}
2024-07-15 22:07:57 -04:00
async function decryptWallet({ password, wallet, walletVersion }) {
2024-07-09 00:44:02 +03:00
try {
const response = await decryptStoredWallet(password, wallet);
const wallet2 = new PhraseWallet(response, walletVersion);
const keyPair = wallet2._addresses[0].keyPair;
2024-09-09 20:36:39 +03:00
const ltcPrivateKey =
wallet2._addresses[0].ltcWallet.derivedMasterPrivateKey;
const ltcPublicKey = wallet2._addresses[0].ltcWallet.derivedMasterPublicKey;
const ltcAddress = wallet2._addresses[0].ltcWallet.address;
2024-07-09 00:44:02 +03:00
const toSave = {
privateKey: Base58.encode(keyPair.privateKey),
2024-07-15 22:07:57 -04:00
publicKey: Base58.encode(keyPair.publicKey),
2024-07-17 17:00:43 -04:00
ltcPrivateKey: ltcPrivateKey,
2024-09-09 20:36:39 +03:00
ltcPublicKey: ltcPublicKey,
};
const dataString = JSON.stringify(toSave);
2024-07-09 00:44:02 +03:00
await new Promise((resolve, reject) => {
chrome.storage.local.set({ keyPair: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
2024-07-11 03:44:45 +03:00
const newWallet = {
...wallet,
2024-07-17 20:53:38 -04:00
publicKey: Base58.encode(keyPair.publicKey),
2024-09-09 20:36:39 +03:00
ltcAddress: ltcAddress,
};
2024-07-09 00:44:02 +03:00
await new Promise((resolve, reject) => {
2024-07-11 03:44:45 +03:00
chrome.storage.local.set({ walletInfo: newWallet }, () => {
2024-07-09 00:44:02 +03:00
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
2024-07-15 22:07:57 -04:00
return true;
2024-07-09 00:44:02 +03:00
} catch (error) {
2024-09-09 20:36:39 +03:00
2024-07-09 00:44:02 +03:00
throw new Error(error.message);
}
}
2024-09-09 20:36:39 +03:00
async function signChatFunc(chatBytesArray, chatNonce, customApi, keyPair) {
let response;
2024-07-15 22:07:57 -04:00
try {
2024-09-09 20:36:39 +03:00
const signedChatBytes = signChat(chatBytesArray, chatNonce, keyPair);
const res = await processTransactionVersion2Chat(signedChatBytes, customApi);
response = res;
2024-07-15 22:07:57 -04:00
} catch (e) {
2024-09-09 20:36:39 +03:00
console.error(e);
console.error(e.message);
response = false;
2024-07-15 22:07:57 -04:00
}
2024-09-09 20:36:39 +03:00
return response;
2024-07-12 23:44:21 -04:00
}
function sbrk(size, heap) {
2024-09-09 20:36:39 +03:00
let brk = 512 * 1024; // stack top
let old = brk;
brk += size;
if (brk > heap.length) throw new Error("heap exhausted");
return old;
2024-07-12 23:44:21 -04:00
}
2024-07-15 22:07:57 -04:00
const computePow = async ({ chatBytes, path, difficulty }) => {
2024-09-09 20:36:39 +03:00
let response = null;
2024-07-15 22:07:57 -04:00
await new Promise((resolve, reject) => {
const _chatBytesArray = Object.keys(chatBytes).map(function (key) {
2024-09-09 20:36:39 +03:00
return chatBytes[key];
});
const chatBytesArray = new Uint8Array(_chatBytesArray);
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
const heap = new Uint8Array(memory.buffer);
const hashPtr = sbrk(32, heap);
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
hashAry.set(chatBytesHash);
const workBufferLength = 8 * 1024 * 1024;
const workBufferPtr = sbrk(workBufferLength, heap);
2024-07-15 22:07:57 -04:00
const importObject = {
env: {
2024-09-09 20:36:39 +03:00
memory: memory,
},
};
2024-07-15 22:07:57 -04:00
function loadWebAssembly(filename, imports) {
// Fetch the file and compile it
2024-09-09 20:36:39 +03:00
return fetch(filename)
.then((response) => response.arrayBuffer())
.then((buffer) => WebAssembly.compile(buffer))
.then((module) => {
// Create the instance.
return new WebAssembly.Instance(module, importObject);
});
2024-07-15 22:07:57 -04:00
}
2024-09-09 20:36:39 +03:00
loadWebAssembly(path).then((wasmModule) => {
response = {
nonce: wasmModule.exports.compute2(
hashPtr,
workBufferPtr,
workBufferLength,
difficulty
),
chatBytesArray,
};
resolve();
});
});
return response;
};
2024-07-12 23:44:21 -04:00
2024-09-09 20:36:39 +03:00
const getStoredData = async (key) => {
return new Promise((resolve, reject) => {
chrome.storage.local.get(key, (result) => {
if (chrome.runtime.lastError) {
return reject(chrome.runtime.lastError);
}
resolve(result[key]);
});
});
};
async function handleActiveGroupDataFromSocket({groups, directs}){
try {
chrome.runtime.sendMessage({
action: "SET_GROUPS",
payload: groups,
});
chrome.runtime.sendMessage({
action: "SET_DIRECTS",
payload: directs,
});
groups = groups
directs = directs
const activeData = {
groups: groups || [], // Your groups data here
directs: directs || [] // Your directs data here
};
// Save the active data to localStorage
chrome.storage.local.set({ 'active-groups-directs': activeData });
try {
handleNotification(groups)
handleNotificationDirect(directs)
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
} catch (error) {
}
} catch (error) {
}
}
async function sendChat({ qortAddress, recipientPublicKey, message }) {
2024-07-12 23:44:21 -04:00
let _reference = new Uint8Array(64);
self.crypto.getRandomValues(_reference);
2024-09-09 20:36:39 +03:00
let sendTimestamp = Date.now();
2024-07-12 23:44:21 -04:00
2024-09-09 20:36:39 +03:00
let reference = Base58.encode(_reference);
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
2024-07-15 22:07:57 -04:00
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
2024-09-09 20:36:39 +03:00
publicKey: uint8PublicKey,
2024-07-15 22:07:57 -04:00
};
2024-09-09 20:36:39 +03:00
const balance = await getBalanceInfo();
const hasEnoughBalance = +balance < 4 ? false : true;
const difficulty = 8;
2024-07-15 22:07:57 -04:00
const jsonData = {
atAddress: message.atAddress,
foreignKey: message.foreignKey,
2024-09-09 20:36:39 +03:00
receivingAddress: message.receivingAddress,
2024-07-15 22:07:57 -04:00
};
const finalJson = {
callRequest: jsonData,
2024-09-09 20:36:39 +03:00
extra: "whatever additional data goes here",
2024-07-15 22:07:57 -04:00
};
2024-09-09 20:36:39 +03:00
const messageStringified = JSON.stringify(finalJson);
const tx = await createTransaction(18, keyPair, {
timestamp: sendTimestamp,
recipient: qortAddress,
recipientPublicKey: recipientPublicKey,
hasChatReference: 0,
message: messageStringified,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 1,
isText: 1,
});
if (!hasEnoughBalance) {
const _encryptedMessage = tx._encryptedMessage;
const encryptedMessageToBase58 = Base58.encode(_encryptedMessage);
return {
encryptedMessageToBase58,
2024-09-09 20:36:39 +03:00
signature: "id-" + Date.now() + "-" + Math.floor(Math.random() * 1000),
reference,
};
}
const path = chrome.runtime.getURL("memory-pow.wasm.full");
const { nonce, chatBytesArray } = await computePow({
chatBytes: tx.chatBytes,
path,
difficulty,
});
let _response = await signChatFunc(
chatBytesArray,
nonce,
"https://appnode.qortal.org",
keyPair
);
if (_response?.error) {
throw new Error(_response?.message);
}
2024-09-09 20:36:39 +03:00
return _response;
}
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
async function sendChatGroup({
groupId,
typeMessage,
chatReference,
messageText,
}) {
let _reference = new Uint8Array(64);
self.crypto.getRandomValues(_reference);
let reference = Base58.encode(_reference);
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const balance = await getBalanceInfo();
const hasEnoughBalance = +balance < 4 ? false : true;
const difficulty = 8;
const tx = await createTransaction(181, keyPair, {
timestamp: Date.now(),
groupID: Number(groupId),
hasReceipient: 0,
hasChatReference: typeMessage === "edit" ? 1 : 0,
// chatReference: chatReference,
message: messageText,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 0, // Set default to not encrypted for groups
isText: 1,
});
if (!hasEnoughBalance) {
throw new Error("Must have at least 4 QORT to send a chat message");
}
const path = chrome.runtime.getURL("memory-pow.wasm.full");
const { nonce, chatBytesArray } = await computePow({
chatBytes: tx.chatBytes,
path,
difficulty,
});
let _response = await signChatFunc(
chatBytesArray,
nonce,
null,
keyPair
);
if (_response?.error) {
throw new Error(_response?.message);
}
return _response;
}
async function sendChatDirect({
directTo,
typeMessage,
chatReference,
messageText,
}) {
const recipientAddress = await getNameOrAddress(directTo)
const recipientPublicKey = await getPublicKey(recipientAddress)
if(!recipientPublicKey) throw new Error('Cannot retrieve publickey')
2024-09-09 20:36:39 +03:00
let _reference = new Uint8Array(64);
self.crypto.getRandomValues(_reference);
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
let reference = Base58.encode(_reference);
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const balance = await getBalanceInfo();
const hasEnoughBalance = +balance < 4 ? false : true;
const difficulty = 8;
const finalJson = {
message: messageText,
version: 2,
};
const messageStringified = JSON.stringify(finalJson);
const tx = await createTransaction(18, keyPair, {
timestamp: Date.now(),
recipient: recipientAddress,
recipientPublicKey: recipientPublicKey,
hasChatReference: 0,
message: messageStringified,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 1,
isText: 1,
});
if (!hasEnoughBalance) {
throw new Error("Must have at least 4 QORT to send a chat message");
}
const path = chrome.runtime.getURL("memory-pow.wasm.full");
const { nonce, chatBytesArray } = await computePow({
chatBytes: tx.chatBytes,
path,
difficulty,
});
let _response = await signChatFunc(
chatBytesArray,
nonce,
null,
keyPair
);
2024-07-15 22:07:57 -04:00
if (_response?.error) {
2024-09-09 20:36:39 +03:00
throw new Error(_response?.message);
}
return _response;
}
async function decryptSingleFunc({ messages, secretKeyObject, skipDecodeBase64 }) {
let holdMessages = [];
for (const message of messages) {
try {
const res = await decryptSingle({
data64: message.data,
secretKeyObject,
skipDecodeBase64
});
const decryptToUnit8Array = base64ToUint8Array(res);
const responseData = uint8ArrayToObject(decryptToUnit8Array);
holdMessages.push({ ...message, text: responseData });
} catch (error) {
}
}
return holdMessages;
}
async function decryptSingleForPublishes({ messages, secretKeyObject, skipDecodeBase64 }) {
let holdMessages = [];
for (const message of messages) {
try {
const res = await decryptSingle({
data64: message.data,
secretKeyObject,
skipDecodeBase64
});
const decryptToUnit8Array = base64ToUint8Array(res);
const responseData = uint8ArrayToObject(decryptToUnit8Array);
holdMessages.push({ ...message, decryptedData: responseData });
} catch (error) {
}
}
return holdMessages;
}
async function decryptDirectFunc({ messages, involvingAddress }) {
const senderPublicKey = await getPublicKey(involvingAddress)
let holdMessages = [];
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
for (const message of messages) {
try {
const decodedMessage = decryptChatMessage(
message.data,
keyPair.privateKey,
senderPublicKey,
message.reference
);
const parsedMessage = JSON.parse(decodedMessage);
holdMessages.push({ ...message, ...parsedMessage });
} catch (error) {
}
2024-07-15 22:07:57 -04:00
}
2024-09-09 20:36:39 +03:00
return holdMessages;
2024-07-12 23:44:21 -04:00
}
2024-07-15 22:07:57 -04:00
async function createBuyOrderTx({ crosschainAtInfo }) {
2024-07-12 23:44:21 -04:00
try {
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
2024-07-12 23:44:21 -04:00
const message = {
atAddress: crosschainAtInfo.qortalAtAddress,
foreignKey: parsedData.ltcPrivateKey,
2024-09-09 20:36:39 +03:00
receivingAddress: address,
};
const res = await sendChat({
qortAddress: proxyAccountAddress,
recipientPublicKey: proxyAccountPublicKey,
message,
});
2024-07-15 22:07:57 -04:00
if (res?.signature) {
listenForChatMessageForBuyOrder({
nodeBaseUrl: buyTradeNodeBaseUrl,
senderAddress: proxyAccountAddress,
senderPublicKey: proxyAccountPublicKey,
2024-07-12 23:44:21 -04:00
signature: res?.signature,
2024-09-09 20:36:39 +03:00
});
if (res?.encryptedMessageToBase58) {
return {
atAddress: crosschainAtInfo.qortalAtAddress,
encryptedMessageToBase58: res?.encryptedMessageToBase58,
node: buyTradeNodeBaseUrl,
qortAddress: address,
chatSignature: res?.signature,
senderPublicKey: parsedData.publicKey,
sender: address,
reference: res?.reference,
};
}
2024-09-09 20:36:39 +03:00
return {
atAddress: crosschainAtInfo.qortalAtAddress,
chatSignature: res?.signature,
node: buyTradeNodeBaseUrl,
qortAddress: address,
};
2024-07-17 17:00:43 -04:00
} else {
2024-09-09 20:36:39 +03:00
throw new Error("Unable to send buy order message");
2024-07-12 23:44:21 -04:00
}
} catch (error) {
throw new Error(error.message);
}
}
2024-09-09 20:36:39 +03:00
async function sendChatNotification(res, groupId, secretKeyObject, numberOfMembers){
try {
const data = await objectToBase64({
type: "notification",
subType: "new-group-encryption",
data: {
timestamp: res.timestamp,
name: res.name,
message: `${res.name} has updated the encryption key`,
numberOfMembers
},
})
encryptSingle({
data64: data,
secretKeyObject: secretKeyObject,
})
.then((res2) => {
pauseAllQueues()
2024-09-09 20:36:39 +03:00
sendChatGroup({
groupId,
typeMessage: undefined,
chatReference: undefined,
messageText: res2,
})
.then(() => {})
.catch((error) => {
console.error('1',error.message);
}).finally(()=> {
resumeAllQueues()
})
2024-09-09 20:36:39 +03:00
})
.catch((error) => {
console.error('2',error.message);
});
} catch (error) {
}
}
export const getFee = async(txType)=> {
const timestamp = Date.now()
const data = await reusableGet(`/transactions/unitfee?txType=${txType}&timestamp=${timestamp}`)
const arbitraryFee = (Number(data) / 1e8).toFixed(8)
return {
timestamp,
fee: arbitraryFee
}
}
async function leaveGroup({groupId}){
const wallet = await getSaveWallet();
const address = wallet.address0;
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('LEAVE_GROUP')
const tx = await createTransaction(32, keyPair, {
fee: feeres.fee,
registrantAddress: address,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function joinGroup({groupId}){
const wallet = await getSaveWallet();
const address = wallet.address0;
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('JOIN_GROUP')
const tx = await createTransaction(31, keyPair, {
fee: feeres.fee,
registrantAddress: address,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error(res?.message || 'Transaction was not able to be processed')
return res
}
async function cancelInvitationToGroup({groupId, qortalAddress}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('CANCEL_GROUP_INVITE')
const tx = await createTransaction(30, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function cancelBan({groupId, qortalAddress}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('CANCEL_GROUP_BAN')
const tx = await createTransaction(27, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function registerName({name}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('REGISTER_NAME')
const tx = await createTransaction(3, keyPair, {
fee: feeres.fee,
name,
value: "",
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function makeAdmin({groupId, qortalAddress}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('ADD_GROUP_ADMIN')
const tx = await createTransaction(24, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function removeAdmin({groupId, qortalAddress}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('REMOVE_GROUP_ADMIN')
const tx = await createTransaction(25, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function banFromGroup({groupId, qortalAddress, rBanReason = "", rBanTime}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('GROUP_BAN')
const tx = await createTransaction(26, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
rBanReason: rBanReason,
rBanTime,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function kickFromGroup({groupId, qortalAddress, rBanReason = ""}){
const lastReference = await getLastRef()
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const feeres = await getFee('GROUP_KICK')
const tx = await createTransaction(28, keyPair, {
fee: feeres.fee,
recipient: qortalAddress,
rGroupId: groupId,
rBanReason: rBanReason,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function createGroup({ groupName, groupDescription, groupType, groupApprovalThreshold, minBlock, maxBlock}){
const wallet = await getSaveWallet();
const address = wallet.address0;
if(!address) throw new Error('Cannot find user')
const lastReference = await getLastRef()
const feeres = await getFee('CREATE_GROUP')
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const tx = await createTransaction(22, keyPair, {
fee: feeres.fee,
registrantAddress: address,
rGroupName: groupName,
rGroupDesc: groupDescription,
rGroupType: groupType,
rGroupApprovalThreshold: groupApprovalThreshold,
rGroupMinimumBlockDelay: minBlock,
rGroupMaximumBlockDelay: maxBlock,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
async function inviteToGroup({groupId, qortalAddress, inviteTime}){
const address = await getNameOrAddress(qortalAddress)
if(!address) throw new Error('Cannot find user')
const lastReference = await getLastRef()
const feeres = await getFee('GROUP_INVITE')
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
publicKey: uint8PublicKey,
};
const tx = await createTransaction(29, keyPair, {
fee: feeres.fee,
recipient: address,
rGroupId: groupId,
rInviteTime: inviteTime,
lastReference: lastReference,
});
const signedBytes = Base58.encode(tx.signedBytes);
const res = await processTransactionVersion2(signedBytes)
if(!res?.signature) throw new Error('Transaction was not able to be processed')
return res
}
2024-07-09 00:44:02 +03:00
async function sendCoin({ password, amount, receiver }, skipConfirmPassword) {
2024-04-14 14:57:30 +03:00
try {
const confirmReceiver = await getNameOrAddress(receiver);
if (confirmReceiver.error)
throw new Error("Invalid receiver address or name");
const wallet = await getSaveWallet();
2024-09-09 20:36:39 +03:00
let keyPair = "";
2024-07-15 22:07:57 -04:00
if (skipConfirmPassword) {
2024-09-09 20:36:39 +03:00
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
2024-07-09 00:44:02 +03:00
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
keyPair = {
privateKey: uint8PrivateKey,
2024-09-09 20:36:39 +03:00
publicKey: uint8PublicKey,
2024-07-09 00:44:02 +03:00
};
} else {
const response = await decryptStoredWallet(password, wallet);
2024-07-15 22:07:57 -04:00
const wallet2 = new PhraseWallet(response, walletVersion);
2024-04-14 14:57:30 +03:00
2024-09-09 20:36:39 +03:00
keyPair = wallet2._addresses[0].keyPair;
2024-07-09 00:44:02 +03:00
}
2024-07-15 22:07:57 -04:00
2024-04-14 14:57:30 +03:00
const lastRef = await getLastRef();
const fee = await sendQortFee();
2024-09-09 20:36:39 +03:00
const validApi = await findUsableApi()
2024-04-14 18:03:30 +03:00
2024-04-14 14:57:30 +03:00
const res = await makeTransactionRequest(
confirmReceiver,
lastRef,
amount,
fee,
2024-07-09 00:44:02 +03:00
keyPair,
2024-04-14 18:03:30 +03:00
validApi
2024-04-14 14:57:30 +03:00
);
2024-09-09 20:36:39 +03:00
2024-07-04 20:14:32 +03:00
return { res, validApi };
} catch (error) {
throw new Error(error.message);
}
}
function fetchMessages(apiCall) {
let retryDelay = 2000; // Start with a 2-second delay
2024-07-12 23:44:21 -04:00
const maxDuration = 360000 * 2; // Maximum duration set to 12 minutes
2024-07-04 20:14:32 +03:00
const startTime = Date.now(); // Record the start time
// Promise to handle polling logic
return new Promise((resolve, reject) => {
2024-07-15 22:07:57 -04:00
const attemptFetch = async () => {
if (Date.now() - startTime > maxDuration) {
return reject(new Error("Maximum polling time exceeded"));
}
2024-07-04 20:14:32 +03:00
2024-07-15 22:07:57 -04:00
try {
const response = await fetch(apiCall);
const data = await response.json();
if (data && data.length > 0) {
resolve(data[0]); // Resolve the promise when data is found
} else {
setTimeout(attemptFetch, retryDelay);
retryDelay = Math.min(retryDelay * 2, 360000); // Ensure delay does not exceed 6 minutes
}
} catch (error) {
reject(error); // Reject the promise on error
}
};
attemptFetch(); // Initial call to start the polling
});
}
2024-07-17 17:00:43 -04:00
async function fetchMessagesForBuyOrders(apiCall, signature, senderPublicKey) {
2024-07-15 22:07:57 -04:00
let retryDelay = 2000; // Start with a 2-second delay
const maxDuration = 360000 * 2; // Maximum duration set to 12 minutes
const startTime = Date.now(); // Record the start time
2024-09-09 20:36:39 +03:00
let triedChatMessage = [];
2024-07-15 22:07:57 -04:00
// Promise to handle polling logic
2024-09-09 20:36:39 +03:00
await new Promise((res) => {
2024-07-17 17:00:43 -04:00
setTimeout(() => {
2024-09-09 20:36:39 +03:00
res();
2024-07-17 17:00:43 -04:00
}, 40000);
2024-09-09 20:36:39 +03:00
});
2024-07-15 22:07:57 -04:00
return new Promise((resolve, reject) => {
const attemptFetch = async () => {
if (Date.now() - startTime > maxDuration) {
return reject(new Error("Maximum polling time exceeded"));
}
try {
const response = await fetch(apiCall);
let data = await response.json();
2024-09-09 20:36:39 +03:00
data = data.filter(
(item) => !triedChatMessage.includes(item.signature)
);
2024-07-15 22:07:57 -04:00
if (data && data.length > 0) {
2024-09-09 20:36:39 +03:00
const encodedMessageObj = data[0];
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
2024-07-15 22:07:57 -04:00
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
2024-09-09 20:36:39 +03:00
publicKey: uint8PublicKey,
2024-07-15 22:07:57 -04:00
};
2024-09-09 20:36:39 +03:00
const decodedMessage = decryptChatMessage(
encodedMessageObj.data,
keyPair.privateKey,
senderPublicKey,
encodedMessageObj.reference
);
const parsedMessage = JSON.parse(decodedMessage);
2024-07-15 22:07:57 -04:00
if (parsedMessage?.extra?.chatRequestSignature === signature) {
resolve(parsedMessage);
} else {
2024-09-09 20:36:39 +03:00
triedChatMessage.push(encodedMessageObj.signature);
2024-07-15 22:07:57 -04:00
setTimeout(attemptFetch, retryDelay);
retryDelay = Math.min(retryDelay * 2, 360000); // Ensure delay does not exceed 6 minutes
2024-07-04 20:14:32 +03:00
}
2024-07-15 22:07:57 -04:00
// Resolve the promise when data is found
} else {
setTimeout(attemptFetch, retryDelay);
retryDelay = Math.min(retryDelay * 2, 360000); // Ensure delay does not exceed 6 minutes
}
} catch (error) {
reject(error); // Reject the promise on error
}
};
2024-07-04 20:14:32 +03:00
2024-07-15 22:07:57 -04:00
attemptFetch(); // Initial call to start the polling
2024-07-04 20:14:32 +03:00
});
}
2024-09-09 20:36:39 +03:00
async function listenForChatMessage({
nodeBaseUrl,
senderAddress,
senderPublicKey,
timestamp,
}) {
2024-07-04 20:14:32 +03:00
try {
let validApi = "";
const checkIfNodeBaseUrlIsAcceptable = apiEndpoints.find(
(item) => item === nodeBaseUrl
);
if (checkIfNodeBaseUrlIsAcceptable) {
validApi = checkIfNodeBaseUrlIsAcceptable;
} else {
validApi = await findUsableApi();
}
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const before = timestamp + 5000;
const after = timestamp - 5000;
const apiCall = `${validApi}/chat/messages?involving=${senderAddress}&involving=${address}&reverse=true&limit=1&before=${before}&after=${after}&encoding=BASE64`;
const encodedMessageObj = await fetchMessages(apiCall);
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
const resKeyPair = await getKeyPair();
const parsedData = JSON.parse(resKeyPair);
2024-07-15 22:07:57 -04:00
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
const uint8PublicKey = Base58.decode(parsedData.publicKey);
const keyPair = {
privateKey: uint8PrivateKey,
2024-09-09 20:36:39 +03:00
publicKey: uint8PublicKey,
2024-07-15 22:07:57 -04:00
};
2024-09-09 20:36:39 +03:00
const decodedMessage = decryptChatMessage(
encodedMessageObj.data,
keyPair.privateKey,
senderPublicKey,
encodedMessageObj.reference
);
2024-07-04 20:14:32 +03:00
return { secretCode: decodedMessage };
2024-04-14 14:57:30 +03:00
} catch (error) {
2024-09-09 20:36:39 +03:00
console.error(error);
2024-04-14 14:57:30 +03:00
throw new Error(error.message);
}
}
2024-09-09 20:36:39 +03:00
async function listenForChatMessageForBuyOrder({
nodeBaseUrl,
senderAddress,
senderPublicKey,
signature,
}) {
2024-07-12 23:44:21 -04:00
try {
let validApi = "";
const checkIfNodeBaseUrlIsAcceptable = apiEndpoints.find(
(item) => item === nodeBaseUrl
);
if (checkIfNodeBaseUrlIsAcceptable) {
validApi = checkIfNodeBaseUrlIsAcceptable;
} else {
validApi = await findUsableApi();
}
const wallet = await getSaveWallet();
const address = wallet.address0;
2024-09-09 20:36:39 +03:00
const before = Date.now() + 1200000;
const after = Date.now();
const apiCall = `${validApi}/chat/messages?involving=${senderAddress}&involving=${address}&reverse=true&limit=1&before=${before}&after=${after}&encoding=BASE64`;
const parsedMessageObj = await fetchMessagesForBuyOrders(
apiCall,
signature,
senderPublicKey
);
// const resKeyPair = await getKeyPair()
// const parsedData = JSON.parse(resKeyPair)
// const uint8PrivateKey = Base58.decode(parsedData.privateKey);
// const uint8PublicKey = Base58.decode(parsedData.publicKey);
// const keyPair = {
// privateKey: uint8PrivateKey,
// publicKey: uint8PublicKey
// };
// const decodedMessage = decryptChatMessage(encodedMessageObj.data, keyPair.privateKey, senderPublicKey, encodedMessageObj.reference)
// const parsedMessage = JSON.parse(decodedMessage)
chrome.tabs.query({}, function (tabs) {
tabs.forEach((tab) => {
chrome.tabs.sendMessage(tab.id, {
type: "RESPONSE_FOR_TRADES",
message: parsedMessageObj,
});
});
});
} catch (error) {
console.error(error);
throw new Error(error.message);
}
}
function removeDuplicateWindow(popupUrl){
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Filter to find popups matching the specific URL
const existingPopupsPending = windows.filter(
(w) =>
w.tabs &&
w.tabs.some(
(tab) => tab.pendingUrl && tab.pendingUrl.startsWith(popupUrl)
)
);
const existingPopups = windows.filter(
(w) =>
w.tabs &&
w.tabs.some(
(tab) => tab.url && tab.url.startsWith(popupUrl)
)
);
if(existingPopupsPending.length > 1){
chrome.windows.remove(existingPopupsPending?.[0]?.tabs?.[0]?.windowId, () => {
});
} else if(existingPopupsPending.length > 0 && existingPopups.length > 0){
chrome.windows.remove(existingPopupsPending?.[0]?.tabs?.[0]?.windowId, () => {
});
}
}
);
}
async function setChatHeads(data){
const wallet = await getSaveWallet();
const address = wallet.address0;
const dataString = JSON.stringify(data);
return await new Promise((resolve, reject) => {
chrome.storage.local.set({ [`chatheads-${address}`]: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
async function getTempPublish(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `tempPublish-${address}`
const res = await chrome.storage.local.get([key]);
const SIX_MINUTES = 6 * 60 * 1000; // 6 minutes in milliseconds
if (res?.[key]) {
const parsedData = JSON.parse(res[key]);
const currentTime = Date.now();
// Filter through each top-level key (e.g., "announcement") and then through its nested entries
const filteredData = Object.fromEntries(
Object.entries(parsedData).map(([category, entries]) => {
// Filter out entries inside each category that are older than 6 minutes
const filteredEntries = Object.fromEntries(
Object.entries(entries).filter(([entryKey, entryValue]) => {
return currentTime - entryValue.timestampSaved < SIX_MINUTES;
})
);
return [category, filteredEntries];
})
);
if (JSON.stringify(filteredData) !== JSON.stringify(parsedData)) {
const dataString = JSON.stringify(filteredData);
await chrome.storage.local.set({ [key]: dataString });
}
return filteredData;
} else {
return {};
}
}
async function saveTempPublish({data, key}){
const existingTemp = await getTempPublish()
const wallet = await getSaveWallet();
const address = wallet.address0;
const newTemp = {
...existingTemp,
[key]: {
...(existingTemp[key] || {}),
[data.identifier] : {
data,
timestampSaved: Date.now()
}
}
}
const dataString = JSON.stringify(newTemp);
return await new Promise((resolve, reject) => {
chrome.storage.local.set({ [`tempPublish-${address}`]: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(newTemp[key]);
}
});
});
}
async function setChatHeadsDirect(data){
const wallet = await getSaveWallet();
const address = wallet.address0;
const dataString = JSON.stringify(data);
return await new Promise((resolve, reject) => {
chrome.storage.local.set({ [`chatheads-direct-${address}`]: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
async function getTimestampEnterChat(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `enter-chat-timestamp-${address}`
const res = await chrome.storage.local.get([key]);
if (res?.[key]) {
const parsedData = JSON.parse(res[key])
return parsedData;
} else {
return {}
}
}
async function getTimestampGroupAnnouncement(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `group-announcement-${address}`
const res = await chrome.storage.local.get([key]);
if (res?.[key]) {
const parsedData = JSON.parse(res[key])
return parsedData;
} else {
return {}
}
}
async function addTimestampGroupAnnouncement({groupId, timestamp, seenTimestamp}){
const wallet = await getSaveWallet();
const address = wallet.address0;
const data = await getTimestampGroupAnnouncement() || {}
data[groupId] = {
notification: timestamp,
seentimestamp: seenTimestamp ? true : false
}
const dataString = JSON.stringify(data);
return await new Promise((resolve, reject) => {
chrome.storage.local.set({ [`group-announcement-${address}`]: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
async function addTimestampEnterChat({groupId, timestamp}){
const wallet = await getSaveWallet();
const address = wallet.address0;
const data = await getTimestampEnterChat()
data[groupId] = timestamp
const dataString = JSON.stringify(data);
return await new Promise((resolve, reject) => {
chrome.storage.local.set({ [`enter-chat-timestamp-${address}`]: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
2024-07-15 22:07:57 -04:00
});
2024-09-09 20:36:39 +03:00
});
}
async function notifyAdminRegenerateSecretKey({groupName, adminAddress}){
const wallet = await getSaveWallet();
const address = wallet.address0;
const name = await getNameInfo(address)
const nameOrAddress = name || address
await sendChatDirect({
directTo: adminAddress,
typeMessage: undefined,
chatReference: undefined,
messageText: `<p>Member ${nameOrAddress} has requested that you regenerate the group's secret key. Group: ${groupName}</p>`
})
return true
}
async function getChatHeads(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `chatheads-${address}`
const res = await chrome.storage.local.get([key]);
if (res?.[key]) {
const parsedData = JSON.parse(res[key])
return parsedData;
} else {
throw new Error("No Chatheads saved");
}
2024-07-12 23:44:21 -04:00
}
2024-09-09 20:36:39 +03:00
async function getChatHeadsDirect(){
const wallet = await getSaveWallet();
const address = wallet.address0;
const key = `chatheads-direct-${address}`
const res = await chrome.storage.local.get([key]);
if (res?.[key]) {
const parsedData = JSON.parse(res[key])
return parsedData;
} else {
throw new Error("No Chatheads saved");
}
}
2024-04-14 14:57:30 +03:00
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request) {
switch (request.action) {
case "version":
// Example: respond with the version
sendResponse({ version: "1.0" });
break;
case "storeWalletInfo":
chrome.storage.local.set({ walletInfo: request.wallet }, () => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
} else {
sendResponse({ result: "Data saved successfully" });
}
});
break;
case "getWalletInfo":
2024-09-09 20:36:39 +03:00
getKeyPair()
.then(() => {
chrome.storage.local.get(["walletInfo"], (result) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
} else if (result.walletInfo) {
sendResponse({ walletInfo: result.walletInfo });
} else {
sendResponse({ error: "No wallet info found" });
}
});
})
.catch((error) => {
sendResponse({ error: error.message });
2024-07-15 22:07:57 -04:00
});
2024-04-14 14:57:30 +03:00
break;
case "validApi":
findUsableApi()
.then((usableApi) => {
console.log("Usable API:", usableApi);
})
.catch((error) => {
console.error(error.message);
});
case "name":
getNameInfo()
.then((name) => {
sendResponse(name);
})
.catch((error) => {
console.error(error.message);
});
break;
case "userInfo":
getUserInfo()
.then((name) => {
2024-09-09 20:36:39 +03:00
2024-04-14 14:57:30 +03:00
sendResponse(name);
})
.catch((error) => {
sendResponse({ error: "User not authenticated" });
console.error(error.message);
});
break;
2024-09-09 20:36:39 +03:00
case "decryptWallet":
{
const { password, wallet } = request.payload;
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
decryptWallet({
password,
wallet,
walletVersion,
2024-07-09 00:44:02 +03:00
})
2024-09-09 20:36:39 +03:00
.then((hasDecrypted) => {
sendResponse(hasDecrypted);
})
.catch((error) => {
sendResponse({ error: error?.message });
console.error(error.message);
});
}
2024-07-15 22:07:57 -04:00
2024-07-09 00:44:02 +03:00
break;
2024-04-14 14:57:30 +03:00
case "balance":
getBalanceInfo()
.then((balance) => {
sendResponse(balance);
})
.catch((error) => {
console.error(error.message);
});
break;
2024-09-09 20:36:39 +03:00
case "ltcBalance":
{
getLTCBalance()
.then((balance) => {
sendResponse(balance);
})
.catch((error) => {
console.error(error.message);
});
}
break;
2024-04-14 14:57:30 +03:00
case "sendCoin":
{
const { receiver, password, amount } = request.payload;
sendCoin({ receiver, password, amount })
2024-09-09 20:36:39 +03:00
.then(({res}) => {
if(!res?.success){
sendResponse({ error: res?.data?.message });
return
}
2024-04-14 14:57:30 +03:00
sendResponse(true);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
2024-09-09 20:36:39 +03:00
case "inviteToGroup":
{
const { groupId, qortalAddress, inviteTime } = request.payload;
inviteToGroup({ groupId, qortalAddress, inviteTime })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
2024-07-04 20:14:32 +03:00
2024-09-09 20:36:39 +03:00
break;
case "saveTempPublish":
{
const { data, key } = request.payload;
saveTempPublish({ data, key })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
return true
}
break;
case "getTempPublish":
{
getTempPublish()
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "createGroup":
{
const { groupName, groupDescription, groupType, groupApprovalThreshold, minBlock, maxBlock } = request.payload;
createGroup({ groupName, groupDescription, groupType, groupApprovalThreshold, minBlock, maxBlock })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
2024-07-09 00:44:02 +03:00
2024-09-09 20:36:39 +03:00
break;
case "cancelInvitationToGroup":
{
const { groupId, qortalAddress } = request.payload;
cancelInvitationToGroup({ groupId, qortalAddress })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "leaveGroup":
{
const { groupId } = request.payload;
leaveGroup({ groupId })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "joinGroup":
{
const { groupId } = request.payload;
joinGroup({ groupId })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "kickFromGroup":
{
const { groupId, qortalAddress, rBanReason } = request.payload;
kickFromGroup({ groupId, qortalAddress, rBanReason })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "banFromGroup":
{
const { groupId, qortalAddress, rBanReason, rBanTime } = request.payload;
banFromGroup({ groupId, qortalAddress, rBanReason, rBanTime })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "cancelBan":
{
const { groupId, qortalAddress } = request.payload;
cancelBan({ groupId, qortalAddress})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "registerName":
{
const { name } = request.payload;
registerName({ name})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "makeAdmin":
{
const { groupId, qortalAddress } = request.payload;
makeAdmin({ groupId, qortalAddress})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "removeAdmin":
{
const { groupId, qortalAddress } = request.payload;
removeAdmin({ groupId, qortalAddress})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
}
break;
case "oauth": {
const { nodeBaseUrl, senderAddress, senderPublicKey, timestamp } =
request.payload;
listenForChatMessage({
nodeBaseUrl,
senderAddress,
senderPublicKey,
timestamp,
})
2024-07-04 20:14:32 +03:00
.then(({ secretCode }) => {
sendResponse(secretCode);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
2024-09-09 20:36:39 +03:00
case "setChatHeads": {
const { data} =
request.payload;
setChatHeads({
data
})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "getChatHeads": {
getChatHeads()
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "notification": {
const notificationId = 'chat_notification_' + Date.now(); // Create a unique ID
const { } =
request.payload;
chrome.notifications.create(notificationId, {
type: 'basic',
iconUrl: 'qort.png', // Add an appropriate icon for chat notifications
title: 'New Group Message!',
message: 'You have received a new message from one of your groups',
priority: 2, // Use the maximum priority to ensure it's noticeable
// buttons: [
// { title: 'Go to group' }
// ]
});
// Set a timeout to clear the notification after 'timeout' milliseconds
setTimeout(() => {
chrome.notifications.clear(notificationId);
}, 3000);
sendResponse(true)
break;
}
case "addTimestampEnterChat": {
const { groupId, timestamp } =
request.payload;
addTimestampEnterChat({groupId, timestamp})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "setApiKey": {
const { payload } = request;
// Save the apiKey in chrome.storage.local for persistence
chrome.storage.local.set({ apiKey: payload }, () => {
sendResponse(true)
});
return true
break;
}
case "getApiKey": {
getApiKeyFromStorage()
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
return true
break;
}
case "notifyAdminRegenerateSecretKey": {
const { groupName, adminAddress } =
request.payload;
notifyAdminRegenerateSecretKey({groupName, adminAddress})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "addGroupNotificationTimestamp": {
const { groupId, timestamp } =
request.payload;
addTimestampGroupAnnouncement({groupId, timestamp, seenTimestamp: true})
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "getTimestampEnterChat": {
getTimestampEnterChat()
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
case "getGroupNotificationTimestamp": {
getTimestampGroupAnnouncement()
.then((res) => {
sendResponse(res);
})
.catch((error) => {
sendResponse({ error: error.message });
console.error(error.message);
});
break;
}
2024-04-14 14:57:30 +03:00
case "authentication":
{
getSaveWallet()
.then(() => {
sendResponse(true);
})
.catch((error) => {
2024-09-09 20:36:39 +03:00
const popupUrl = chrome.runtime.getURL("index.html?secondary=true");
2024-04-14 14:57:30 +03:00
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) =>
w.tabs &&
w.tabs.some(
(tab) => tab.url && tab.url.startsWith(popupUrl)
)
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
} else {
// No existing popup found, create a new one
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const windowHeight = 500; // Your window height
const windowWidth = 400; // Your window width
// Calculate left position for the window to appear on the right of the screen
const leftPosition = screenWidth - windowWidth;
// Calculate top position for the window, adjust as desired
const topPosition =
(primaryDisplay.bounds.height - windowHeight) / 2;
chrome.windows.create({
2024-09-09 20:36:39 +03:00
url: chrome.runtime.getURL("index.html?secondary=true"),
2024-04-14 14:57:30 +03:00
type: "popup",
width: windowWidth,
height: windowHeight,
left: leftPosition,
top: 0,
2024-09-09 20:36:39 +03:00
} , () => {
removeDuplicateWindow(popupUrl)
2024-04-14 14:57:30 +03:00
});
});
}
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
action: "SET_COUNTDOWN",
payload: request.timeout ? 0.75 * request.timeout : 60,
});
chrome.runtime.sendMessage({
action: "UPDATE_STATE_REQUEST_AUTHENTICATION",
payload: {
hostname,
interactionId,
},
});
}, 500);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
let intervalId = null;
const startTime = Date.now();
const checkInterval = 3000; // Check every 3 seconds
2024-07-04 20:14:32 +03:00
const timeout = request.timeout
? 0.75 * (request.timeout * 1000)
: 60000; // Stop after 15 seconds
2024-04-14 14:57:30 +03:00
const checkFunction = () => {
getSaveWallet()
.then(() => {
clearInterval(intervalId); // Stop checking
sendResponse(true); // Perform the success action
chrome.runtime.sendMessage({
action: "closePopup",
});
})
.catch((error) => {
// Handle error if needed
});
if (Date.now() - startTime > timeout) {
sendResponse({
error: "User has not authenticated, try again.",
});
clearInterval(intervalId); // Stop checking due to timeout
2024-09-09 20:36:39 +03:00
2024-04-14 14:57:30 +03:00
// Handle timeout situation if needed
}
};
intervalId = setInterval(checkFunction, checkInterval);
}
);
});
}
break;
2024-09-09 20:36:39 +03:00
case "buyOrder":
{
const { qortalAtAddress, hostname } = request.payload;
getTradeInfo(qortalAtAddress)
.then((crosschainAtInfo) => {
const popupUrl = chrome.runtime.getURL("index.html?secondary=true")
2024-04-14 14:57:30 +03:00
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) =>
w.tabs &&
w.tabs.some(
(tab) => tab.url && tab.url.startsWith(popupUrl)
)
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
} else {
// No existing popup found, create a new one
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const windowHeight = 500; // Your window height
const windowWidth = 400; // Your window width
// Calculate left position for the window to appear on the right of the screen
const leftPosition = screenWidth - windowWidth;
// Calculate top position for the window, adjust as desired
const topPosition =
(primaryDisplay.bounds.height - windowHeight) / 2;
chrome.windows.create({
2024-09-09 20:36:39 +03:00
url: chrome.runtime.getURL("index.html?secondary=true"),
2024-04-14 14:57:30 +03:00
type: "popup",
width: windowWidth,
height: windowHeight,
left: leftPosition,
top: 0,
2024-09-09 20:36:39 +03:00
}, () => {
removeDuplicateWindow(popupUrl)
2024-04-14 14:57:30 +03:00
});
});
}
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
action: "SET_COUNTDOWN",
payload: request.timeout ? 0.9 * request.timeout : 20,
});
chrome.runtime.sendMessage({
2024-09-09 20:36:39 +03:00
action: "UPDATE_STATE_REQUEST_BUY_ORDER",
2024-04-14 14:57:30 +03:00
payload: {
hostname,
2024-09-09 20:36:39 +03:00
crosschainAtInfo,
2024-04-14 14:57:30 +03:00
interactionId,
},
});
}, 500);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
}
);
2024-09-09 20:36:39 +03:00
})
.catch((error) => {
console.error(error.message);
});
}
break;
case "connection":
{
const { hostname } = request.payload;
connection(hostname)
.then((isConnected) => {
if (
Object.keys(isConnected)?.length > 0 &&
isConnected[hostname]
) {
sendResponse(true);
} else {
const popupUrl = chrome.runtime.getURL("index.html?secondary=true");
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) =>
w.tabs &&
w.tabs.some(
(tab) => tab.url && tab.url.startsWith(popupUrl)
)
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
} else {
// No existing popup found, create a new one
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const windowHeight = 500; // Your window height
const windowWidth = 400; // Your window width
// Calculate left position for the window to appear on the right of the screen
const leftPosition = screenWidth - windowWidth;
// Calculate top position for the window, adjust as desired
const topPosition =
(primaryDisplay.bounds.height - windowHeight) / 2;
chrome.windows.create({
url: popupUrl,
type: "popup",
width: windowWidth,
height: windowHeight,
left: leftPosition,
top: 0,
}, () => {
removeDuplicateWindow(popupUrl)
});
});
}
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
action: "SET_COUNTDOWN",
payload: request.timeout ? 0.9 * request.timeout : 20,
});
chrome.runtime.sendMessage({
action: "UPDATE_STATE_REQUEST_CONNECTION",
payload: {
hostname,
interactionId,
},
});
}, 500);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
}
);
}
})
.catch((error) => {
console.error(error.message);
});
}
2024-07-15 22:07:57 -04:00
2024-04-14 14:57:30 +03:00
break;
case "sendQort":
{
const { amount, hostname, address, description } = request.payload;
2024-09-09 20:36:39 +03:00
const popupUrl = chrome.runtime.getURL("index.html?secondary=true");
2024-04-14 14:57:30 +03:00
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) =>
w.tabs &&
w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl))
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
} else {
// No existing popup found, create a new one
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const windowHeight = 500; // Your window height
const windowWidth = 400; // Your window width
// Calculate left position for the window to appear on the right of the screen
const leftPosition = screenWidth - windowWidth;
// Calculate top position for the window, adjust as desired
const topPosition =
(primaryDisplay.bounds.height - windowHeight) / 2;
chrome.windows.create({
2024-09-09 20:36:39 +03:00
url: chrome.runtime.getURL("index.html?secondary=true"),
2024-04-14 14:57:30 +03:00
type: "popup",
width: windowWidth,
height: windowHeight,
left: leftPosition,
top: 0,
2024-09-09 20:36:39 +03:00
}, () => {
removeDuplicateWindow(popupUrl)
2024-04-14 14:57:30 +03:00
});
});
}
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
action: "SET_COUNTDOWN",
2024-04-15 22:38:00 +03:00
payload: (request.timeout ? request.timeout : 60) - 6,
2024-04-14 14:57:30 +03:00
});
chrome.runtime.sendMessage({
action: "UPDATE_STATE_CONFIRM_SEND_QORT",
payload: {
amount,
address,
hostname,
description,
interactionId,
},
});
}, 500);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
}
);
}
break;
case "responseToConnectionRequest":
{
const { hostname, isOkay } = request.payload;
const interactionId3 = request.payload.interactionId;
if (!isOkay) {
const originalSendResponse = pendingResponses.get(interactionId3);
if (originalSendResponse) {
originalSendResponse(false);
sendResponse(false);
}
} else {
const originalSendResponse = pendingResponses.get(interactionId3);
if (originalSendResponse) {
// Example of setting domain permission
chrome.storage.local.set({ [hostname]: true });
originalSendResponse(true);
sendResponse(true);
}
}
2024-07-09 07:27:22 +03:00
pendingResponses.delete(interactionId3);
2024-04-14 14:57:30 +03:00
}
break;
case "sendQortConfirmation":
const { password, amount, receiver, isDecline } = request.payload;
const interactionId2 = request.payload.interactionId;
// Retrieve the stored sendResponse callback
const originalSendResponse = pendingResponses.get(interactionId2);
if (originalSendResponse) {
if (isDecline) {
originalSendResponse({ error: "User has declined" });
sendResponse(false);
2024-07-09 07:27:22 +03:00
pendingResponses.delete(interactionId2);
2024-04-14 14:57:30 +03:00
return;
}
2024-07-09 00:44:02 +03:00
sendCoin({ password, amount, receiver }, true)
2024-04-14 14:57:30 +03:00
.then((res) => {
sendResponse(true);
// Use the sendResponse callback to respond to the original message
originalSendResponse(res);
2024-07-09 07:27:22 +03:00
// Remove the callback from the Map as it's no longer needed
pendingResponses.delete(interactionId2);
2024-04-15 22:30:49 +03:00
// chrome.runtime.sendMessage({
// action: "closePopup",
// });
2024-04-14 14:57:30 +03:00
})
.catch((error) => {
console.error(error.message);
2024-04-15 22:30:49 +03:00
sendResponse({ error: error.message });
2024-04-14 14:57:30 +03:00
originalSendResponse({ error: error.message });
});
2024-09-09 20:36:39 +03:00
}
2024-04-14 14:57:30 +03:00
2024-09-09 20:36:39 +03:00
break;
case "buyOrderConfirmation":
{
const { crosschainAtInfo, isDecline } = request.payload;
const interactionId2 = request.payload.interactionId;
// Retrieve the stored sendResponse callback
const originalSendResponse = pendingResponses.get(interactionId2);
if (originalSendResponse) {
if (isDecline) {
originalSendResponse({ error: "User has declined" });
sendResponse(false);
pendingResponses.delete(interactionId2);
return;
}
createBuyOrderTx({ crosschainAtInfo })
.then((res) => {
sendResponse(true);
originalSendResponse(res);
pendingResponses.delete(interactionId2);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
// originalSendResponse({ error: error.message });
});
}
2024-04-14 14:57:30 +03:00
}
2024-07-12 23:44:21 -04:00
break;
2024-09-09 20:36:39 +03:00
case "encryptAndPublishSymmetricKeyGroupChat": {
const { groupId, previousData, previousNumber } = request.payload;
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
encryptAndPublishSymmetricKeyGroupChat({
groupId,
previousData,
previousNumber,
})
.then(({data, numberOfMembers}) => {
sendResponse(data);
if(!previousData){
// first secret key of the group
sendChatGroup({
groupId,
typeMessage: undefined,
chatReference: undefined,
messageText: PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY,
})
.then(() => {})
.catch((error) => {
console.error('1',error.message);
});
return
}
sendChatNotification(data, groupId, previousData, numberOfMembers)
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
break;
}
case "publishGroupEncryptedResource": {
const { encryptedData, identifier } = request.payload;
publishGroupEncryptedResource({
encryptedData, identifier
})
.then((data) => {
sendResponse(data);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
return true
break;
}
case "handleActiveGroupDataFromSocket": {
const { groups, directs } = request.payload;
handleActiveGroupDataFromSocket({
groups, directs
})
.then((data) => {
sendResponse(true);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "getThreadActivity": {
checkThreads(true)
.then((data) => {
sendResponse(data);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "updateThreadActivity": {
const { threadId, qortalName, groupId, thread } = request.payload;
updateThreadActivity({threadId, qortalName, groupId, thread})
.then(() => {
sendResponse(true);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "decryptGroupEncryption": {
const { data } = request.payload;
decryptGroupEncryption({ data })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "encryptSingle": {
const { data, secretKeyObject } = request.payload;
encryptSingle({ data64: data, secretKeyObject: secretKeyObject })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "decryptSingle": {
const { data, secretKeyObject, skipDecodeBase64 } = request.payload;
decryptSingleFunc({ messages: data, secretKeyObject, skipDecodeBase64 })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "pauseAllQueues": {
pauseAllQueues()
sendResponse(res);
break;
break;
}
case "resumeAllQueues": {
resumeAllQueues()
sendResponse(res);
break;
}
2024-09-09 20:36:39 +03:00
case "decryptSingleForPublishes": {
const { data, secretKeyObject, skipDecodeBase64 } = request.payload;
decryptSingleForPublishes({ messages: data, secretKeyObject, skipDecodeBase64 })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "decryptDirect": {
const { data, involvingAddress } = request.payload;
decryptDirectFunc({ messages: data, involvingAddress })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
}
case "sendChatGroup": {
const {
groupId,
typeMessage = undefined,
chatReference = undefined,
messageText,
} = request.payload;
sendChatGroup({ groupId, typeMessage, chatReference, messageText })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
break;
2024-07-15 22:07:57 -04:00
}
2024-09-09 20:36:39 +03:00
case "sendChatDirect": {
const {
directTo,
typeMessage = undefined,
chatReference = undefined,
messageText,
} = request.payload;
sendChatDirect({ directTo, chatReference, messageText, typeMessage })
.then((res) => {
sendResponse(res);
})
.catch((error) => {
console.error(error.message);
sendResponse({ error: error.message });
});
2024-07-15 22:07:57 -04:00
2024-09-09 20:36:39 +03:00
break;
}
case "setupGroupWebsocket": {
checkNewMessages()
checkThreads()
// if(socket){
// if(groups){
// console.log('hasgroups1')
// chrome.runtime.sendMessage({
// action: "SET_GROUPS",
// payload: groups,
// });
// }
// if(directs){
// console.log('hasgroups1')
// chrome.runtime.sendMessage({
// action: "SET_DIRECTS",
// payload: directs,
// });
// }
// sendResponse(true)
// return
// }
// setTimeout(() => {
// // initWebsocketMessageGroup()
// listenForNewGroupAnnouncements()
// listenForThreadUpdates()
// }, 200);
sendResponse(true)
2024-07-12 23:44:21 -04:00
2024-04-14 14:57:30 +03:00
break;
2024-09-09 20:36:39 +03:00
}
2024-07-04 20:14:32 +03:00
case "logout":
{
2024-09-09 20:36:39 +03:00
try {
const logoutFunc = async()=> {
forceCloseWebSocket()
clearAllQueues()
2024-09-09 20:36:39 +03:00
if(interval){
// for announcement notification
clearInterval(interval)
}
const wallet = await getSaveWallet();
const address = wallet.address0;
const key1 = `tempPublish-${address}`
chrome.storage.local.remove(["keyPair", "walletInfo", "apiKey", "active-groups-directs", key1], () => {
2024-07-04 20:14:32 +03:00
if (chrome.runtime.lastError) {
// Handle error
console.error(chrome.runtime.lastError.message);
} else {
2024-07-15 22:07:57 -04:00
chrome.tabs.query({}, function (tabs) {
2024-09-09 20:36:39 +03:00
tabs.forEach((tab) => {
2024-07-15 22:07:57 -04:00
chrome.tabs.sendMessage(tab.id, { type: "LOGOUT" });
2024-07-09 06:58:45 +03:00
});
2024-07-15 22:07:57 -04:00
});
2024-07-04 20:14:32 +03:00
// Data removed successfully
sendResponse(true);
}
});
2024-09-09 20:36:39 +03:00
}
logoutFunc()
} catch (error) {
}
2024-07-04 20:14:32 +03:00
}
2024-04-14 14:57:30 +03:00
2024-07-04 20:14:32 +03:00
break;
2024-04-14 14:57:30 +03:00
}
}
return true;
});
2024-04-15 22:30:49 +03:00
2024-09-09 20:36:39 +03:00
// Function to save window position and size
const saveWindowBounds = (windowId) => {
chrome.windows.get(windowId, (window) => {
const { top, left, width, height } = window;
chrome.storage.local.set({
windowBounds: { top, left, width, height }
}, () => {
console.log('Window bounds saved:', { top, left, width, height });
});
});
};
// Function to restore window position and size
const restoreWindowBounds = (callback) => {
chrome.storage.local.get('windowBounds', (data) => {
if (data.windowBounds) {
callback(data.windowBounds);
} else {
callback(null); // No saved bounds, use default size/position
}
});
};
2024-04-14 14:57:30 +03:00
chrome.action.onClicked.addListener((tab) => {
2024-09-09 20:36:39 +03:00
const popupUrl = chrome.runtime.getURL("index.html?main=true");
2024-04-14 14:57:30 +03:00
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) =>
2024-09-09 20:36:39 +03:00
{
return w.tabs &&
w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl));
}
2024-04-14 14:57:30 +03:00
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
} else {
2024-09-09 20:36:39 +03:00
// No existing popup found, restore the saved bounds or create a new one
restoreWindowBounds((savedBounds) => {
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const screenHeight = primaryDisplay.bounds.height;
// Create a new window that uses the saved bounds if available
chrome.windows.create({
url: chrome.runtime.getURL("index.html?main=true"),
type: "popup",
width: savedBounds ? savedBounds.width : screenWidth,
height: savedBounds ? savedBounds.height : screenHeight,
left: savedBounds ? savedBounds.left : 0,
top: savedBounds ? savedBounds.top : 0,
}, (newWindow) => {
// Listen for changes in the window's size or position and save them
chrome.windows.onBoundsChanged.addListener((window) => {
if (window.id === newWindow.id) {
saveWindowBounds(newWindow.id);
}
});
// Save the final window bounds when the window is closed
chrome.windows.onRemoved.addListener((windowId) => {
if (windowId === newWindow.id) {
saveWindowBounds(windowId); // Save the position/size before its closed
}
});
});
2024-04-14 14:57:30 +03:00
});
});
}
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
2024-09-09 20:36:39 +03:00
action: "INITIATE_MAIN",
payload: {},
2024-04-14 14:57:30 +03:00
});
}, 500);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
}
);
});
2024-09-09 20:36:39 +03:00
const checkGroupList = async() => {
try {
const wallet = await getSaveWallet();
const address = wallet.address0;
const url = await createEndpoint(`/chat/active/${address}`);
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
const filteredGroups = data.groups?.filter(item => item?.groupId !== 0) || [];
const sortedGroups = filteredGroups.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
const sortedDirects = (data?.direct || []).filter(item =>
item?.name !== 'extension-proxy' && item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH'
).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
handleActiveGroupDataFromSocket({
groups: sortedGroups,
directs: sortedDirects
})
} catch (error) {
console.error(error)
} finally {
}
}
const checkActiveChatsForNotifications = async ()=> {
try {
const popupUrl = chrome.runtime.getURL("index.html?main=true");
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
(windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) => {
return w.tabs && w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl));
}
);
if (existingPopup) {
} else {
checkGroupList()
}
}
);
} catch (error) {
}
}
chrome.notifications.onClicked.addListener( (notificationId) => {
const popupUrl = chrome.runtime.getURL("index.html?main=true");
const isDirect = notificationId.includes('_type=direct_');
const isGroup = notificationId.includes('_type=group_');
const isGroupAnnouncement = notificationId.includes('_type=group-announcement_');
const isNewThreadPost = notificationId.includes('_type=thread-post_');
let isExisting = false
chrome.windows.getAll(
{ populate: true, windowTypes: ["popup"] },
async (windows) => {
// Attempt to find an existing popup window that has a tab with the correct URL
const existingPopup = windows.find(
(w) => {
return w.tabs && w.tabs.some((tab) => tab.url && tab.url.startsWith(popupUrl));
}
);
if (existingPopup) {
// If the popup exists but is minimized or not focused, focus it
chrome.windows.update(existingPopup.id, {
focused: true,
state: "normal",
});
isExisting = true
} else {
// No existing popup found, restore saved bounds or create a new one
restoreWindowBounds((savedBounds) => {
chrome.system.display.getInfo((displays) => {
// Assuming the primary display is the first one (adjust logic as needed)
const primaryDisplay = displays[0];
const screenWidth = primaryDisplay.bounds.width;
const screenHeight = primaryDisplay.bounds.height;
// Create a new window that takes up the full screen or uses saved bounds
chrome.windows.create({
url: chrome.runtime.getURL("index.html?main=true"),
type: "popup",
width: savedBounds ? savedBounds.width : screenWidth,
height: savedBounds ? savedBounds.height : screenHeight,
left: savedBounds ? savedBounds.left : 0,
top: savedBounds ? savedBounds.top : 0,
}, (newWindow) => {
// Listen for changes in the window's size or position and save them
chrome.windows.onBoundsChanged.addListener((window) => {
if (window.id === newWindow.id) {
saveWindowBounds(newWindow.id);
}
});
// Save the final window bounds when the window is closed
chrome.windows.onRemoved.addListener((windowId) => {
if (windowId === newWindow.id) {
saveWindowBounds(windowId); // Save the position/size before its closed
}
});
});
});
});
}
const activeData = await getStoredData('active-groups-directs') || { groups: [], directs: [] };
setTimeout(() => {
chrome.runtime.sendMessage({
action: "SET_GROUPS",
payload: activeData?.groups || [],
});
chrome.runtime.sendMessage({
action: "SET_DIRECTS",
payload: activeData?.directs || [],
});
}, isExisting ? 100 : 1000);
const interactionId = Date.now().toString(); // Simple example; consider a better unique ID
setTimeout(() => {
chrome.runtime.sendMessage({
action: "INITIATE_MAIN",
payload: {},
});
// Handle different types of notifications
if (isDirect) {
const fromValue = notificationId.split('_from=')[1];
chrome.runtime.sendMessage({
action: "NOTIFICATION_OPEN_DIRECT",
payload: { from: fromValue },
});
} else if (isGroup) {
const fromValue = notificationId.split('_from=')[1];
chrome.runtime.sendMessage({
action: "NOTIFICATION_OPEN_GROUP",
payload: { from: fromValue },
});
} else if (isGroupAnnouncement) {
const fromValue = notificationId.split('_from=')[1];
chrome.runtime.sendMessage({
action: "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP",
payload: { from: fromValue },
});
} else if (isNewThreadPost) {
const dataValue = notificationId.split('_data=')[1];
const dataParsed = JSON.parse(dataValue);
chrome.runtime.sendMessage({
action: "NOTIFICATION_OPEN_THREAD_NEW_POST",
payload: { data: dataParsed },
});
}
}, isExisting ? 400 : 3000);
// Store sendResponse callback with the interaction ID
pendingResponses.set(interactionId, sendResponse);
}
);
});
// Reconnect when service worker wakes up
chrome.runtime.onStartup.addListener(() => {
console.log("Service worker started up, reconnecting WebSocket...");
// initWebsocketMessageGroup();
// listenForNewGroupAnnouncements()
// listenForThreadUpdates()
});
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
console.log('Extension Installed');
// Perform tasks that should only happen on extension installation
// Example: Initialize WebSocket, set default settings, etc.
} else if (details.reason === chrome.runtime.OnInstalledReason.UPDATE) {
console.log('Extension Updated');
// Handle the update logic here (e.g., migrate settings)
} else if (details.reason === chrome.runtime.OnInstalledReason.CHROME_UPDATE) {
console.log('Chrome updated');
// Optional: Handle Chrome-specific updates if necessary
}
// Initialize WebSocket and other required listeners
// initWebsocketMessageGroup();
// listenForNewGroupAnnouncements();
// listenForThreadUpdates();
});
// Check if the alarm already exists before creating it
chrome.alarms.get("checkForNotifications", (existingAlarm) => {
if (!existingAlarm) {
// If the alarm does not exist, create it
chrome.alarms.create("checkForNotifications", { periodInMinutes: 4 });
}
});
chrome.alarms.onAlarm.addListener(async (alarm) => {
try {
if (alarm.name === "checkForNotifications") {
// initWebsocketMessageGroup(address);
const wallet = await getSaveWallet();
const address = wallet.address0;
if(!address) return
checkActiveChatsForNotifications()
checkNewMessages()
checkThreads()
}
} catch (error) {
}
});