blindly copying deltachat-node to core repository

This commit is contained in:
missytake
2022-05-02 18:56:37 +02:00
committed by Simon Laux
parent 961612370d
commit a786a1427d
48 changed files with 10343 additions and 0 deletions

9
node/lib/binding.ts Normal file
View File

@@ -0,0 +1,9 @@
import { join } from 'path'
/**
* bindings are not typed yet.
* if the availible function names are required they can be found inside of `../src/module.c`
*/
export const bindings: any = require('node-gyp-build')(join(__dirname, '../'))
export default bindings

103
node/lib/chat.ts Normal file
View File

@@ -0,0 +1,103 @@
/* eslint-disable camelcase */
import binding from './binding'
import rawDebug from 'debug'
const debug = rawDebug('deltachat:node:chat')
import { C } from './constants'
import { integerToHexColor } from './util'
import { ChatJSON } from './types'
interface NativeChat {}
/**
* Wrapper around dc_chat_t*
*/
export class Chat {
constructor(public dc_chat: NativeChat) {
debug('Chat constructor')
}
getVisibility():
| C.DC_CHAT_VISIBILITY_NORMAL
| C.DC_CHAT_VISIBILITY_ARCHIVED
| C.DC_CHAT_VISIBILITY_PINNED {
return binding.dcn_chat_get_visibility(this.dc_chat)
}
get color(): string {
return integerToHexColor(binding.dcn_chat_get_color(this.dc_chat))
}
getId(): number {
return binding.dcn_chat_get_id(this.dc_chat)
}
getName(): string {
return binding.dcn_chat_get_name(this.dc_chat)
}
getProfileImage(): string {
return binding.dcn_chat_get_profile_image(this.dc_chat)
}
getType(): number {
return binding.dcn_chat_get_type(this.dc_chat)
}
isSelfTalk(): boolean {
return Boolean(binding.dcn_chat_is_self_talk(this.dc_chat))
}
isContactRequest(): boolean {
return Boolean(binding.dcn_chat_is_contact_request(this.dc_chat))
}
isUnpromoted(): boolean {
return Boolean(binding.dcn_chat_is_unpromoted(this.dc_chat))
}
isProtected(): boolean {
return Boolean(binding.dcn_chat_is_protected(this.dc_chat))
}
get canSend(): boolean {
return Boolean(binding.dcn_chat_can_send(this.dc_chat))
}
isDeviceTalk(): boolean {
return Boolean(binding.dcn_chat_is_device_talk(this.dc_chat))
}
isSingle(): boolean {
return this.getType() === C.DC_CHAT_TYPE_SINGLE
}
isGroup(): boolean {
return this.getType() === C.DC_CHAT_TYPE_GROUP
}
isMuted(): boolean {
return Boolean(binding.dcn_chat_is_muted(this.dc_chat))
}
toJson(): ChatJSON {
debug('toJson')
const visibility = this.getVisibility()
return {
archived: visibility === C.DC_CHAT_VISIBILITY_ARCHIVED,
pinned: visibility === C.DC_CHAT_VISIBILITY_PINNED,
color: this.color,
id: this.getId(),
name: this.getName(),
profileImage: this.getProfileImage(),
type: this.getType(),
isSelfTalk: this.isSelfTalk(),
isUnpromoted: this.isUnpromoted(),
isProtected: this.isProtected(),
canSend: this.canSend,
isDeviceTalk: this.isDeviceTalk(),
isContactRequest: this.isContactRequest(),
muted: this.isMuted(),
}
}
}

39
node/lib/chatlist.ts Normal file
View File

@@ -0,0 +1,39 @@
/* eslint-disable camelcase */
import binding from './binding'
import { Lot } from './lot'
import { Chat } from './chat'
const debug = require('debug')('deltachat:node:chatlist')
interface NativeChatList {}
/**
* Wrapper around dc_chatlist_t*
*/
export class ChatList {
constructor(private dc_chatlist: NativeChatList) {
debug('ChatList constructor')
}
getChatId(index: number): number {
debug(`getChatId ${index}`)
return binding.dcn_chatlist_get_chat_id(this.dc_chatlist, index)
}
getCount(): number {
debug('getCount')
return binding.dcn_chatlist_get_cnt(this.dc_chatlist)
}
getMessageId(index: number): number {
debug(`getMessageId ${index}`)
return binding.dcn_chatlist_get_msg_id(this.dc_chatlist, index)
}
getSummary(index: number, chat?: Chat): Lot {
debug(`getSummary ${index}`)
const dc_chat = (chat && chat.dc_chat) || null
return new Lot(
binding.dcn_chatlist_get_summary(this.dc_chatlist, index, dc_chat)
)
}
}

260
node/lib/constants.ts Normal file
View File

@@ -0,0 +1,260 @@
// Generated!
export enum C {
DC_CERTCK_ACCEPT_INVALID_CERTIFICATES = 3,
DC_CERTCK_AUTO = 0,
DC_CERTCK_STRICT = 1,
DC_CHAT_ID_ALLDONE_HINT = 7,
DC_CHAT_ID_ARCHIVED_LINK = 6,
DC_CHAT_ID_LAST_SPECIAL = 9,
DC_CHAT_ID_TRASH = 3,
DC_CHAT_TYPE_BROADCAST = 160,
DC_CHAT_TYPE_GROUP = 120,
DC_CHAT_TYPE_MAILINGLIST = 140,
DC_CHAT_TYPE_SINGLE = 100,
DC_CHAT_TYPE_UNDEFINED = 0,
DC_CHAT_VISIBILITY_ARCHIVED = 1,
DC_CHAT_VISIBILITY_NORMAL = 0,
DC_CHAT_VISIBILITY_PINNED = 2,
DC_CONNECTIVITY_CONNECTED = 4000,
DC_CONNECTIVITY_CONNECTING = 2000,
DC_CONNECTIVITY_NOT_CONNECTED = 1000,
DC_CONNECTIVITY_WORKING = 3000,
DC_CONTACT_ID_DEVICE = 5,
DC_CONTACT_ID_INFO = 2,
DC_CONTACT_ID_LAST_SPECIAL = 9,
DC_CONTACT_ID_SELF = 1,
DC_DOWNLOAD_AVAILABLE = 10,
DC_DOWNLOAD_DONE = 0,
DC_DOWNLOAD_FAILURE = 20,
DC_DOWNLOAD_IN_PROGRESS = 1000,
DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED = 2021,
DC_EVENT_CHAT_MODIFIED = 2020,
DC_EVENT_CONFIGURE_PROGRESS = 2041,
DC_EVENT_CONNECTIVITY_CHANGED = 2100,
DC_EVENT_CONTACTS_CHANGED = 2030,
DC_EVENT_DELETED_BLOB_FILE = 151,
DC_EVENT_ERROR = 400,
DC_EVENT_ERROR_SELF_NOT_IN_GROUP = 410,
DC_EVENT_IMAP_CONNECTED = 102,
DC_EVENT_IMAP_MESSAGE_DELETED = 104,
DC_EVENT_IMAP_MESSAGE_MOVED = 105,
DC_EVENT_IMEX_FILE_WRITTEN = 2052,
DC_EVENT_IMEX_PROGRESS = 2051,
DC_EVENT_INCOMING_MSG = 2005,
DC_EVENT_INFO = 100,
DC_EVENT_LOCATION_CHANGED = 2035,
DC_EVENT_MSGS_CHANGED = 2000,
DC_EVENT_MSGS_NOTICED = 2008,
DC_EVENT_MSG_DELIVERED = 2010,
DC_EVENT_MSG_FAILED = 2012,
DC_EVENT_MSG_READ = 2015,
DC_EVENT_NEW_BLOB_FILE = 150,
DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060,
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061,
DC_EVENT_SELFAVATAR_CHANGED = 2110,
DC_EVENT_SMTP_CONNECTED = 101,
DC_EVENT_SMTP_MESSAGE_SENT = 103,
DC_EVENT_WARNING = 300,
DC_EVENT_WEBXDC_STATUS_UPDATE = 2120,
DC_GCL_ADD_ALLDONE_HINT = 4,
DC_GCL_ADD_SELF = 2,
DC_GCL_ARCHIVED_ONLY = 1,
DC_GCL_FOR_FORWARDING = 8,
DC_GCL_NO_SPECIALS = 2,
DC_GCL_VERIFIED_ONLY = 1,
DC_GCM_ADDDAYMARKER = 1,
DC_GCM_INFO_ONLY = 2,
DC_IMEX_EXPORT_BACKUP = 11,
DC_IMEX_EXPORT_SELF_KEYS = 1,
DC_IMEX_IMPORT_BACKUP = 12,
DC_IMEX_IMPORT_SELF_KEYS = 2,
DC_INFO_PROTECTION_DISABLED = 12,
DC_INFO_PROTECTION_ENABLED = 11,
DC_KEY_GEN_DEFAULT = 0,
DC_KEY_GEN_ED25519 = 2,
DC_KEY_GEN_RSA2048 = 1,
DC_LP_AUTH_NORMAL = 4,
DC_LP_AUTH_OAUTH2 = 2,
DC_MEDIA_QUALITY_BALANCED = 0,
DC_MEDIA_QUALITY_WORSE = 1,
DC_MSG_AUDIO = 40,
DC_MSG_FILE = 60,
DC_MSG_GIF = 21,
DC_MSG_ID_DAYMARKER = 9,
DC_MSG_ID_LAST_SPECIAL = 9,
DC_MSG_ID_MARKER1 = 1,
DC_MSG_IMAGE = 20,
DC_MSG_STICKER = 23,
DC_MSG_TEXT = 10,
DC_MSG_VIDEO = 50,
DC_MSG_VIDEOCHAT_INVITATION = 70,
DC_MSG_VOICE = 41,
DC_MSG_WEBXDC = 80,
DC_PROVIDER_STATUS_BROKEN = 3,
DC_PROVIDER_STATUS_OK = 1,
DC_PROVIDER_STATUS_PREPARATION = 2,
DC_QR_ACCOUNT = 250,
DC_QR_ADDR = 320,
DC_QR_ASK_VERIFYCONTACT = 200,
DC_QR_ASK_VERIFYGROUP = 202,
DC_QR_ERROR = 400,
DC_QR_FPR_MISMATCH = 220,
DC_QR_FPR_OK = 210,
DC_QR_FPR_WITHOUT_ADDR = 230,
DC_QR_REVIVE_VERIFYCONTACT = 510,
DC_QR_REVIVE_VERIFYGROUP = 512,
DC_QR_TEXT = 330,
DC_QR_URL = 332,
DC_QR_WEBRTC_INSTANCE = 260,
DC_QR_WITHDRAW_VERIFYCONTACT = 500,
DC_QR_WITHDRAW_VERIFYGROUP = 502,
DC_SHOW_EMAILS_ACCEPTED_CONTACTS = 1,
DC_SHOW_EMAILS_ALL = 2,
DC_SHOW_EMAILS_OFF = 0,
DC_SOCKET_AUTO = 0,
DC_SOCKET_PLAIN = 3,
DC_SOCKET_SSL = 1,
DC_SOCKET_STARTTLS = 2,
DC_STATE_IN_FRESH = 10,
DC_STATE_IN_NOTICED = 13,
DC_STATE_IN_SEEN = 16,
DC_STATE_OUT_DELIVERED = 26,
DC_STATE_OUT_DRAFT = 19,
DC_STATE_OUT_FAILED = 24,
DC_STATE_OUT_MDN_RCVD = 28,
DC_STATE_OUT_PENDING = 20,
DC_STATE_OUT_PREPARING = 18,
DC_STATE_UNDEFINED = 0,
DC_STR_AC_SETUP_MSG_BODY = 43,
DC_STR_AC_SETUP_MSG_SUBJECT = 42,
DC_STR_ARCHIVEDCHATS = 40,
DC_STR_AUDIO = 11,
DC_STR_BAD_TIME_MSG_BODY = 85,
DC_STR_BROADCAST_LIST = 115,
DC_STR_CANNOT_LOGIN = 60,
DC_STR_CANTDECRYPT_MSG_BODY = 29,
DC_STR_CONFIGURATION_FAILED = 84,
DC_STR_CONNECTED = 107,
DC_STR_CONNTECTING = 108,
DC_STR_CONTACT_NOT_VERIFIED = 36,
DC_STR_CONTACT_SETUP_CHANGED = 37,
DC_STR_CONTACT_VERIFIED = 35,
DC_STR_DEVICE_MESSAGES = 68,
DC_STR_DEVICE_MESSAGES_HINT = 70,
DC_STR_DOWNLOAD_AVAILABILITY = 100,
DC_STR_DRAFT = 3,
DC_STR_E2E_AVAILABLE = 25,
DC_STR_E2E_PREFERRED = 34,
DC_STR_ENCRYPTEDMSG = 24,
DC_STR_ENCR_NONE = 28,
DC_STR_ENCR_TRANSP = 27,
DC_STR_EPHEMERAL_DAY = 79,
DC_STR_EPHEMERAL_DAYS = 95,
DC_STR_EPHEMERAL_DISABLED = 75,
DC_STR_EPHEMERAL_FOUR_WEEKS = 81,
DC_STR_EPHEMERAL_HOUR = 78,
DC_STR_EPHEMERAL_HOURS = 94,
DC_STR_EPHEMERAL_MINUTE = 77,
DC_STR_EPHEMERAL_MINUTES = 93,
DC_STR_EPHEMERAL_SECONDS = 76,
DC_STR_EPHEMERAL_WEEK = 80,
DC_STR_EPHEMERAL_WEEKS = 96,
DC_STR_ERROR = 112,
DC_STR_ERROR_NO_NETWORK = 87,
DC_STR_FAILED_SENDING_TO = 74,
DC_STR_FILE = 12,
DC_STR_FINGERPRINTS = 30,
DC_STR_FORWARDED = 97,
DC_STR_GIF = 23,
DC_STR_IMAGE = 9,
DC_STR_INCOMING_MESSAGES = 103,
DC_STR_LAST_MSG_SENT_SUCCESSFULLY = 111,
DC_STR_LOCATION = 66,
DC_STR_MESSAGES = 114,
DC_STR_MSGACTIONBYME = 63,
DC_STR_MSGACTIONBYUSER = 62,
DC_STR_MSGADDMEMBER = 17,
DC_STR_MSGDELMEMBER = 18,
DC_STR_MSGGROUPLEFT = 19,
DC_STR_MSGGRPIMGCHANGED = 16,
DC_STR_MSGGRPIMGDELETED = 33,
DC_STR_MSGGRPNAME = 15,
DC_STR_MSGLOCATIONDISABLED = 65,
DC_STR_MSGLOCATIONENABLED = 64,
DC_STR_NOMESSAGES = 1,
DC_STR_NOT_CONNECTED = 121,
DC_STR_NOT_SUPPORTED_BY_PROVIDER = 113,
DC_STR_ONE_MOMENT = 106,
DC_STR_OUTGOING_MESSAGES = 104,
DC_STR_PARTIAL_DOWNLOAD_MSG_BODY = 99,
DC_STR_PART_OF_TOTAL_USED = 116,
DC_STR_PROTECTION_DISABLED = 89,
DC_STR_PROTECTION_ENABLED = 88,
DC_STR_QUOTA_EXCEEDING_MSG_BODY = 98,
DC_STR_READRCPT = 31,
DC_STR_READRCPT_MAILBODY = 32,
DC_STR_REPLY_NOUN = 90,
DC_STR_SAVED_MESSAGES = 69,
DC_STR_SECURE_JOIN_GROUP_QR_DESC = 120,
DC_STR_SECURE_JOIN_REPLIES = 118,
DC_STR_SECURE_JOIN_STARTED = 117,
DC_STR_SELF = 2,
DC_STR_SELF_DELETED_MSG_BODY = 91,
DC_STR_SENDING = 110,
DC_STR_SERVER_TURNED_OFF = 92,
DC_STR_SETUP_CONTACT_QR_DESC = 119,
DC_STR_STICKER = 67,
DC_STR_STORAGE_ON_DOMAIN = 105,
DC_STR_SUBJECT_FOR_NEW_CONTACT = 73,
DC_STR_SYNC_MSG_BODY = 102,
DC_STR_SYNC_MSG_SUBJECT = 101,
DC_STR_UNKNOWN_SENDER_FOR_CHAT = 72,
DC_STR_UPDATE_REMINDER_MSG_BODY = 86,
DC_STR_UPDATING = 109,
DC_STR_VIDEO = 10,
DC_STR_VIDEOCHAT_INVITATION = 82,
DC_STR_VIDEOCHAT_INVITE_MSG_BODY = 83,
DC_STR_VOICEMESSAGE = 7,
DC_STR_WELCOME_MESSAGE = 71,
DC_TEXT1_DRAFT = 1,
DC_TEXT1_SELF = 3,
DC_TEXT1_USERNAME = 2,
DC_VIDEOCHATTYPE_BASICWEBRTC = 1,
DC_VIDEOCHATTYPE_JITSI = 2,
DC_VIDEOCHATTYPE_UNKNOWN = 0,
}
// Generated!
export const EventId2EventName: { [key: number]: string } = {
100: 'DC_EVENT_INFO',
101: 'DC_EVENT_SMTP_CONNECTED',
102: 'DC_EVENT_IMAP_CONNECTED',
103: 'DC_EVENT_SMTP_MESSAGE_SENT',
104: 'DC_EVENT_IMAP_MESSAGE_DELETED',
105: 'DC_EVENT_IMAP_MESSAGE_MOVED',
150: 'DC_EVENT_NEW_BLOB_FILE',
151: 'DC_EVENT_DELETED_BLOB_FILE',
300: 'DC_EVENT_WARNING',
400: 'DC_EVENT_ERROR',
410: 'DC_EVENT_ERROR_SELF_NOT_IN_GROUP',
2000: 'DC_EVENT_MSGS_CHANGED',
2005: 'DC_EVENT_INCOMING_MSG',
2008: 'DC_EVENT_MSGS_NOTICED',
2010: 'DC_EVENT_MSG_DELIVERED',
2012: 'DC_EVENT_MSG_FAILED',
2015: 'DC_EVENT_MSG_READ',
2020: 'DC_EVENT_CHAT_MODIFIED',
2021: 'DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED',
2030: 'DC_EVENT_CONTACTS_CHANGED',
2035: 'DC_EVENT_LOCATION_CHANGED',
2041: 'DC_EVENT_CONFIGURE_PROGRESS',
2051: 'DC_EVENT_IMEX_PROGRESS',
2052: 'DC_EVENT_IMEX_FILE_WRITTEN',
2060: 'DC_EVENT_SECUREJOIN_INVITER_PROGRESS',
2061: 'DC_EVENT_SECUREJOIN_JOINER_PROGRESS',
2100: 'DC_EVENT_CONNECTIVITY_CHANGED',
2110: 'DC_EVENT_SELFAVATAR_CHANGED',
2120: 'DC_EVENT_WEBXDC_STATUS_UPDATE',
}

91
node/lib/contact.ts Normal file
View File

@@ -0,0 +1,91 @@
import { integerToHexColor } from './util'
/* eslint-disable camelcase */
import binding from './binding'
const debug = require('debug')('deltachat:node:contact')
interface NativeContact {}
/**
* Wrapper around dc_contact_t*
*/
export class Contact {
constructor(public dc_contact: NativeContact) {
debug('Contact constructor')
}
toJson() {
debug('toJson')
return {
address: this.getAddress(),
color: this.color,
authName: this.authName,
status: this.status,
displayName: this.getDisplayName(),
id: this.getId(),
lastSeen: this.lastSeen,
name: this.getName(),
profileImage: this.getProfileImage(),
nameAndAddr: this.getNameAndAddress(),
isBlocked: this.isBlocked(),
isVerified: this.isVerified(),
}
}
getAddress(): string {
return binding.dcn_contact_get_addr(this.dc_contact)
}
/** Get original contact name.
* This is the name of the contact as defined by the contact themself.
* If the contact themself does not define such a name,
* an empty string is returned. */
get authName(): string {
return binding.dcn_contact_get_auth_name(this.dc_contact)
}
get color(): string {
return integerToHexColor(binding.dcn_contact_get_color(this.dc_contact))
}
/**
* contact's status
*
* Status is the last signature received in a message from this contact.
*/
get status(): string {
return binding.dcn_contact_get_status(this.dc_contact)
}
getDisplayName(): string {
return binding.dcn_contact_get_display_name(this.dc_contact)
}
getId(): number {
return binding.dcn_contact_get_id(this.dc_contact)
}
get lastSeen(): number {
return binding.dcn_contact_get_last_seen(this.dc_contact)
}
getName(): string {
return binding.dcn_contact_get_name(this.dc_contact)
}
getNameAndAddress(): string {
return binding.dcn_contact_get_name_n_addr(this.dc_contact)
}
getProfileImage(): string {
return binding.dcn_contact_get_profile_image(this.dc_contact)
}
isBlocked() {
return Boolean(binding.dcn_contact_is_blocked(this.dc_contact))
}
isVerified() {
return Boolean(binding.dcn_contact_is_verified(this.dc_contact))
}
}

928
node/lib/context.ts Normal file
View File

@@ -0,0 +1,928 @@
/* eslint-disable camelcase */
import binding from './binding'
import { C, EventId2EventName } from './constants'
import { Chat } from './chat'
import { ChatList } from './chatlist'
import { Contact } from './contact'
import { Message } from './message'
import { Lot } from './lot'
import { Locations } from './locations'
import rawDebug from 'debug'
import { AccountManager } from './deltachat'
import { join } from 'path'
import { EventEmitter } from 'stream'
const debug = rawDebug('deltachat:node:index')
const noop = function () {}
interface NativeContext {}
/**
* Wrapper around dcn_context_t*
*
* only acts as event emitter when created in standalone mode (without account manager)
* with `Context.open`
*/
export class Context extends EventEmitter {
constructor(
readonly manager: AccountManager | null,
private inner_dcn_context: NativeContext,
readonly account_id: number
) {
super()
debug('DeltaChat constructor')
}
/** Opens a stanalone context (without an account manager)
* automatically starts the event handler */
static open(cwd: string): Context {
const dbFile = join(cwd, 'db.sqlite')
const context = new Context(null, binding.dcn_context_new(dbFile), 42)
debug('Opened context')
function handleCoreEvent(
eventId: number,
data1: number,
data2: number | string
) {
const eventString = EventId2EventName[eventId]
debug(eventString, data1, data2)
if (!context.emit) {
console.log('Received an event but EventEmitter is already destroyed.')
console.log(eventString, 42, data1, data2)
return
}
context.emit(eventString, 42, data1, data2)
}
binding.dcn_start_event_handler(
context.dcn_context,
handleCoreEvent.bind(this)
)
debug('Started event handler')
return context
}
get dcn_context() {
return this.inner_dcn_context
}
get is_open() {
return Boolean(binding.dcn_context_is_open())
}
open(passphrase?: string) {
return Boolean(
binding.dcn_context_open(this.dcn_context, passphrase ? passphrase : '')
)
}
unref() {
binding.dcn_context_unref(this.dcn_context)
;(this.inner_dcn_context as any) = null
}
acceptChat(chatId: number) {
binding.dcn_accept_chat(this.dcn_context, chatId)
}
blockChat(chatId: number) {
binding.dcn_block_chat(this.dcn_context, chatId)
}
addAddressBook(addressBook: string) {
debug(`addAddressBook ${addressBook}`)
return binding.dcn_add_address_book(this.dcn_context, addressBook)
}
addContactToChat(chatId: number, contactId: number) {
debug(`addContactToChat ${chatId} ${contactId}`)
return Boolean(
binding.dcn_add_contact_to_chat(
this.dcn_context,
Number(chatId),
Number(contactId)
)
)
}
addDeviceMessage(label: string, msg: Message | string) {
debug(`addDeviceMessage ${label} ${msg}`)
if (!msg) {
throw new Error('invalid msg parameter')
}
if (typeof label !== 'string') {
throw new Error('invalid label parameter, must be a string')
}
if (typeof msg === 'string') {
const msgObj = this.messageNew()
msgObj.setText(msg)
msg = msgObj
}
if (!msg.dc_msg) {
throw new Error('invalid msg object')
}
return binding.dcn_add_device_msg(this.dcn_context, label, msg.dc_msg)
}
setChatVisibility(
chatId: number,
visibility:
| C.DC_CHAT_VISIBILITY_NORMAL
| C.DC_CHAT_VISIBILITY_ARCHIVED
| C.DC_CHAT_VISIBILITY_PINNED
) {
debug(`setChatVisibility ${chatId} ${visibility}`)
binding.dcn_set_chat_visibility(
this.dcn_context,
Number(chatId),
visibility
)
}
blockContact(contactId: number, block: boolean) {
debug(`blockContact ${contactId} ${block}`)
binding.dcn_block_contact(
this.dcn_context,
Number(contactId),
block ? 1 : 0
)
}
checkQrCode(qrCode: string) {
debug(`checkQrCode ${qrCode}`)
const dc_lot = binding.dcn_check_qr(this.dcn_context, qrCode)
let result = dc_lot ? new Lot(dc_lot) : null
if (result) {
return { id: result.getId(), ...result.toJson() }
}
return result
}
configure(opts: any): Promise<void> {
return new Promise((resolve, reject) => {
debug('configure')
const onSuccess = () => {
removeListeners()
resolve()
}
const onFail = (error: string) => {
removeListeners()
reject(new Error(error))
}
const onConfigure = (accountId: number, data1: number, data2: string) => {
if (this.account_id !== accountId) {
return
}
if (data1 === 0) return onFail(data2)
else if (data1 === 1000) return onSuccess()
}
const removeListeners = () => {
;(this.manager || this).removeListener(
'DC_EVENT_CONFIGURE_PROGRESS',
onConfigure
)
}
const registerListeners = () => {
;(this.manager || this).on('DC_EVENT_CONFIGURE_PROGRESS', onConfigure)
}
registerListeners()
if (!opts) opts = {}
Object.keys(opts).forEach((key) => {
const value = opts[key]
this.setConfig(key, value)
})
binding.dcn_configure(this.dcn_context)
})
}
continueKeyTransfer(messageId: number, setupCode: string) {
debug(`continueKeyTransfer ${messageId}`)
return new Promise((resolve, reject) => {
binding.dcn_continue_key_transfer(
this.dcn_context,
Number(messageId),
setupCode,
(result: number) => resolve(result === 1)
)
})
}
/** @returns chatId */
createBroadcastList(): number {
debug(`createBroadcastList`)
return binding.dcn_create_broadcast_list(this.dcn_context)
}
/** @returns chatId */
createChatByContactId(contactId: number): number {
debug(`createChatByContactId ${contactId}`)
return binding.dcn_create_chat_by_contact_id(
this.dcn_context,
Number(contactId)
)
}
/** @returns contactId */
createContact(name: string, addr: string): number {
debug(`createContact ${name} ${addr}`)
return binding.dcn_create_contact(this.dcn_context, name, addr)
}
/**
*
* @param chatName The name of the chat that should be created
* @param is_protected Whether the chat should be protected at creation time
* @returns chatId
*/
createGroupChat(chatName: string, is_protected: boolean = false): number {
debug(`createGroupChat ${chatName} [protected:${is_protected}]`)
return binding.dcn_create_group_chat(
this.dcn_context,
is_protected ? 1 : 0,
chatName
)
}
deleteChat(chatId: number) {
debug(`deleteChat ${chatId}`)
binding.dcn_delete_chat(this.dcn_context, Number(chatId))
}
deleteContact(contactId: number) {
debug(`deleteContact ${contactId}`)
return Boolean(
binding.dcn_delete_contact(this.dcn_context, Number(contactId))
)
}
deleteMessages(messageIds: number[]) {
if (!Array.isArray(messageIds)) {
messageIds = [messageIds]
}
messageIds = messageIds.map((id) => Number(id))
debug('deleteMessages', messageIds)
binding.dcn_delete_msgs(this.dcn_context, messageIds)
}
forwardMessages(messageIds: number[], chatId: number) {
if (!Array.isArray(messageIds)) {
messageIds = [messageIds]
}
messageIds = messageIds.map((id) => Number(id))
debug('forwardMessages', messageIds)
binding.dcn_forward_msgs(this.dcn_context, messageIds, chatId)
}
getBlobdir(): string {
debug('getBlobdir')
return binding.dcn_get_blobdir(this.dcn_context)
}
getBlockedCount(): number {
debug('getBlockedCount')
return binding.dcn_get_blocked_cnt(this.dcn_context)
}
getBlockedContacts(): number[] {
debug('getBlockedContacts')
return binding.dcn_get_blocked_contacts(this.dcn_context)
}
getChat(chatId: number) {
debug(`getChat ${chatId}`)
const dc_chat = binding.dcn_get_chat(this.dcn_context, Number(chatId))
return dc_chat ? new Chat(dc_chat) : null
}
getChatContacts(chatId: number): number[] {
debug(`getChatContacts ${chatId}`)
return binding.dcn_get_chat_contacts(this.dcn_context, Number(chatId))
}
getChatIdByContactId(contactId: number): number {
debug(`getChatIdByContactId ${contactId}`)
return binding.dcn_get_chat_id_by_contact_id(
this.dcn_context,
Number(contactId)
)
}
getChatMedia(
chatId: number,
msgType1: number,
msgType2: number,
msgType3: number
): number[] {
debug(`getChatMedia ${chatId}`)
return binding.dcn_get_chat_media(
this.dcn_context,
Number(chatId),
msgType1,
msgType2 || 0,
msgType3 || 0
)
}
getMimeHeaders(messageId: number): string {
debug(`getMimeHeaders ${messageId}`)
return binding.dcn_get_mime_headers(this.dcn_context, Number(messageId))
}
getChatlistItemSummary(chatId: number, messageId: number) {
debug(`getChatlistItemSummary ${chatId} ${messageId}`)
return new Lot(
binding.dcn_chatlist_get_summary2(this.dcn_context, chatId, messageId)
)
}
getChatMessages(chatId: number, flags: number, marker1before: number) {
debug(`getChatMessages ${chatId} ${flags} ${marker1before}`)
return binding.dcn_get_chat_msgs(
this.dcn_context,
Number(chatId),
flags,
marker1before
)
}
/**
* Get encryption info for a chat.
* Get a multi-line encryption info, containing encryption preferences of all members.
* Can be used to find out why messages sent to group are not encrypted.
*
* @param chatId ID of the chat to get the encryption info for.
* @return Multi-line text, must be released using dc_str_unref() after usage.
*/
getChatEncrytionInfo(chatId: number): string {
return binding.dcn_get_chat_encrinfo(this.dcn_context, chatId)
}
getChats(listFlags: number, queryStr: string, queryContactId: number) {
debug('getChats')
const result = []
const list = this.getChatList(listFlags, queryStr, queryContactId)
const count = list.getCount()
for (let i = 0; i < count; i++) {
result.push(list.getChatId(i))
}
return result
}
getChatList(listFlags: number, queryStr: string, queryContactId: number) {
listFlags = listFlags || 0
queryStr = queryStr || ''
queryContactId = queryContactId || 0
debug(`getChatList ${listFlags} ${queryStr} ${queryContactId}`)
return new ChatList(
binding.dcn_get_chatlist(
this.dcn_context,
listFlags,
queryStr,
Number(queryContactId)
)
)
}
getConfig(key: string): string {
debug(`getConfig ${key}`)
return binding.dcn_get_config(this.dcn_context, key)
}
getContact(contactId: number) {
debug(`getContact ${contactId}`)
const dc_contact = binding.dcn_get_contact(
this.dcn_context,
Number(contactId)
)
return dc_contact ? new Contact(dc_contact) : null
}
getContactEncryptionInfo(contactId: number) {
debug(`getContactEncryptionInfo ${contactId}`)
return binding.dcn_get_contact_encrinfo(this.dcn_context, Number(contactId))
}
getContacts(listFlags: number, query: string) {
listFlags = listFlags || 0
query = query || ''
debug(`getContacts ${listFlags} ${query}`)
return binding.dcn_get_contacts(this.dcn_context, listFlags, query)
}
wasDeviceMessageEverAdded(label: string) {
debug(`wasDeviceMessageEverAdded ${label}`)
const added = binding.dcn_was_device_msg_ever_added(this.dcn_context, label)
return added === 1
}
getDraft(chatId: number) {
debug(`getDraft ${chatId}`)
const dc_msg = binding.dcn_get_draft(this.dcn_context, Number(chatId))
return dc_msg ? new Message(dc_msg) : null
}
getFreshMessageCount(chatId: number): number {
debug(`getFreshMessageCount ${chatId}`)
return binding.dcn_get_fresh_msg_cnt(this.dcn_context, Number(chatId))
}
getFreshMessages() {
debug('getFreshMessages')
return binding.dcn_get_fresh_msgs(this.dcn_context)
}
getInfo() {
debug('getInfo')
const info = binding.dcn_get_info(this.dcn_context)
return AccountManager.parseGetInfo(info)
}
getMessage(messageId: number) {
debug(`getMessage ${messageId}`)
const dc_msg = binding.dcn_get_msg(this.dcn_context, Number(messageId))
return dc_msg ? new Message(dc_msg) : null
}
getMessageCount(chatId: number): number {
debug(`getMessageCount ${chatId}`)
return binding.dcn_get_msg_cnt(this.dcn_context, Number(chatId))
}
getMessageInfo(messageId: number): string {
debug(`getMessageInfo ${messageId}`)
return binding.dcn_get_msg_info(this.dcn_context, Number(messageId))
}
getMessageHTML(messageId: number): string {
debug(`getMessageHTML ${messageId}`)
return binding.dcn_get_msg_html(this.dcn_context, Number(messageId))
}
getNextMediaMessage(
messageId: number,
msgType1: number,
msgType2: number,
msgType3: number
) {
debug(
`getNextMediaMessage ${messageId} ${msgType1} ${msgType2} ${msgType3}`
)
return this._getNextMedia(messageId, 1, msgType1, msgType2, msgType3)
}
getPreviousMediaMessage(
messageId: number,
msgType1: number,
msgType2: number,
msgType3: number
) {
debug(
`getPreviousMediaMessage ${messageId} ${msgType1} ${msgType2} ${msgType3}`
)
return this._getNextMedia(messageId, -1, msgType1, msgType2, msgType3)
}
_getNextMedia(
messageId: number,
dir: number,
msgType1: number,
msgType2: number,
msgType3: number
): number {
return binding.dcn_get_next_media(
this.dcn_context,
Number(messageId),
dir,
msgType1 || 0,
msgType2 || 0,
msgType3 || 0
)
}
getSecurejoinQrCode(chatId: number): string {
debug(`getSecurejoinQrCode ${chatId}`)
return binding.dcn_get_securejoin_qr(this.dcn_context, Number(chatId))
}
getSecurejoinQrCodeSVG(chatId: number): string {
debug(`getSecurejoinQrCodeSVG ${chatId}`)
return binding.dcn_get_securejoin_qr_svg(this.dcn_context, chatId)
}
startIO(): void {
debug(`startIO`)
binding.dcn_start_io(this.dcn_context)
}
stopIO(): void {
debug(`stopIO`)
binding.dcn_stop_io(this.dcn_context)
}
stopOngoingProcess(): void {
debug(`stopOngoingProcess`)
binding.dcn_stop_ongoing_process(this.dcn_context)
}
/**
*
* @deprectated please use `AccountManager.getSystemInfo()` instead
*/
static getSystemInfo() {
return AccountManager.getSystemInfo()
}
getConnectivity(): number {
return binding.dcn_get_connectivity(this.dcn_context)
}
getConnectivityHTML(): String {
return binding.dcn_get_connectivity_html(this.dcn_context)
}
importExport(what: number, param1: string, param2 = '') {
debug(`importExport ${what} ${param1} ${param2}`)
binding.dcn_imex(this.dcn_context, what, param1, param2)
}
importExportHasBackup(dir: string) {
debug(`importExportHasBackup ${dir}`)
return binding.dcn_imex_has_backup(this.dcn_context, dir)
}
initiateKeyTransfer(): Promise<string> {
return new Promise((resolve, reject) => {
debug('initiateKeyTransfer2')
binding.dcn_initiate_key_transfer(this.dcn_context, resolve)
})
}
isConfigured() {
debug('isConfigured')
return Boolean(binding.dcn_is_configured(this.dcn_context))
}
isContactInChat(chatId: number, contactId: number) {
debug(`isContactInChat ${chatId} ${contactId}`)
return Boolean(
binding.dcn_is_contact_in_chat(
this.dcn_context,
Number(chatId),
Number(contactId)
)
)
}
/**
*
* @returns resulting chat id or 0 on error
*/
joinSecurejoin(qrCode: string): number {
debug(`joinSecurejoin ${qrCode}`)
return binding.dcn_join_securejoin(this.dcn_context, qrCode)
}
lookupContactIdByAddr(addr: string): number {
debug(`lookupContactIdByAddr ${addr}`)
return binding.dcn_lookup_contact_id_by_addr(this.dcn_context, addr)
}
markNoticedChat(chatId: number) {
debug(`markNoticedChat ${chatId}`)
binding.dcn_marknoticed_chat(this.dcn_context, Number(chatId))
}
markSeenMessages(messageIds: number[]) {
if (!Array.isArray(messageIds)) {
messageIds = [messageIds]
}
messageIds = messageIds.map((id) => Number(id))
debug('markSeenMessages', messageIds)
binding.dcn_markseen_msgs(this.dcn_context, messageIds)
}
maybeNetwork() {
debug('maybeNetwork')
binding.dcn_maybe_network(this.dcn_context)
}
messageNew(viewType = C.DC_MSG_TEXT) {
debug(`messageNew ${viewType}`)
return new Message(binding.dcn_msg_new(this.dcn_context, viewType))
}
removeContactFromChat(chatId: number, contactId: number) {
debug(`removeContactFromChat ${chatId} ${contactId}`)
return Boolean(
binding.dcn_remove_contact_from_chat(
this.dcn_context,
Number(chatId),
Number(contactId)
)
)
}
/**
*
* @param chatId ID of the chat to search messages in. Set this to 0 for a global search.
* @param query The query to search for.
*/
searchMessages(chatId: number, query: string): number[] {
debug(`searchMessages ${chatId} ${query}`)
return binding.dcn_search_msgs(this.dcn_context, Number(chatId), query)
}
sendMessage(chatId: number, msg: string | Message) {
debug(`sendMessage ${chatId}`)
if (!msg) {
throw new Error('invalid msg parameter')
}
if (typeof msg === 'string') {
const msgObj = this.messageNew()
msgObj.setText(msg)
msg = msgObj
}
if (!msg.dc_msg) {
throw new Error('invalid msg object')
}
return binding.dcn_send_msg(this.dcn_context, Number(chatId), msg.dc_msg)
}
downloadFullMessage(messageId: number) {
binding.dcn_download_full_msg(this.dcn_context, messageId)
}
/**
*
* @returns {Promise<number>} Promise that resolves into the resulting message id
*/
sendVideochatInvitation(chatId: number): Promise<number> {
debug(`sendVideochatInvitation ${chatId}`)
return new Promise((resolve, reject) => {
binding.dcn_send_videochat_invitation(
this.dcn_context,
chatId,
(result: number) => {
if (result !== 0) {
resolve(result)
} else {
reject(
'Videochatinvitation failed to send, see error events for detailed info'
)
}
}
)
})
}
setChatName(chatId: number, name: string) {
debug(`setChatName ${chatId} ${name}`)
return Boolean(
binding.dcn_set_chat_name(this.dcn_context, Number(chatId), name)
)
}
/**
*
* @param chatId
* @param protect
* @returns success boolean
*/
setChatProtection(chatId: number, protect: boolean) {
debug(`setChatProtection ${chatId} ${protect}`)
return Boolean(
binding.dcn_set_chat_protection(
this.dcn_context,
Number(chatId),
protect ? 1 : 0
)
)
}
getChatEphemeralTimer(chatId: number): number {
debug(`getChatEphemeralTimer ${chatId}`)
return binding.dcn_get_chat_ephemeral_timer(
this.dcn_context,
Number(chatId)
)
}
setChatEphemeralTimer(chatId: number, timer: number) {
debug(`setChatEphemeralTimer ${chatId} ${timer}`)
return Boolean(
binding.dcn_set_chat_ephemeral_timer(
this.dcn_context,
Number(chatId),
Number(timer)
)
)
}
setChatProfileImage(chatId: number, image: string) {
debug(`setChatProfileImage ${chatId} ${image}`)
return Boolean(
binding.dcn_set_chat_profile_image(
this.dcn_context,
Number(chatId),
image || ''
)
)
}
setConfig(key: string, value: string | boolean | number): number {
debug(`setConfig (string) ${key} ${value}`)
if (value === null) {
return binding.dcn_set_config_null(this.dcn_context, key)
} else {
if (typeof value === 'boolean') {
value = value === true ? '1' : '0'
} else if (typeof value === 'number') {
value = String(value)
}
return binding.dcn_set_config(this.dcn_context, key, value)
}
}
setConfigFromQr(qrcodeContent: string): boolean {
return Boolean(
binding.dcn_set_config_from_qr(this.dcn_context, qrcodeContent)
)
}
estimateDeletionCount(fromServer: boolean, seconds: number): number {
debug(`estimateDeletionCount fromServer: ${fromServer} seconds: ${seconds}`)
return binding.dcn_estimate_deletion_cnt(
this.dcn_context,
fromServer === true ? 1 : 0,
seconds
)
}
setStockTranslation(stockId: number, stockMsg: string) {
debug(`setStockTranslation ${stockId} ${stockMsg}`)
return Boolean(
binding.dcn_set_stock_translation(
this.dcn_context,
Number(stockId),
stockMsg
)
)
}
setDraft(chatId: number, msg: Message | null) {
debug(`setDraft ${chatId}`)
binding.dcn_set_draft(
this.dcn_context,
Number(chatId),
msg ? msg.dc_msg : null
)
}
setLocation(latitude: number, longitude: number, accuracy: number) {
debug(`setLocation ${latitude}`)
binding.dcn_set_location(
this.dcn_context,
Number(latitude),
Number(longitude),
Number(accuracy)
)
}
/*
* @param chatId Chat-id to get location information for.
* 0 to get locations independently of the chat.
* @param contactId Contact id to get location information for.
* If also a chat-id is given, this should be a member of the given chat.
* 0 to get locations independently of the contact.
* @param timestampFrom Start of timespan to return.
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
* 0 for "start from the beginning".
* @param timestampTo End of timespan to return.
* Must be given in number of seconds since 00:00 hours, Jan 1, 1970 UTC.
* 0 for "all up to now".
* @return Array of locations, NULL is never returned.
* The array is sorted decending;
* the first entry in the array is the location with the newest timestamp.
*
* Examples:
* // get locations from the last hour for a global map
* getLocations(0, 0, time(NULL)-60*60, 0);
*
* // get locations from a contact for a global map
* getLocations(0, contact_id, 0, 0);
*
* // get all locations known for a given chat
* getLocations(chat_id, 0, 0, 0);
*
* // get locations from a single contact for a given chat
* getLocations(chat_id, contact_id, 0, 0);
*/
getLocations(
chatId: number,
contactId: number,
timestampFrom = 0,
timestampTo = 0
) {
const locations = new Locations(
binding.dcn_get_locations(
this.dcn_context,
Number(chatId),
Number(contactId),
timestampFrom,
timestampTo
)
)
return locations.toJson()
}
/**
*
* @param duration The duration (0 for no mute, -1 for forever mute, everything else is is the relative mute duration from now in seconds)
*/
setChatMuteDuration(chatId: number, duration: number) {
return Boolean(
binding.dcn_set_chat_mute_duration(this.dcn_context, chatId, duration)
)
}
/** get information about the provider */
getProviderFromEmail(email: string) {
debug('DeltaChat.getProviderFromEmail')
const provider = binding.dcn_provider_new_from_email(
this.dcn_context,
email
)
if (!provider) {
return undefined
}
return {
before_login_hint: binding.dcn_provider_get_before_login_hint(provider),
overview_page: binding.dcn_provider_get_overview_page(provider),
status: binding.dcn_provider_get_status(provider),
}
}
sendWebxdcStatusUpdate<T>(
msgId: number,
json: WebxdcSendingStatusUpdate<T>,
descr: string
) {
return Boolean(
binding.dcn_send_webxdc_status_update(
this.dcn_context,
msgId,
JSON.stringify(json),
descr
)
)
}
getWebxdcStatusUpdates<T>(
msgId: number,
serial = 0
): WebxdcReceivedStatusUpdate<T>[] {
return JSON.parse(
binding.dcn_get_webxdc_status_updates(this.dcn_context, msgId, serial)
)
}
/** the string contains the binary data, it is an "u8 string", maybe we will use a more efficient type in the future. */
getWebxdcBlob(message: Message, filename: string): Buffer | null {
return binding.dcn_msg_get_webxdc_blob(message.dc_msg, filename)
}
}
type WebxdcSendingStatusUpdate<T> = {
/** the payload, deserialized json:
* any javascript primitive, array or object. */
payload: T
/** optional, short, informational message that will be added to the chat,
* eg. "Alice voted" or "Bob scored 123 in MyGame";
* usually only one line of text is shown,
* use this option sparingly to not spam the chat. */
info?: string
/** optional, short text, shown beside app icon;
* it is recommended to use some aggregated value,
* eg. "8 votes", "Highscore: 123" */
summary?: string
}
type WebxdcReceivedStatusUpdate<T> = {
/** the payload, deserialized json */
payload: T
/** the serial number of this update. Serials are larger `0` and newer serials have higher numbers. */
serial: number
/** the maximum serial currently known.
* If `max_serial` equals `serial` this update is the last update (until new network messages arrive). */
max_serial: number
/** optional, short, informational message. */
info?: string
/** optional, short text, shown beside app icon. If there are no updates, an empty JSON-array is returned. */
summary?: string
}

200
node/lib/deltachat.ts Normal file
View File

@@ -0,0 +1,200 @@
/* eslint-disable camelcase */
import binding from './binding'
import { EventId2EventName } from './constants'
import { EventEmitter } from 'events'
import { existsSync } from 'fs'
import rawDebug from 'debug'
import { tmpdir } from 'os'
import { join } from 'path'
import { Context } from './context'
const debug = rawDebug('deltachat:node:index')
const noop = function () {}
interface NativeAccount {}
/**
* Wrapper around dcn_account_t*
*/
export class AccountManager extends EventEmitter {
dcn_accounts: NativeAccount
accountDir: string
constructor(cwd: string, os = 'deltachat-node') {
debug('DeltaChat constructor')
super()
this.accountDir = cwd
this.dcn_accounts = binding.dcn_accounts_new(os, this.accountDir)
}
getAllAccountIds() {
return binding.dcn_accounts_get_all(this.dcn_accounts)
}
selectAccount(account_id: number) {
return binding.dcn_accounts_select_account(this.dcn_accounts, account_id)
}
selectedAccount(): number {
return binding.dcn_accounts_get_selected_account(this.dcn_accounts)
}
addAccount(): number {
return binding.dcn_accounts_add_account(this.dcn_accounts)
}
addClosedAccount(): number {
return binding.dcn_accounts_add_closed_account(this.dcn_accounts)
}
removeAccount(account_id: number) {
return binding.dcn_accounts_remove_account(this.dcn_accounts, account_id)
}
accountContext(account_id: number) {
const native_context = binding.dcn_accounts_get_account(
this.dcn_accounts,
account_id
)
return new Context(this, native_context, account_id)
}
migrateAccount(dbfile: string): number {
return binding.dcn_accounts_migrate_account(this.dcn_accounts, dbfile)
}
close() {
this.stopIO()
debug('unrefing context')
binding.dcn_accounts_unref(this.dcn_accounts)
debug('Unref end')
}
emit(
event: string | symbol,
account_id: number,
data1: any,
data2: any
): boolean {
super.emit('ALL', event, account_id, data1, data2)
return super.emit(event, account_id, data1, data2)
}
handleCoreEvent(
eventId: number,
accountId: number,
data1: number,
data2: number | string
) {
const eventString = EventId2EventName[eventId]
debug('event', eventString, accountId, data1, data2)
debug(eventString, data1, data2)
if (!this.emit) {
console.log('Received an event but EventEmitter is already destroyed.')
console.log(eventString, data1, data2)
return
}
this.emit(eventString, accountId, data1, data2)
}
startEvents() {
if (this.dcn_accounts === null) {
throw new Error('dcn_account is null')
}
binding.dcn_accounts_start_event_handler(
this.dcn_accounts,
this.handleCoreEvent.bind(this)
)
debug('Started event handler')
}
startIO() {
binding.dcn_accounts_start_io(this.dcn_accounts)
}
stopIO() {
binding.dcn_accounts_stop_io(this.dcn_accounts)
}
static maybeValidAddr(addr: string) {
debug('DeltaChat.maybeValidAddr')
if (addr === null) return false
return Boolean(binding.dcn_maybe_valid_addr(addr))
}
static parseGetInfo(info: string) {
debug('static _getInfo')
const result: { [key: string]: string } = {}
const regex = /^(\w+)=(.*)$/i
info
.split('\n')
.filter(Boolean)
.forEach((line) => {
const match = regex.exec(line)
if (match) {
result[match[1]] = match[2]
}
})
return result
}
static newTemporary() {
let directory = null
while (true) {
const randomString = Math.random().toString(36).substr(2, 5)
directory = join(tmpdir(), 'deltachat-' + randomString)
if (!existsSync(directory)) break
}
const dc = new AccountManager(directory)
const accountId = dc.addAccount()
const context = dc.accountContext(accountId)
return { dc, context, accountId, directory }
}
static getSystemInfo() {
debug('DeltaChat.getSystemInfo')
const { dc, context } = AccountManager.newTemporary()
const info = AccountManager.parseGetInfo(
binding.dcn_get_info(context.dcn_context)
)
const {
deltachat_core_version,
sqlite_version,
sqlite_thread_safe,
libetpan_version,
openssl_version,
compile_date,
arch,
} = info
const result = {
deltachat_core_version,
sqlite_version,
sqlite_thread_safe,
libetpan_version,
openssl_version,
compile_date,
arch,
}
context.unref()
dc.close()
return result
}
/** get information about the provider
*
* This function creates a temporary context to be standalone,
* if posible use `Context.getProviderFromEmail` instead. (otherwise potential proxy settings are not used)
* @deprecated
*/
static getProviderFromEmail(email: string) {
debug('DeltaChat.getProviderFromEmail')
const { dc, context } = AccountManager.newTemporary()
const provider = context.getProviderFromEmail(email)
context.unref()
dc.close()
return provider
}
}

20
node/lib/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import { AccountManager } from './deltachat'
export default AccountManager
export { Context } from './context'
export { Chat } from './chat'
export { ChatList } from './chatlist'
export { C } from './constants'
export { Contact } from './contact'
export { AccountManager as DeltaChat }
export { Locations } from './locations'
export { Lot } from './lot'
export {
Message,
MessageState,
MessageViewType,
MessageDownloadState,
} from './message'
export * from './types'

79
node/lib/locations.ts Normal file
View File

@@ -0,0 +1,79 @@
/* eslint-disable camelcase */
const binding = require('../binding')
const debug = require('debug')('deltachat:node:locations')
interface NativeLocations {}
/**
* Wrapper around dc_location_t*
*/
export class Locations {
constructor(public dc_locations: NativeLocations) {
debug('Locations constructor')
}
locationToJson(index: number) {
debug('locationToJson')
return {
accuracy: this.getAccuracy(index),
latitude: this.getLatitude(index),
longitude: this.getLongitude(index),
timestamp: this.getTimestamp(index),
contactId: this.getContactId(index),
msgId: this.getMsgId(index),
chatId: this.getChatId(index),
isIndependent: this.isIndependent(index),
marker: this.getMarker(index),
}
}
toJson(): ReturnType<Locations['locationToJson']>[] {
debug('toJson')
const locations = []
const count = this.getCount()
for (let index = 0; index < count; index++) {
locations.push(this.locationToJson(index))
}
return locations
}
getCount(): number {
return binding.dcn_array_get_cnt(this.dc_locations)
}
getAccuracy(index: number): number {
return binding.dcn_array_get_accuracy(this.dc_locations, index)
}
getLatitude(index: number): number {
return binding.dcn_array_get_latitude(this.dc_locations, index)
}
getLongitude(index: number): number {
return binding.dcn_array_get_longitude(this.dc_locations, index)
}
getTimestamp(index: number): number {
return binding.dcn_array_get_timestamp(this.dc_locations, index)
}
getMsgId(index: number): number {
return binding.dcn_array_get_msg_id(this.dc_locations, index)
}
getContactId(index: number): number {
return binding.dcn_array_get_contact_id(this.dc_locations, index)
}
getChatId(index: number): number {
return binding.dcn_array_get_chat_id(this.dc_locations, index)
}
isIndependent(index: number): boolean {
return binding.dcn_array_is_independent(this.dc_locations, index)
}
getMarker(index: number): string {
return binding.dcn_array_get_marker(this.dc_locations, index)
}
}

49
node/lib/lot.ts Normal file
View File

@@ -0,0 +1,49 @@
/* eslint-disable camelcase */
const binding = require('../binding')
const debug = require('debug')('deltachat:node:lot')
interface NativeLot {}
/**
* Wrapper around dc_lot_t*
*/
export class Lot {
constructor(public dc_lot: NativeLot) {
debug('Lot constructor')
}
toJson() {
debug('toJson')
return {
state: this.getState(),
text1: this.getText1(),
text1Meaning: this.getText1Meaning(),
text2: this.getText2(),
timestamp: this.getTimestamp(),
}
}
getId(): number {
return binding.dcn_lot_get_id(this.dc_lot)
}
getState(): number {
return binding.dcn_lot_get_state(this.dc_lot)
}
getText1(): string {
return binding.dcn_lot_get_text1(this.dc_lot)
}
getText1Meaning(): string {
return binding.dcn_lot_get_text1_meaning(this.dc_lot)
}
getText2(): string {
return binding.dcn_lot_get_text2(this.dc_lot)
}
getTimestamp(): number {
return binding.dcn_lot_get_timestamp(this.dc_lot)
}
}

366
node/lib/message.ts Normal file
View File

@@ -0,0 +1,366 @@
/* eslint-disable camelcase */
import binding from './binding'
import { C } from './constants'
import { Lot } from './lot'
import { Chat } from './chat'
const debug = require('debug')('deltachat:node:message')
export enum MessageDownloadState {
Available = C.DC_DOWNLOAD_AVAILABLE,
Done = C.DC_DOWNLOAD_DONE,
Failure = C.DC_DOWNLOAD_FAILURE,
InProgress = C.DC_DOWNLOAD_IN_PROGRESS,
}
/**
* Helper class for message states so you can do e.g.
*
* if (msg.getState().isPending()) { .. }
*
*/
export class MessageState {
constructor(public state: number) {
debug(`MessageState constructor ${state}`)
}
isUndefined() {
return this.state === C.DC_STATE_UNDEFINED
}
isFresh() {
return this.state === C.DC_STATE_IN_FRESH
}
isNoticed() {
return this.state === C.DC_STATE_IN_NOTICED
}
isSeen() {
return this.state === C.DC_STATE_IN_SEEN
}
isPending() {
return this.state === C.DC_STATE_OUT_PENDING
}
isFailed() {
return this.state === C.DC_STATE_OUT_FAILED
}
isDelivered() {
return this.state === C.DC_STATE_OUT_DELIVERED
}
isReceived() {
return this.state === C.DC_STATE_OUT_MDN_RCVD
}
}
/**
* Helper class for message types so you can do e.g.
*
* if (msg.getViewType().isVideo()) { .. }
*
*/
export class MessageViewType {
constructor(public viewType: number) {
debug(`MessageViewType constructor ${viewType}`)
}
isText() {
return this.viewType === C.DC_MSG_TEXT
}
isImage() {
return this.viewType === C.DC_MSG_IMAGE || this.viewType === C.DC_MSG_GIF
}
isGif() {
return this.viewType === C.DC_MSG_GIF
}
isAudio() {
return this.viewType === C.DC_MSG_AUDIO || this.viewType === C.DC_MSG_VOICE
}
isVoice() {
return this.viewType === C.DC_MSG_VOICE
}
isVideo() {
return this.viewType === C.DC_MSG_VIDEO
}
isFile() {
return this.viewType === C.DC_MSG_FILE
}
isVideochatInvitation() {
return this.viewType === C.DC_MSG_VIDEOCHAT_INVITATION
}
}
interface NativeMessage {}
/**
* Wrapper around dc_msg_t*
*/
export class Message {
constructor(public dc_msg: NativeMessage) {
debug('Message constructor')
}
toJson() {
debug('toJson')
const quotedMessage = this.getQuotedMessage()
const viewType = binding.dcn_msg_get_viewtype(this.dc_msg)
return {
chatId: this.getChatId(),
webxdcInfo: viewType == C.DC_MSG_WEBXDC ? this.webxdcInfo : null,
downloadState: this.downloadState,
duration: this.getDuration(),
file: this.getFile(),
fromId: this.getFromId(),
id: this.getId(),
quotedText: this.getQuotedText(),
quotedMessageId: quotedMessage ? quotedMessage.getId() : null,
receivedTimestamp: this.getReceivedTimestamp(),
sortTimestamp: this.getSortTimestamp(),
text: this.getText(),
timestamp: this.getTimestamp(),
hasLocation: this.hasLocation(),
hasHTML: this.hasHTML,
viewType,
state: binding.dcn_msg_get_state(this.dc_msg),
hasDeviatingTimestamp: this.hasDeviatingTimestamp(),
showPadlock: this.getShowpadlock(),
summary: this.getSummary().toJson(),
subject: this.subject,
isSetupmessage: this.isSetupmessage(),
isInfo: this.isInfo(),
isForwarded: this.isForwarded(),
dimensions: {
height: this.getHeight(),
width: this.getWidth(),
},
videochatType: this.getVideochatType(),
videochatUrl: this.getVideochatUrl(),
overrideSenderName: this.overrideSenderName,
parentId: this.parent?.getId(),
}
}
getChatId(): number {
return binding.dcn_msg_get_chat_id(this.dc_msg)
}
get webxdcInfo(): { name: string; icon: string; summary: string } | null {
let info = binding.dcn_msg_get_webxdc_info(this.dc_msg)
return info
? JSON.parse(binding.dcn_msg_get_webxdc_info(this.dc_msg))
: null
}
get downloadState(): MessageDownloadState {
return binding.dcn_msg_get_download_state(this.dc_msg)
}
get parent(): Message | null {
let msg = binding.dcn_msg_get_parent(this.dc_msg)
return msg ? new Message(msg) : null
}
getDuration(): number {
return binding.dcn_msg_get_duration(this.dc_msg)
}
getFile(): string {
return binding.dcn_msg_get_file(this.dc_msg)
}
getFilebytes(): number {
return binding.dcn_msg_get_filebytes(this.dc_msg)
}
getFilemime(): string {
return binding.dcn_msg_get_filemime(this.dc_msg)
}
getFilename(): string {
return binding.dcn_msg_get_filename(this.dc_msg)
}
getFromId(): number {
return binding.dcn_msg_get_from_id(this.dc_msg)
}
getHeight(): number {
return binding.dcn_msg_get_height(this.dc_msg)
}
getId(): number {
return binding.dcn_msg_get_id(this.dc_msg)
}
getQuotedText(): string {
return binding.dcn_msg_get_quoted_text(this.dc_msg)
}
getQuotedMessage(): Message | null {
const dc_msg = binding.dcn_msg_get_quoted_msg(this.dc_msg)
return dc_msg ? new Message(dc_msg) : null
}
getReceivedTimestamp(): number {
return binding.dcn_msg_get_received_timestamp(this.dc_msg)
}
getSetupcodebegin() {
return binding.dcn_msg_get_setupcodebegin(this.dc_msg)
}
getShowpadlock() {
return Boolean(binding.dcn_msg_get_showpadlock(this.dc_msg))
}
getSortTimestamp(): number {
return binding.dcn_msg_get_sort_timestamp(this.dc_msg)
}
getState() {
return new MessageState(binding.dcn_msg_get_state(this.dc_msg))
}
getSummary(chat?: Chat) {
const dc_chat = (chat && chat.dc_chat) || null
return new Lot(binding.dcn_msg_get_summary(this.dc_msg, dc_chat))
}
get subject(): string {
return binding.dcn_msg_get_subject(this.dc_msg)
}
getSummarytext(approxCharacters: number): string {
approxCharacters = approxCharacters || 0
return binding.dcn_msg_get_summarytext(this.dc_msg, approxCharacters)
}
getText(): string {
return binding.dcn_msg_get_text(this.dc_msg)
}
getTimestamp(): number {
return binding.dcn_msg_get_timestamp(this.dc_msg)
}
getViewType() {
return new MessageViewType(binding.dcn_msg_get_viewtype(this.dc_msg))
}
getVideochatType(): number {
return binding.dcn_msg_get_videochat_type(this.dc_msg)
}
getVideochatUrl(): string {
return binding.dcn_msg_get_videochat_url(this.dc_msg)
}
getWidth(): number {
return binding.dcn_msg_get_width(this.dc_msg)
}
get overrideSenderName(): string {
return binding.dcn_msg_get_override_sender_name(this.dc_msg)
}
hasDeviatingTimestamp() {
return binding.dcn_msg_has_deviating_timestamp(this.dc_msg)
}
hasLocation() {
return Boolean(binding.dcn_msg_has_location(this.dc_msg))
}
get hasHTML() {
return Boolean(binding.dcn_msg_has_html(this.dc_msg))
}
isDeadDrop() {
// TODO: Fix
//return this.getChatId() === C.DC_CHAT_ID_DEADDROP
return false
}
isForwarded() {
return Boolean(binding.dcn_msg_is_forwarded(this.dc_msg))
}
isIncreation() {
return Boolean(binding.dcn_msg_is_increation(this.dc_msg))
}
isInfo() {
return Boolean(binding.dcn_msg_is_info(this.dc_msg))
}
isSent() {
return Boolean(binding.dcn_msg_is_sent(this.dc_msg))
}
isSetupmessage() {
return Boolean(binding.dcn_msg_is_setupmessage(this.dc_msg))
}
latefilingMediasize(width: number, height: number, duration: number) {
binding.dcn_msg_latefiling_mediasize(this.dc_msg, width, height, duration)
}
setDimension(width: number, height: number) {
binding.dcn_msg_set_dimension(this.dc_msg, width, height)
return this
}
setDuration(duration: number) {
binding.dcn_msg_set_duration(this.dc_msg, duration)
return this
}
setFile(file: string, mime?: string) {
if (typeof file !== 'string') throw new Error('Missing filename')
binding.dcn_msg_set_file(this.dc_msg, file, mime || '')
return this
}
setLocation(longitude: number, latitude: number) {
binding.dcn_msg_set_location(this.dc_msg, longitude, latitude)
return this
}
setQuote(quotedMessage: Message | null) {
binding.dcn_msg_set_quote(this.dc_msg, quotedMessage?.dc_msg)
return this
}
setText(text: string) {
binding.dcn_msg_set_text(this.dc_msg, text)
return this
}
setHTML(html: string) {
binding.dcn_msg_set_html(this.dc_msg, html)
return this
}
setOverrideSenderName(senderName: string) {
binding.dcn_msg_set_override_sender_name(this.dc_msg, senderName)
return this
}
/** Force the message to be sent in plain text.
*
* This API is for bots, there is no need to expose it in the UI.
*/
forcePlaintext() {
binding.dcn_msg_force_plaintext(this.dc_msg)
}
}

24
node/lib/types.ts Normal file
View File

@@ -0,0 +1,24 @@
import { C } from './constants'
export type ChatTypes =
| C.DC_CHAT_TYPE_GROUP
| C.DC_CHAT_TYPE_MAILINGLIST
| C.DC_CHAT_TYPE_SINGLE
| C.DC_CHAT_TYPE_UNDEFINED
export interface ChatJSON {
archived: boolean
pinned: boolean
color: string
id: number
name: string
profileImage: string
type: number
isSelfTalk: boolean
isUnpromoted: boolean
isProtected: boolean
canSend: boolean
isDeviceTalk: boolean
isContactRequest: boolean
muted: boolean
}

6
node/lib/util.ts Normal file
View File

@@ -0,0 +1,6 @@
/**
* @param integerColor expects a 24bit rgb integer (left to right: 8bits red, 8bits green, 8bits blue)
*/
export function integerToHexColor(integerColor: number) {
return '#' + (integerColor + 16777216).toString(16).substring(1)
}