move 'models' to status lib for clarity
This commit is contained in:
parent
f16de508ed
commit
73069255cd
|
@ -1,8 +1,8 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import json, eventemitter
|
import json, eventemitter
|
||||||
import ../../models/chat as chat_model
|
import ../../status/chat as chat_model
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../status/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import views/channels_list
|
import views/channels_list
|
||||||
import view
|
import view
|
||||||
import chronicles
|
import chronicles
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Tables
|
||||||
import views/channels_list
|
import views/channels_list
|
||||||
import views/message_list
|
import views/message_list
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../models/chat
|
import ../../status/chat
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Tables
|
||||||
import strformat
|
import strformat
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import ../../../models/chat
|
import ../../../status/chat
|
||||||
|
|
||||||
const accountColors* = [
|
const accountColors* = [
|
||||||
"#9B832F",
|
"#9B832F",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml, Tables
|
import NimQml, Tables
|
||||||
import ../../../models/chat
|
import ../../../status/chat
|
||||||
|
|
||||||
type
|
type
|
||||||
ChatMessageRoles {.pure.} = enum
|
ChatMessageRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ../../status/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import view
|
import view
|
||||||
import ../../models/accounts as AccountModel
|
import ../../status/accounts as AccountModel
|
||||||
import chronicles
|
import chronicles
|
||||||
import options
|
import options
|
||||||
import std/wrapnils
|
import std/wrapnils
|
||||||
|
|
|
@ -3,12 +3,12 @@ import Tables
|
||||||
import json
|
import json
|
||||||
import nimcrypto
|
import nimcrypto
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../status/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import ../../status/accounts as status_accounts
|
import ../../status/libstatus/accounts as status_accounts
|
||||||
import strformat
|
import strformat
|
||||||
import json_serialization
|
import json_serialization
|
||||||
import core
|
import core
|
||||||
import ../../models/accounts as AccountModel
|
import ../../status/accounts as AccountModel
|
||||||
|
|
||||||
type
|
type
|
||||||
AccountRoles {.pure.} = enum
|
AccountRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import chronicles
|
import chronicles
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import "../../status/core" as status
|
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../models/node
|
import ../../status/node
|
||||||
import view
|
import view
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ../../models/node
|
import ../../status/node
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type NodeView* = ref object of QObject
|
type NodeView* = ref object of QObject
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ../../status/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import ../../status/accounts as status_accounts
|
import ../../status/libstatus/accounts as status_accounts
|
||||||
import ../../models/accounts as AccountModel
|
import ../../status/accounts as AccountModel
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import view
|
import view
|
||||||
import chronicles
|
import chronicles
|
||||||
|
|
|
@ -2,11 +2,11 @@ import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import json
|
import json
|
||||||
import nimcrypto
|
import nimcrypto
|
||||||
import ../../status/types as status_types
|
import ../../status/libstatus/types as status_types
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import strformat
|
import strformat
|
||||||
import json_serialization
|
import json_serialization
|
||||||
import ../../models/accounts as AccountModel
|
import ../../status/accounts as AccountModel
|
||||||
|
|
||||||
type
|
type
|
||||||
AccountRoles {.pure.} = enum
|
AccountRoles {.pure.} = enum
|
||||||
|
|
|
@ -2,12 +2,12 @@ import NimQml
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import strformat
|
import strformat
|
||||||
import json
|
import json
|
||||||
import "../../status/core" as status
|
import "../../status/libstatus/core" as status
|
||||||
import ../../status/mailservers as status_mailservers
|
import ../../status/libstatus/mailservers as status_mailservers
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import view
|
import view
|
||||||
import "../../status/types" as status_types
|
import "../../status/libstatus/types" as status_types
|
||||||
import ../../models/profile
|
import ../../status/profile
|
||||||
|
|
||||||
type ProfileController* = object
|
type ProfileController* = object
|
||||||
view*: ProfileView
|
view*: ProfileView
|
||||||
|
|
|
@ -2,7 +2,7 @@ import NimQml
|
||||||
import views/mailservers_list
|
import views/mailservers_list
|
||||||
import views/contact_list
|
import views/contact_list
|
||||||
import views/profile_info
|
import views/profile_info
|
||||||
import ../../models/profile
|
import ../../status/profile
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ProfileView* = ref object of QObject
|
type ProfileView* = ref object of QObject
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import strformat
|
import strformat
|
||||||
import ../../../models/profile
|
import ../../../status/profile
|
||||||
|
|
||||||
type
|
type
|
||||||
ContactRoles {.pure.} = enum
|
ContactRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import ../../../models/profile
|
import ../../../status/profile
|
||||||
|
|
||||||
type
|
type
|
||||||
MailServerRoles {.pure.} = enum
|
MailServerRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ../../../models/profile
|
import ../../../status/profile
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type ProfileInfoView* = ref object of QObject
|
type ProfileInfoView* = ref object of QObject
|
||||||
|
|
|
@ -5,8 +5,8 @@ import strutils
|
||||||
import chronicles
|
import chronicles
|
||||||
|
|
||||||
import view
|
import view
|
||||||
import ../../status/wallet as status_wallet
|
import ../../status/libstatus/wallet as status_wallet
|
||||||
import ../../models/wallet
|
import ../../status/wallet
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
|
|
||||||
type WalletController* = ref object of SignalSubscriber
|
type WalletController* = ref object of SignalSubscriber
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import views/asset_list
|
import views/asset_list
|
||||||
import ../../models/wallet
|
import ../../status/wallet
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import Tables
|
import Tables
|
||||||
import strformat
|
import strformat
|
||||||
import ../../../models/wallet
|
import ../../../status/wallet
|
||||||
|
|
||||||
type
|
type
|
||||||
AssetRoles {.pure.} = enum
|
AssetRoles {.pure.} = enum
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
import ../status/accounts as status_accounts
|
|
||||||
import ../status/types
|
|
||||||
import options
|
|
||||||
|
|
||||||
type
|
|
||||||
AccountModel* = ref object
|
|
||||||
generatedAddresses*: seq[GeneratedAccount]
|
|
||||||
nodeAccounts*: seq[NodeAccount]
|
|
||||||
currentAccount*: Account
|
|
||||||
|
|
||||||
proc newAccountModel*(): AccountModel =
|
|
||||||
result = AccountModel()
|
|
||||||
result.currentAccount = nil
|
|
||||||
|
|
||||||
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
|
||||||
var accounts = status_accounts.generateAddresses()
|
|
||||||
for account in accounts.mitems:
|
|
||||||
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
|
||||||
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
|
||||||
self.generatedAddresses.add(account)
|
|
||||||
self.generatedAddresses
|
|
||||||
|
|
||||||
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
|
|
||||||
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
|
|
||||||
self.currentAccount = currentNodeAccount.toAccount
|
|
||||||
result = status_accounts.login(currentNodeAccount, password)
|
|
||||||
|
|
||||||
proc storeAccountAndLogin*(self: AccountModel, selectedAccountIndex: int, password: string): Account =
|
|
||||||
let generatedAccount: GeneratedAccount = self.generatedAddresses[selectedAccountIndex]
|
|
||||||
result = status_accounts.setupAccount(generatedAccount, password)
|
|
||||||
self.currentAccount = generatedAccount.toAccount
|
|
|
@ -1,103 +0,0 @@
|
||||||
import eventemitter, sets, json, strutils
|
|
||||||
import sequtils
|
|
||||||
import ../status/utils
|
|
||||||
import ../status/chat as status_chat
|
|
||||||
import chronicles
|
|
||||||
import ../signals/types
|
|
||||||
import chat/chat_item
|
|
||||||
import chat/chat_message
|
|
||||||
import tables
|
|
||||||
export chat_item
|
|
||||||
export chat_message
|
|
||||||
|
|
||||||
type MsgArgs* = ref object of Args
|
|
||||||
message*: string
|
|
||||||
chatId*: string
|
|
||||||
payload*: JsonNode
|
|
||||||
|
|
||||||
type ChannelArgs* = ref object of Args
|
|
||||||
channel*: string
|
|
||||||
chatTypeInt*: ChatType
|
|
||||||
|
|
||||||
type ChatArgs* = ref object of Args
|
|
||||||
chats*: seq[Chat]
|
|
||||||
|
|
||||||
type
|
|
||||||
ChatModel* = ref object
|
|
||||||
events*: EventEmitter
|
|
||||||
channels*: HashSet[string]
|
|
||||||
filters*: Table[string, string]
|
|
||||||
|
|
||||||
proc newChatModel*(): ChatModel =
|
|
||||||
result = ChatModel()
|
|
||||||
result.events = createEventEmitter()
|
|
||||||
result.channels = initHashSet[string]()
|
|
||||||
result.filters = initTable[string, string]()
|
|
||||||
|
|
||||||
proc delete*(self: ChatModel) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
|
||||||
result = self.channels.contains(chatId)
|
|
||||||
|
|
||||||
proc getActiveChannel*(self: ChatModel): string =
|
|
||||||
if (self.channels.len == 0): "" else: self.channels.toSeq[self.channels.len - 1]
|
|
||||||
|
|
||||||
proc join*(self: ChatModel, chatId: string, chatTypeInt: ChatType, isNewChat: bool = true) =
|
|
||||||
if self.hasChannel(chatId): return
|
|
||||||
|
|
||||||
self.channels.incl chatId
|
|
||||||
|
|
||||||
let generatedSymKey = status_chat.generateSymKeyFromPassword()
|
|
||||||
|
|
||||||
# TODO get this from the connection or something
|
|
||||||
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
|
|
||||||
|
|
||||||
let oneToOne = isOneToOneChat(chatId)
|
|
||||||
|
|
||||||
if isNewChat: status_chat.saveChat(chatId, oneToOne)
|
|
||||||
|
|
||||||
let filterResult = status_chat.loadFilters(chatId = chatId, oneToOne = oneToOne)
|
|
||||||
|
|
||||||
status_chat.chatMessages(chatId)
|
|
||||||
|
|
||||||
let parsedResult = parseJson(filterResult)["result"]
|
|
||||||
|
|
||||||
var topics = newSeq[string](0)
|
|
||||||
for topicObj in parsedResult:
|
|
||||||
if (($topicObj["chatId"]).strip(chars = {'"'}) == chatId):
|
|
||||||
topics.add(($topicObj["topic"]).strip(chars = {'"'}))
|
|
||||||
|
|
||||||
if(not self.filters.hasKey(chatId)): self.filters[chatId] = topicObj["filterId"].getStr
|
|
||||||
|
|
||||||
if (topics.len == 0):
|
|
||||||
warn "No topic found for the chat. Cannot load past messages"
|
|
||||||
else:
|
|
||||||
status_chat.requestMessages(topics, generatedSymKey, peer, 20)
|
|
||||||
|
|
||||||
self.events.emit("channelJoined", ChannelArgs(channel: chatId, chatTypeInt: chatTypeInt))
|
|
||||||
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
|
|
||||||
|
|
||||||
proc load*(self: ChatModel) =
|
|
||||||
let chatList = status_chat.loadChats()
|
|
||||||
for chat in chatList:
|
|
||||||
# TODO: use correct type of chat instead of hardcoded 2 (assumes it's only public chats)
|
|
||||||
self.join(chat.id, ChatType.Public, false)
|
|
||||||
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
|
|
||||||
|
|
||||||
proc leave*(self: ChatModel, chatId: string) =
|
|
||||||
status_chat.removeFilters(chatId, self.filters[chatId])
|
|
||||||
status_chat.deactivateChat(chatId)
|
|
||||||
# TODO: REMOVE MAILSERVER TOPIC
|
|
||||||
# TODO: REMOVE HISTORY
|
|
||||||
|
|
||||||
self.filters.del(chatId)
|
|
||||||
self.channels.excl(chatId)
|
|
||||||
self.events.emit("channelLeft", ChannelArgs(channel: chatId))
|
|
||||||
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
|
|
||||||
|
|
||||||
proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
|
|
||||||
var sentMessage = status_chat.sendChatMessage(chatId, msg)
|
|
||||||
var parsedMessage = parseJson(sentMessage)["result"]["chats"][0]["lastMessage"]
|
|
||||||
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
|
|
||||||
sentMessage
|
|
|
@ -1,40 +0,0 @@
|
||||||
import eventemitter
|
|
||||||
import json
|
|
||||||
import strformat
|
|
||||||
import strutils
|
|
||||||
import ../status/wallet as status_wallet
|
|
||||||
|
|
||||||
type Asset* = ref object
|
|
||||||
name*, symbol*, value*, fiatValue*, image*: string
|
|
||||||
|
|
||||||
type WalletModel* = ref object
|
|
||||||
events*: EventEmitter
|
|
||||||
|
|
||||||
proc newWalletModel*(): WalletModel =
|
|
||||||
result = WalletModel()
|
|
||||||
result.events = createEventEmitter()
|
|
||||||
|
|
||||||
proc delete*(self: WalletModel) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc sendTransaction*(self: WalletModel, from_value: string, to: string, value: string, password: string): string =
|
|
||||||
status_wallet.sendTransaction(from_value, to, value, password)
|
|
||||||
|
|
||||||
proc getEthBalance*(self: WalletModel, address: string): string =
|
|
||||||
var balance = status_wallet.getBalance(address)
|
|
||||||
echo(fmt"balance in hex: {balance}")
|
|
||||||
|
|
||||||
# 2. convert balance to eth
|
|
||||||
var eth_value = status_wallet.hex2Eth(balance)
|
|
||||||
echo(fmt"balance in eth: {eth_value}")
|
|
||||||
eth_value
|
|
||||||
|
|
||||||
proc getFiatValue*(self: WalletModel, eth_balance: string, symbol: string, fiat_symbol: string): float =
|
|
||||||
# 3. get usd price of 1 eth
|
|
||||||
var usd_eth_price = status_wallet.getPrice("ETH", "USD")
|
|
||||||
echo(fmt"usd_price: {usd_eth_price}")
|
|
||||||
|
|
||||||
# 4. convert balance to usd
|
|
||||||
var usd_balance = parseFloat(eth_balance) * parseFloat(usd_eth_price)
|
|
||||||
echo(fmt"balance in usd: {usd_balance}")
|
|
||||||
usd_balance
|
|
|
@ -8,12 +8,12 @@ import signals/core as signals
|
||||||
import app/onboarding/core as onboarding
|
import app/onboarding/core as onboarding
|
||||||
import app/login/core as login
|
import app/login/core as login
|
||||||
import state
|
import state
|
||||||
import status/accounts as status_accounts
|
import status/libstatus/accounts as status_accounts
|
||||||
import status/core as status_core
|
import status/libstatus/core as status_core
|
||||||
import status/types as types
|
import status/libstatus/types as types
|
||||||
import status/libstatus
|
import status/libstatus/libstatus
|
||||||
|
import status/libstatus/types
|
||||||
import state
|
import state
|
||||||
import status/types
|
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import json_serialization
|
import json_serialization
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import NimQml
|
import NimQml
|
||||||
import ../status/types as status_types
|
import ../status/libstatus/types as status_types
|
||||||
import tables
|
import tables
|
||||||
import json
|
import json
|
||||||
import types
|
import types
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
import chronicles
|
import chronicles
|
||||||
import ../status/types
|
import ../status/libstatus/types
|
||||||
import json_serialization
|
import json_serialization
|
||||||
|
|
||||||
type SignalSubscriber* = ref object of RootObj
|
type SignalSubscriber* = ref object of RootObj
|
||||||
|
|
|
@ -1,161 +1,31 @@
|
||||||
import libstatus
|
import libstatus/accounts as status_accounts
|
||||||
import core
|
import libstatus/types
|
||||||
import json
|
import options
|
||||||
import utils
|
|
||||||
import accounts/constants
|
|
||||||
import nimcrypto
|
|
||||||
import os
|
|
||||||
import uuids
|
|
||||||
import types
|
|
||||||
import json_serialization
|
|
||||||
import chronicles
|
|
||||||
|
|
||||||
proc queryAccounts*(): string =
|
type
|
||||||
var response = callPrivateRPC("eth_accounts")
|
AccountModel* = ref object
|
||||||
result = parseJson(response)["result"][0].getStr()
|
generatedAddresses*: seq[GeneratedAccount]
|
||||||
|
nodeAccounts*: seq[NodeAccount]
|
||||||
|
currentAccount*: Account
|
||||||
|
|
||||||
proc generateAddresses*(): seq[GeneratedAccount] =
|
proc newAccountModel*(): AccountModel =
|
||||||
let multiAccountConfig = %* {
|
result = AccountModel()
|
||||||
"n": 5,
|
result.currentAccount = nil
|
||||||
"mnemonicPhraseLength": 12,
|
|
||||||
"bip39Passphrase": "",
|
|
||||||
"paths": [PATH_WHISPER, PATH_WALLET_ROOT, PATH_DEFAULT_WALLET]
|
|
||||||
}
|
|
||||||
result = Json.decode($libstatus.multiAccountGenerateAndDeriveAddresses($multiAccountConfig), seq[GeneratedAccount])
|
|
||||||
|
|
||||||
proc generateAlias*(publicKey: string): string =
|
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
|
||||||
result = $libstatus.generateAlias(publicKey.toGoString)
|
var accounts = status_accounts.generateAddresses()
|
||||||
|
for account in accounts.mitems:
|
||||||
|
account.name = status_accounts.generateAlias(account.derived.whisper.publicKey)
|
||||||
|
account.photoPath = status_accounts.generateIdenticon(account.derived.whisper.publicKey)
|
||||||
|
self.generatedAddresses.add(account)
|
||||||
|
self.generatedAddresses
|
||||||
|
|
||||||
proc generateIdenticon*(publicKey: string): string =
|
proc login*(self: AccountModel, selectedAccountIndex: int, password: string): NodeAccount =
|
||||||
result = $libstatus.identicon(publicKey.toGoString)
|
let currentNodeAccount = self.nodeAccounts[selectedAccountIndex]
|
||||||
|
self.currentAccount = currentNodeAccount.toAccount
|
||||||
|
result = status_accounts.login(currentNodeAccount, password)
|
||||||
|
|
||||||
proc ensureDir(dirname: string) =
|
proc storeAccountAndLogin*(self: AccountModel, selectedAccountIndex: int, password: string): Account =
|
||||||
if not existsDir(dirname):
|
let generatedAccount: GeneratedAccount = self.generatedAddresses[selectedAccountIndex]
|
||||||
# removeDir(dirname)
|
result = status_accounts.setupAccount(generatedAccount, password)
|
||||||
createDir(dirname)
|
self.currentAccount = generatedAccount.toAccount
|
||||||
|
|
||||||
proc initNodeAccounts*(): seq[NodeAccount] =
|
|
||||||
const datadir = "./data/"
|
|
||||||
const keystoredir = "./data/keystore/"
|
|
||||||
const nobackupdir = "./noBackup/"
|
|
||||||
|
|
||||||
ensureDir(datadir)
|
|
||||||
ensureDir(keystoredir)
|
|
||||||
ensureDir(nobackupdir)
|
|
||||||
|
|
||||||
discard $libstatus.initKeystore(keystoredir);
|
|
||||||
let strNodeAccounts = $libstatus.openAccounts(datadir);
|
|
||||||
result = Json.decode(strNodeAccounts, seq[NodeAccount])
|
|
||||||
|
|
||||||
proc saveAccountAndLogin*(
|
|
||||||
multiAccounts: MultiAccounts,
|
|
||||||
alias: string,
|
|
||||||
identicon: string,
|
|
||||||
accountData: string,
|
|
||||||
password: string,
|
|
||||||
configJSON: string,
|
|
||||||
settingsJSON: string): Account =
|
|
||||||
let hashedPassword = "0x" & $keccak_256.digest(password)
|
|
||||||
let subaccountData = %* [
|
|
||||||
{
|
|
||||||
"public-key": multiAccounts.defaultWallet.publicKey,
|
|
||||||
"address": multiAccounts.defaultWallet.address,
|
|
||||||
"color": "#4360df",
|
|
||||||
"wallet": true,
|
|
||||||
"path": constants.PATH_DEFAULT_WALLET,
|
|
||||||
"name": "Status account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"public-key": multiAccounts.whisper.publicKey,
|
|
||||||
"address": multiAccounts.whisper.address,
|
|
||||||
"name": alias,
|
|
||||||
"photo-path": identicon,
|
|
||||||
"path": constants.PATH_WHISPER,
|
|
||||||
"chat": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
var savedResult = $libstatus.saveAccountAndLogin(accountData, hashedPassword, settingsJSON, configJSON, $subaccountData)
|
|
||||||
let parsedSavedResult = savedResult.parseJson
|
|
||||||
let error = parsedSavedResult["error"].getStr
|
|
||||||
|
|
||||||
if error == "":
|
|
||||||
debug "Account saved succesfully"
|
|
||||||
result = Account(name: alias, photoPath: identicon)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise newException(LoginError, "Error saving account and logging in: " & error)
|
|
||||||
|
|
||||||
proc generateMultiAccounts*(account: GeneratedAccount, password: string): MultiAccounts =
|
|
||||||
let hashedPassword = "0x" & $keccak_256.digest(password)
|
|
||||||
let multiAccount = %* {
|
|
||||||
"accountID": account.id,
|
|
||||||
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET],
|
|
||||||
"password": hashedPassword
|
|
||||||
}
|
|
||||||
var response = $libstatus.multiAccountStoreDerivedAccounts($multiAccount);
|
|
||||||
result = Json.decode($response, MultiAccounts)
|
|
||||||
|
|
||||||
proc getAccountData*(account: GeneratedAccount, alias: string, identicon: string): JsonNode =
|
|
||||||
result = %* {
|
|
||||||
"name": alias,
|
|
||||||
"address": account.address,
|
|
||||||
"photo-path": identicon,
|
|
||||||
"key-uid": account.keyUid,
|
|
||||||
"keycard-pairing": nil
|
|
||||||
}
|
|
||||||
|
|
||||||
proc getAccountSettings*(account: GeneratedAccount, alias: string, identicon: string, multiAccounts: MultiAccounts, defaultNetworks: JsonNode): JsonNode =
|
|
||||||
result = %* {
|
|
||||||
"key-uid": account.keyUid,
|
|
||||||
"mnemonic": account.mnemonic,
|
|
||||||
"public-key": multiAccounts.whisper.publicKey,
|
|
||||||
"name": alias,
|
|
||||||
"address": account.address,
|
|
||||||
"eip1581-address": multiAccounts.eip1581.address,
|
|
||||||
"dapps-address": multiAccounts.defaultWallet.address,
|
|
||||||
"wallet-root-address": multiAccounts.walletRoot.address,
|
|
||||||
"preview-privacy?": true,
|
|
||||||
"signing-phrase": generateSigningPhrase(3),
|
|
||||||
"log-level": "INFO",
|
|
||||||
"latest-derived-path": 0,
|
|
||||||
"networks/networks": defaultNetworks,
|
|
||||||
"currency": "usd",
|
|
||||||
"photo-path": identicon,
|
|
||||||
"waku-enabled": true,
|
|
||||||
"wallet/visible-tokens": {
|
|
||||||
"mainnet": ["SNT"]
|
|
||||||
},
|
|
||||||
"appearance": 0,
|
|
||||||
"networks/current-network": "mainnet_rpc",
|
|
||||||
"installation-id": $genUUID()
|
|
||||||
}
|
|
||||||
|
|
||||||
proc setupAccount*(account: GeneratedAccount, password: string): Account =
|
|
||||||
let multiAccounts = generateMultiAccounts(account, password)
|
|
||||||
|
|
||||||
let whisperPubKey = account.derived.whisper.publicKey
|
|
||||||
let alias = generateAlias(whisperPubKey)
|
|
||||||
let identicon =generateIdenticon(whisperPubKey)
|
|
||||||
|
|
||||||
let accountData = getAccountData(account, alias, identicon)
|
|
||||||
var settingsJSON = getAccountSettings(account, alias, identicon, multiAccounts, constants.DEFAULT_NETWORKS)
|
|
||||||
|
|
||||||
result = saveAccountAndLogin(multiAccounts, alias, identicon, $accountData, password, $constants.NODE_CONFIG, $settingsJSON)
|
|
||||||
|
|
||||||
# TODO this is needed for now for the retrieving of past messages. We'll either move or remove it later
|
|
||||||
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
|
|
||||||
discard libstatus.addPeer(peer)
|
|
||||||
|
|
||||||
proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
|
|
||||||
let hashedPassword = "0x" & $keccak_256.digest(password)
|
|
||||||
let account = nodeAccount.toAccount
|
|
||||||
let loginResult = $libstatus.login($toJson(account), hashedPassword)
|
|
||||||
let error = parseJson(loginResult)["error"].getStr
|
|
||||||
|
|
||||||
if error == "":
|
|
||||||
debug "Login requested", user=nodeAccount.name
|
|
||||||
result = nodeAccount
|
|
||||||
return
|
|
||||||
|
|
||||||
raise newException(LoginError, "Error logging in: " & error)
|
|
|
@ -1,106 +1,103 @@
|
||||||
import core
|
import eventemitter, sets, json, strutils
|
||||||
import json
|
import sequtils
|
||||||
import utils
|
import libstatus/utils
|
||||||
import times
|
import libstatus/chat as status_chat
|
||||||
import strutils
|
|
||||||
import chronicles
|
import chronicles
|
||||||
import ../signals/types
|
import ../signals/types
|
||||||
import ../signals/messages
|
import chat/chat_item
|
||||||
|
import chat/chat_message
|
||||||
|
import tables
|
||||||
|
export chat_item
|
||||||
|
export chat_message
|
||||||
|
|
||||||
proc loadFilters*(chatId: string, filterId: string = "", symKeyId: string = "", oneToOne: bool = false, identity: string = "", topic: string = "", discovery: bool = false, negotiated: bool = false, listen: bool = true): string =
|
type MsgArgs* = ref object of Args
|
||||||
result = callPrivateRPC("loadFilters".prefix, %* [
|
message*: string
|
||||||
[{
|
chatId*: string
|
||||||
"ChatID": chatId, # identifier of the chat
|
payload*: JsonNode
|
||||||
"FilterID": filterId, # whisper filter id generated
|
|
||||||
"SymKeyID": symKeyId, # symmetric key id used for symmetric filters
|
|
||||||
"OneToOne": oneToOne, # if asymmetric encryption is used for this chat
|
|
||||||
"Identity": identity, # public key of the other recipient for non-public filters.
|
|
||||||
# FIXME: passing empty string to the topic makes it error
|
|
||||||
# "Topic": topic, # whisper topic
|
|
||||||
"Discovery": discovery,
|
|
||||||
"Negotiated": negotiated,
|
|
||||||
"Listen": listen # whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic
|
|
||||||
}]
|
|
||||||
])
|
|
||||||
|
|
||||||
proc removeFilters*(chatId: string, filterId: string) =
|
type ChannelArgs* = ref object of Args
|
||||||
discard callPrivateRPC("removeFilters".prefix, %* [
|
channel*: string
|
||||||
[{
|
chatTypeInt*: ChatType
|
||||||
"ChatID": chatId,
|
|
||||||
"FilterID": filterId
|
|
||||||
}]
|
|
||||||
])
|
|
||||||
|
|
||||||
proc saveChat*(chatId: string, oneToOne: bool = false, active: bool = true) =
|
type ChatArgs* = ref object of Args
|
||||||
discard callPrivateRPC("saveChat".prefix, %* [
|
chats*: seq[Chat]
|
||||||
{
|
|
||||||
"lastClockValue": 0, # TODO:
|
|
||||||
"color": "#51d0f0", # TODO:
|
|
||||||
"name": chatId,
|
|
||||||
"lastMessage": nil, # TODO:
|
|
||||||
"active": active,
|
|
||||||
"id": chatId,
|
|
||||||
"unviewedMessagesCount": 0, # TODO:
|
|
||||||
# TODO use constants for those too or use the Date
|
|
||||||
"chatType": if oneToOne: 1 else: 2, # TODO: use constants
|
|
||||||
"timestamp": 1588940692659 # TODO:
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
proc deactivateChat*(chatId: string) =
|
type
|
||||||
discard callPrivateRPC("saveChat".prefix, %* [
|
ChatModel* = ref object
|
||||||
{
|
events*: EventEmitter
|
||||||
"lastClockValue": 0,
|
channels*: HashSet[string]
|
||||||
"color": "",
|
filters*: Table[string, string]
|
||||||
"name": chatId,
|
|
||||||
"lastMessage": nil,
|
|
||||||
"active": false,
|
|
||||||
"id": chatId,
|
|
||||||
"unviewedMessagesCount": 0,
|
|
||||||
"timestamp": 0
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
proc loadChats*(): seq[Chat] =
|
proc newChatModel*(): ChatModel =
|
||||||
result = @[]
|
result = ChatModel()
|
||||||
let jsonResponse = parseJson($callPrivateRPC("chats".prefix))
|
result.events = createEventEmitter()
|
||||||
if jsonResponse["result"].kind != JNull:
|
result.channels = initHashSet[string]()
|
||||||
for jsonChat in jsonResponse{"result"}:
|
result.filters = initTable[string, string]()
|
||||||
let chat = jsonChat.toChat
|
|
||||||
if chat.active: result.add(jsonChat.toChat)
|
|
||||||
|
|
||||||
proc chatMessages*(chatId: string) =
|
proc delete*(self: ChatModel) =
|
||||||
discard callPrivateRPC("chatMessages".prefix, %* [chatId, nil, 20])
|
discard
|
||||||
|
|
||||||
# TODO this probably belongs in another file
|
proc hasChannel*(self: ChatModel, chatId: string): bool =
|
||||||
proc generateSymKeyFromPassword*(): string =
|
result = self.channels.contains(chatId)
|
||||||
result = ($parseJson(callPrivateRPC("waku_generateSymKeyFromPassword", %* [
|
|
||||||
# TODO unhardcode this for non-status mailservers
|
|
||||||
"status-offline-inbox"
|
|
||||||
]))["result"]).strip(chars = {'"'})
|
|
||||||
|
|
||||||
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int) =
|
proc getActiveChannel*(self: ChatModel): string =
|
||||||
discard callPrivateRPC("requestMessages".prefix, %* [
|
if (self.channels.len == 0): "" else: self.channels.toSeq[self.channels.len - 1]
|
||||||
{
|
|
||||||
"topics": topics,
|
|
||||||
"mailServerPeer": peer,
|
|
||||||
"symKeyID": symKeyID,
|
|
||||||
"timeout": 30,
|
|
||||||
"limit": numberOfMessages,
|
|
||||||
"cursor": nil,
|
|
||||||
"from": times.toUnix(times.getTime()) - 30000 # Unhardcode this. Need to keep the last fetch in a DB
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
|
proc join*(self: ChatModel, chatId: string, chatTypeInt: ChatType, isNewChat: bool = true) =
|
||||||
|
if self.hasChannel(chatId): return
|
||||||
|
|
||||||
proc sendChatMessage*(chatId: string, msg: string): string =
|
self.channels.incl chatId
|
||||||
callPrivateRPC("sendChatMessage".prefix, %* [
|
|
||||||
{
|
let generatedSymKey = status_chat.generateSymKeyFromPassword()
|
||||||
"chatId": chatId,
|
|
||||||
"text": msg,
|
# TODO get this from the connection or something
|
||||||
"responseTo": nil,
|
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
|
||||||
"ensName": nil,
|
|
||||||
"sticker": nil,
|
let oneToOne = isOneToOneChat(chatId)
|
||||||
"contentType": 1
|
|
||||||
}
|
if isNewChat: status_chat.saveChat(chatId, oneToOne)
|
||||||
])
|
|
||||||
|
let filterResult = status_chat.loadFilters(chatId = chatId, oneToOne = oneToOne)
|
||||||
|
|
||||||
|
status_chat.chatMessages(chatId)
|
||||||
|
|
||||||
|
let parsedResult = parseJson(filterResult)["result"]
|
||||||
|
|
||||||
|
var topics = newSeq[string](0)
|
||||||
|
for topicObj in parsedResult:
|
||||||
|
if (($topicObj["chatId"]).strip(chars = {'"'}) == chatId):
|
||||||
|
topics.add(($topicObj["topic"]).strip(chars = {'"'}))
|
||||||
|
|
||||||
|
if(not self.filters.hasKey(chatId)): self.filters[chatId] = topicObj["filterId"].getStr
|
||||||
|
|
||||||
|
if (topics.len == 0):
|
||||||
|
warn "No topic found for the chat. Cannot load past messages"
|
||||||
|
else:
|
||||||
|
status_chat.requestMessages(topics, generatedSymKey, peer, 20)
|
||||||
|
|
||||||
|
self.events.emit("channelJoined", ChannelArgs(channel: chatId, chatTypeInt: chatTypeInt))
|
||||||
|
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
|
||||||
|
|
||||||
|
proc load*(self: ChatModel) =
|
||||||
|
let chatList = status_chat.loadChats()
|
||||||
|
for chat in chatList:
|
||||||
|
# TODO: use correct type of chat instead of hardcoded 2 (assumes it's only public chats)
|
||||||
|
self.join(chat.id, ChatType.Public, false)
|
||||||
|
self.events.emit("chatsLoaded", ChatArgs(chats: chatList))
|
||||||
|
|
||||||
|
proc leave*(self: ChatModel, chatId: string) =
|
||||||
|
status_chat.removeFilters(chatId, self.filters[chatId])
|
||||||
|
status_chat.deactivateChat(chatId)
|
||||||
|
# TODO: REMOVE MAILSERVER TOPIC
|
||||||
|
# TODO: REMOVE HISTORY
|
||||||
|
|
||||||
|
self.filters.del(chatId)
|
||||||
|
self.channels.excl(chatId)
|
||||||
|
self.events.emit("channelLeft", ChannelArgs(channel: chatId))
|
||||||
|
self.events.emit("activeChannelChanged", ChannelArgs(channel: self.getActiveChannel()))
|
||||||
|
|
||||||
|
proc sendMessage*(self: ChatModel, chatId: string, msg: string): string =
|
||||||
|
var sentMessage = status_chat.sendChatMessage(chatId, msg)
|
||||||
|
var parsedMessage = parseJson(sentMessage)["result"]["chats"][0]["lastMessage"]
|
||||||
|
self.events.emit("messageSent", MsgArgs(message: msg, chatId: chatId, payload: parsedMessage))
|
||||||
|
sentMessage
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import ../../signals/types
|
import ../../signals/types
|
||||||
import ../../status/accounts as status_accounts
|
import ../libstatus/accounts as status_accounts
|
||||||
|
|
||||||
type ChatItem* = ref object
|
type ChatItem* = ref object
|
||||||
id*: string
|
id*: string
|
|
@ -0,0 +1,161 @@
|
||||||
|
import libstatus
|
||||||
|
import core
|
||||||
|
import json
|
||||||
|
import utils
|
||||||
|
import accounts/constants
|
||||||
|
import nimcrypto
|
||||||
|
import os
|
||||||
|
import uuids
|
||||||
|
import types
|
||||||
|
import json_serialization
|
||||||
|
import chronicles
|
||||||
|
|
||||||
|
proc queryAccounts*(): string =
|
||||||
|
var response = callPrivateRPC("eth_accounts")
|
||||||
|
result = parseJson(response)["result"][0].getStr()
|
||||||
|
|
||||||
|
proc generateAddresses*(): seq[GeneratedAccount] =
|
||||||
|
let multiAccountConfig = %* {
|
||||||
|
"n": 5,
|
||||||
|
"mnemonicPhraseLength": 12,
|
||||||
|
"bip39Passphrase": "",
|
||||||
|
"paths": [PATH_WHISPER, PATH_WALLET_ROOT, PATH_DEFAULT_WALLET]
|
||||||
|
}
|
||||||
|
result = Json.decode($libstatus.multiAccountGenerateAndDeriveAddresses($multiAccountConfig), seq[GeneratedAccount])
|
||||||
|
|
||||||
|
proc generateAlias*(publicKey: string): string =
|
||||||
|
result = $libstatus.generateAlias(publicKey.toGoString)
|
||||||
|
|
||||||
|
proc generateIdenticon*(publicKey: string): string =
|
||||||
|
result = $libstatus.identicon(publicKey.toGoString)
|
||||||
|
|
||||||
|
proc ensureDir(dirname: string) =
|
||||||
|
if not existsDir(dirname):
|
||||||
|
# removeDir(dirname)
|
||||||
|
createDir(dirname)
|
||||||
|
|
||||||
|
proc initNodeAccounts*(): seq[NodeAccount] =
|
||||||
|
const datadir = "./data/"
|
||||||
|
const keystoredir = "./data/keystore/"
|
||||||
|
const nobackupdir = "./noBackup/"
|
||||||
|
|
||||||
|
ensureDir(datadir)
|
||||||
|
ensureDir(keystoredir)
|
||||||
|
ensureDir(nobackupdir)
|
||||||
|
|
||||||
|
discard $libstatus.initKeystore(keystoredir);
|
||||||
|
let strNodeAccounts = $libstatus.openAccounts(datadir);
|
||||||
|
result = Json.decode(strNodeAccounts, seq[NodeAccount])
|
||||||
|
|
||||||
|
proc saveAccountAndLogin*(
|
||||||
|
multiAccounts: MultiAccounts,
|
||||||
|
alias: string,
|
||||||
|
identicon: string,
|
||||||
|
accountData: string,
|
||||||
|
password: string,
|
||||||
|
configJSON: string,
|
||||||
|
settingsJSON: string): Account =
|
||||||
|
let hashedPassword = "0x" & $keccak_256.digest(password)
|
||||||
|
let subaccountData = %* [
|
||||||
|
{
|
||||||
|
"public-key": multiAccounts.defaultWallet.publicKey,
|
||||||
|
"address": multiAccounts.defaultWallet.address,
|
||||||
|
"color": "#4360df",
|
||||||
|
"wallet": true,
|
||||||
|
"path": constants.PATH_DEFAULT_WALLET,
|
||||||
|
"name": "Status account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"public-key": multiAccounts.whisper.publicKey,
|
||||||
|
"address": multiAccounts.whisper.address,
|
||||||
|
"name": alias,
|
||||||
|
"photo-path": identicon,
|
||||||
|
"path": constants.PATH_WHISPER,
|
||||||
|
"chat": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
var savedResult = $libstatus.saveAccountAndLogin(accountData, hashedPassword, settingsJSON, configJSON, $subaccountData)
|
||||||
|
let parsedSavedResult = savedResult.parseJson
|
||||||
|
let error = parsedSavedResult["error"].getStr
|
||||||
|
|
||||||
|
if error == "":
|
||||||
|
debug "Account saved succesfully"
|
||||||
|
result = Account(name: alias, photoPath: identicon)
|
||||||
|
return
|
||||||
|
|
||||||
|
raise newException(LoginError, "Error saving account and logging in: " & error)
|
||||||
|
|
||||||
|
proc generateMultiAccounts*(account: GeneratedAccount, password: string): MultiAccounts =
|
||||||
|
let hashedPassword = "0x" & $keccak_256.digest(password)
|
||||||
|
let multiAccount = %* {
|
||||||
|
"accountID": account.id,
|
||||||
|
"paths": [PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALLET],
|
||||||
|
"password": hashedPassword
|
||||||
|
}
|
||||||
|
var response = $libstatus.multiAccountStoreDerivedAccounts($multiAccount);
|
||||||
|
result = Json.decode($response, MultiAccounts)
|
||||||
|
|
||||||
|
proc getAccountData*(account: GeneratedAccount, alias: string, identicon: string): JsonNode =
|
||||||
|
result = %* {
|
||||||
|
"name": alias,
|
||||||
|
"address": account.address,
|
||||||
|
"photo-path": identicon,
|
||||||
|
"key-uid": account.keyUid,
|
||||||
|
"keycard-pairing": nil
|
||||||
|
}
|
||||||
|
|
||||||
|
proc getAccountSettings*(account: GeneratedAccount, alias: string, identicon: string, multiAccounts: MultiAccounts, defaultNetworks: JsonNode): JsonNode =
|
||||||
|
result = %* {
|
||||||
|
"key-uid": account.keyUid,
|
||||||
|
"mnemonic": account.mnemonic,
|
||||||
|
"public-key": multiAccounts.whisper.publicKey,
|
||||||
|
"name": alias,
|
||||||
|
"address": account.address,
|
||||||
|
"eip1581-address": multiAccounts.eip1581.address,
|
||||||
|
"dapps-address": multiAccounts.defaultWallet.address,
|
||||||
|
"wallet-root-address": multiAccounts.walletRoot.address,
|
||||||
|
"preview-privacy?": true,
|
||||||
|
"signing-phrase": generateSigningPhrase(3),
|
||||||
|
"log-level": "INFO",
|
||||||
|
"latest-derived-path": 0,
|
||||||
|
"networks/networks": defaultNetworks,
|
||||||
|
"currency": "usd",
|
||||||
|
"photo-path": identicon,
|
||||||
|
"waku-enabled": true,
|
||||||
|
"wallet/visible-tokens": {
|
||||||
|
"mainnet": ["SNT"]
|
||||||
|
},
|
||||||
|
"appearance": 0,
|
||||||
|
"networks/current-network": "mainnet_rpc",
|
||||||
|
"installation-id": $genUUID()
|
||||||
|
}
|
||||||
|
|
||||||
|
proc setupAccount*(account: GeneratedAccount, password: string): Account =
|
||||||
|
let multiAccounts = generateMultiAccounts(account, password)
|
||||||
|
|
||||||
|
let whisperPubKey = account.derived.whisper.publicKey
|
||||||
|
let alias = generateAlias(whisperPubKey)
|
||||||
|
let identicon =generateIdenticon(whisperPubKey)
|
||||||
|
|
||||||
|
let accountData = getAccountData(account, alias, identicon)
|
||||||
|
var settingsJSON = getAccountSettings(account, alias, identicon, multiAccounts, constants.DEFAULT_NETWORKS)
|
||||||
|
|
||||||
|
result = saveAccountAndLogin(multiAccounts, alias, identicon, $accountData, password, $constants.NODE_CONFIG, $settingsJSON)
|
||||||
|
|
||||||
|
# TODO this is needed for now for the retrieving of past messages. We'll either move or remove it later
|
||||||
|
let peer = "enode://44160e22e8b42bd32a06c1532165fa9e096eebedd7fa6d6e5f8bbef0440bc4a4591fe3651be68193a7ec029021cdb496cfe1d7f9f1dc69eb99226e6f39a7a5d4@35.225.221.245:443"
|
||||||
|
discard libstatus.addPeer(peer)
|
||||||
|
|
||||||
|
proc login*(nodeAccount: NodeAccount, password: string): NodeAccount =
|
||||||
|
let hashedPassword = "0x" & $keccak_256.digest(password)
|
||||||
|
let account = nodeAccount.toAccount
|
||||||
|
let loginResult = $libstatus.login($toJson(account), hashedPassword)
|
||||||
|
let error = parseJson(loginResult)["error"].getStr
|
||||||
|
|
||||||
|
if error == "":
|
||||||
|
debug "Login requested", user=nodeAccount.name
|
||||||
|
result = nodeAccount
|
||||||
|
return
|
||||||
|
|
||||||
|
raise newException(LoginError, "Error logging in: " & error)
|
|
@ -0,0 +1,106 @@
|
||||||
|
import core
|
||||||
|
import json
|
||||||
|
import utils
|
||||||
|
import times
|
||||||
|
import strutils
|
||||||
|
import chronicles
|
||||||
|
import ../../signals/types
|
||||||
|
import ../../signals/messages
|
||||||
|
|
||||||
|
proc loadFilters*(chatId: string, filterId: string = "", symKeyId: string = "", oneToOne: bool = false, identity: string = "", topic: string = "", discovery: bool = false, negotiated: bool = false, listen: bool = true): string =
|
||||||
|
result = callPrivateRPC("loadFilters".prefix, %* [
|
||||||
|
[{
|
||||||
|
"ChatID": chatId, # identifier of the chat
|
||||||
|
"FilterID": filterId, # whisper filter id generated
|
||||||
|
"SymKeyID": symKeyId, # symmetric key id used for symmetric filters
|
||||||
|
"OneToOne": oneToOne, # if asymmetric encryption is used for this chat
|
||||||
|
"Identity": identity, # public key of the other recipient for non-public filters.
|
||||||
|
# FIXME: passing empty string to the topic makes it error
|
||||||
|
# "Topic": topic, # whisper topic
|
||||||
|
"Discovery": discovery,
|
||||||
|
"Negotiated": negotiated,
|
||||||
|
"Listen": listen # whether we are actually listening for messages on this chat, or the filter is only created in order to be able to post on the topic
|
||||||
|
}]
|
||||||
|
])
|
||||||
|
|
||||||
|
proc removeFilters*(chatId: string, filterId: string) =
|
||||||
|
discard callPrivateRPC("removeFilters".prefix, %* [
|
||||||
|
[{
|
||||||
|
"ChatID": chatId,
|
||||||
|
"FilterID": filterId
|
||||||
|
}]
|
||||||
|
])
|
||||||
|
|
||||||
|
proc saveChat*(chatId: string, oneToOne: bool = false, active: bool = true) =
|
||||||
|
discard callPrivateRPC("saveChat".prefix, %* [
|
||||||
|
{
|
||||||
|
"lastClockValue": 0, # TODO:
|
||||||
|
"color": "#51d0f0", # TODO:
|
||||||
|
"name": chatId,
|
||||||
|
"lastMessage": nil, # TODO:
|
||||||
|
"active": active,
|
||||||
|
"id": chatId,
|
||||||
|
"unviewedMessagesCount": 0, # TODO:
|
||||||
|
# TODO use constants for those too or use the Date
|
||||||
|
"chatType": if oneToOne: 1 else: 2, # TODO: use constants
|
||||||
|
"timestamp": 1588940692659 # TODO:
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
proc deactivateChat*(chatId: string) =
|
||||||
|
discard callPrivateRPC("saveChat".prefix, %* [
|
||||||
|
{
|
||||||
|
"lastClockValue": 0,
|
||||||
|
"color": "",
|
||||||
|
"name": chatId,
|
||||||
|
"lastMessage": nil,
|
||||||
|
"active": false,
|
||||||
|
"id": chatId,
|
||||||
|
"unviewedMessagesCount": 0,
|
||||||
|
"timestamp": 0
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
proc loadChats*(): seq[Chat] =
|
||||||
|
result = @[]
|
||||||
|
let jsonResponse = parseJson($callPrivateRPC("chats".prefix))
|
||||||
|
if jsonResponse["result"].kind != JNull:
|
||||||
|
for jsonChat in jsonResponse{"result"}:
|
||||||
|
let chat = jsonChat.toChat
|
||||||
|
if chat.active: result.add(jsonChat.toChat)
|
||||||
|
|
||||||
|
proc chatMessages*(chatId: string) =
|
||||||
|
discard callPrivateRPC("chatMessages".prefix, %* [chatId, nil, 20])
|
||||||
|
|
||||||
|
# TODO this probably belongs in another file
|
||||||
|
proc generateSymKeyFromPassword*(): string =
|
||||||
|
result = ($parseJson(callPrivateRPC("waku_generateSymKeyFromPassword", %* [
|
||||||
|
# TODO unhardcode this for non-status mailservers
|
||||||
|
"status-offline-inbox"
|
||||||
|
]))["result"]).strip(chars = {'"'})
|
||||||
|
|
||||||
|
proc requestMessages*(topics: seq[string], symKeyID: string, peer: string, numberOfMessages: int) =
|
||||||
|
discard callPrivateRPC("requestMessages".prefix, %* [
|
||||||
|
{
|
||||||
|
"topics": topics,
|
||||||
|
"mailServerPeer": peer,
|
||||||
|
"symKeyID": symKeyID,
|
||||||
|
"timeout": 30,
|
||||||
|
"limit": numberOfMessages,
|
||||||
|
"cursor": nil,
|
||||||
|
"from": times.toUnix(times.getTime()) - 30000 # Unhardcode this. Need to keep the last fetch in a DB
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
proc sendChatMessage*(chatId: string, msg: string): string =
|
||||||
|
callPrivateRPC("sendChatMessage".prefix, %* [
|
||||||
|
{
|
||||||
|
"chatId": chatId,
|
||||||
|
"text": msg,
|
||||||
|
"responseTo": nil,
|
||||||
|
"ensName": nil,
|
||||||
|
"sticker": nil,
|
||||||
|
"contentType": 1
|
||||||
|
}
|
||||||
|
])
|
|
@ -0,0 +1,43 @@
|
||||||
|
import core as status
|
||||||
|
import json
|
||||||
|
# import utils
|
||||||
|
import httpclient, json
|
||||||
|
import strformat
|
||||||
|
import stint
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
proc getAccounts*(): seq[string] =
|
||||||
|
var response = callPrivateRPC("eth_accounts")
|
||||||
|
result = parseJson(response)["result"].to(seq[string])
|
||||||
|
|
||||||
|
proc getAccount*(): string =
|
||||||
|
var accounts = getAccounts()
|
||||||
|
result = accounts[0]
|
||||||
|
|
||||||
|
proc sendTransaction*(from_address: string, to: string, value: string, password: string): string =
|
||||||
|
var args = %* {
|
||||||
|
"value": fmt"0x{toHex(value)}",
|
||||||
|
"from": from_address,
|
||||||
|
"to": to
|
||||||
|
}
|
||||||
|
var response = status.sendTransaction($args, password)
|
||||||
|
result = response
|
||||||
|
|
||||||
|
proc getPrice*(crypto: string, fiat: string): string =
|
||||||
|
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
|
||||||
|
let client = newHttpClient()
|
||||||
|
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
||||||
|
|
||||||
|
let response = client.request(url)
|
||||||
|
$parseJson(response.body)["USD"]
|
||||||
|
|
||||||
|
proc getBalance*(address: string): string =
|
||||||
|
let payload = %* [address, "latest"]
|
||||||
|
parseJson(status.callPrivateRPC("eth_getBalance", payload))["result"].str
|
||||||
|
|
||||||
|
proc hex2Eth*(input: string): string =
|
||||||
|
var value = fromHex(Stuint[256], input)
|
||||||
|
var one_eth = fromHex(Stuint[256], "DE0B6B3A7640000")
|
||||||
|
|
||||||
|
var (eth, remainder) = divmod(value, one_eth)
|
||||||
|
fmt"{eth}.{remainder}"
|
|
@ -2,7 +2,7 @@ import eventemitter
|
||||||
# import json
|
# import json
|
||||||
# import strformat
|
# import strformat
|
||||||
# import strutils
|
# import strutils
|
||||||
import "../status/core" as status
|
import libstatus/core as status
|
||||||
|
|
||||||
type NodeModel* = ref object
|
type NodeModel* = ref object
|
||||||
events*: EventEmitter
|
events*: EventEmitter
|
|
@ -1,5 +1,5 @@
|
||||||
import eventemitter
|
import eventemitter
|
||||||
import ../status/types
|
import libstatus/types
|
||||||
|
|
||||||
type
|
type
|
||||||
MailServer* = ref object
|
MailServer* = ref object
|
|
@ -1,43 +1,40 @@
|
||||||
import core as status
|
import eventemitter
|
||||||
import json
|
import json
|
||||||
# import utils
|
|
||||||
import httpclient, json
|
|
||||||
import strformat
|
import strformat
|
||||||
import stint
|
|
||||||
import strutils
|
import strutils
|
||||||
|
import libstatus/wallet as status_wallet
|
||||||
|
|
||||||
proc getAccounts*(): seq[string] =
|
type Asset* = ref object
|
||||||
var response = callPrivateRPC("eth_accounts")
|
name*, symbol*, value*, fiatValue*, image*: string
|
||||||
result = parseJson(response)["result"].to(seq[string])
|
|
||||||
|
|
||||||
proc getAccount*(): string =
|
type WalletModel* = ref object
|
||||||
var accounts = getAccounts()
|
events*: EventEmitter
|
||||||
result = accounts[0]
|
|
||||||
|
|
||||||
proc sendTransaction*(from_address: string, to: string, value: string, password: string): string =
|
proc newWalletModel*(): WalletModel =
|
||||||
var args = %* {
|
result = WalletModel()
|
||||||
"value": fmt"0x{toHex(value)}",
|
result.events = createEventEmitter()
|
||||||
"from": from_address,
|
|
||||||
"to": to
|
|
||||||
}
|
|
||||||
var response = status.sendTransaction($args, password)
|
|
||||||
result = response
|
|
||||||
|
|
||||||
proc getPrice*(crypto: string, fiat: string): string =
|
proc delete*(self: WalletModel) =
|
||||||
var url: string = fmt"https://min-api.cryptocompare.com/data/price?fsym={crypto}&tsyms={fiat}"
|
discard
|
||||||
let client = newHttpClient()
|
|
||||||
client.headers = newHttpHeaders({ "Content-Type": "application/json" })
|
|
||||||
|
|
||||||
let response = client.request(url)
|
proc sendTransaction*(self: WalletModel, from_value: string, to: string, value: string, password: string): string =
|
||||||
$parseJson(response.body)["USD"]
|
status_wallet.sendTransaction(from_value, to, value, password)
|
||||||
|
|
||||||
proc getBalance*(address: string): string =
|
proc getEthBalance*(self: WalletModel, address: string): string =
|
||||||
let payload = %* [address, "latest"]
|
var balance = status_wallet.getBalance(address)
|
||||||
parseJson(status.callPrivateRPC("eth_getBalance", payload))["result"].str
|
echo(fmt"balance in hex: {balance}")
|
||||||
|
|
||||||
proc hex2Eth*(input: string): string =
|
# 2. convert balance to eth
|
||||||
var value = fromHex(Stuint[256], input)
|
var eth_value = status_wallet.hex2Eth(balance)
|
||||||
var one_eth = fromHex(Stuint[256], "DE0B6B3A7640000")
|
echo(fmt"balance in eth: {eth_value}")
|
||||||
|
eth_value
|
||||||
|
|
||||||
var (eth, remainder) = divmod(value, one_eth)
|
proc getFiatValue*(self: WalletModel, eth_balance: string, symbol: string, fiat_symbol: string): float =
|
||||||
fmt"{eth}.{remainder}"
|
# 3. get usd price of 1 eth
|
||||||
|
var usd_eth_price = status_wallet.getPrice("ETH", "USD")
|
||||||
|
echo(fmt"usd_price: {usd_eth_price}")
|
||||||
|
|
||||||
|
# 4. convert balance to usd
|
||||||
|
var usd_balance = parseFloat(eth_balance) * parseFloat(usd_eth_price)
|
||||||
|
echo(fmt"balance in usd: {usd_balance}")
|
||||||
|
usd_balance
|
||||||
|
|
Loading…
Reference in New Issue