mirror of
https://github.com/chatmail/core.git
synced 2026-04-21 15:36:30 +03:00
347 lines
11 KiB
Markdown
347 lines
11 KiB
Markdown
## Core system
|
|
|
|
- [X] Base structure of JSON API code
|
|
- [X] Implement the first methods for testing + the code that should later be generated by the proc macro
|
|
- [X] Create the proc macro
|
|
- [X] json api
|
|
- [X] ts types
|
|
- [X] arguments (no args, one argument, multiple args)
|
|
- [X] return type
|
|
- [X] custom types as type aliases that ts file looks prettier
|
|
|
|
|
|
## Pre - MVP
|
|
|
|
- [X] Web socket server
|
|
- [WIP] Web socket client (ts)
|
|
- [X] backend connection state changed events
|
|
- [X] Reconnect on connection loss / connection state
|
|
- [ ] find a way to type the event emitter callback functions
|
|
- [X] Events
|
|
|
|
## MVP
|
|
|
|
- [X] mocha integration test for ts api
|
|
- [X] basic tests
|
|
- [X] advanced / "online tests" (mailadm for burner accounts)
|
|
- [ ] coverage for a majority of the API
|
|
- [ ] Blobs served
|
|
- [ ] Blob upload (for attachments, setting profile-picture, importing backup and so on)
|
|
- [ ] Web push API? At least some kind of notification hook closure this lib can accept.
|
|
|
|
## Other Ideas
|
|
|
|
- [ ] make sure there can only be one connection at a time to the ws
|
|
- why? , it could give problems if its commanded from multiple connections
|
|
- [ ] encrypted connection?
|
|
- [ ] authenticated connection?
|
|
- [ ] Look into unit-testing for the proc macros?
|
|
- [ ] proc macro taking over doc comments to generated typescript file
|
|
- [X] GH action for tests (rust and typescript)
|
|
- [X] rust test
|
|
- [X] rust fmt
|
|
- [X] rust clippy
|
|
- [X] tsc check
|
|
- [X] prettier
|
|
- [X] mocha
|
|
- [X] scripts to check&fix prettier formatting
|
|
|
|
|
|
|
|
## Apis
|
|
|
|
replicate desktop api feature set:
|
|
|
|
(this feature set is based on desktop version `1.20`, needs to be updated in the future)
|
|
|
|
```rs
|
|
|
|
|
|
struct sendMessageParams {
|
|
text: Option<String>,
|
|
filename: Option<String>, // TODO we need to think about blobs some more
|
|
location: Option<(u32,u32)>,
|
|
quote_message_id: Option<u32>,
|
|
}
|
|
|
|
struct QrCodeResponse = {
|
|
state: u32 // also enum in reality, for simlicity u32 here
|
|
id: u32
|
|
text1: String
|
|
}
|
|
|
|
impl Api {
|
|
|
|
// root ---------------------------------------------------------------
|
|
|
|
// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
|
|
async fn sc_set_profile_picture(&self, new_image: String) -> Result<()> {}
|
|
|
|
// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
|
|
// 'getProfilePicture' equals to `dc.getContact(C.DC_CONTACT_ID_SELF).getProfileImage()` or `dc.get_config("selfavatar")`
|
|
|
|
async fn sc_join_secure_join(&self, qrCode: String) -> Result<u32> {}
|
|
async fn sc_stop_ongoing_process(&self) -> Result<u32> {}
|
|
async fn sc_check_qr_code(&self, qrCode: String) -> Result<QrCodeResponse> {}
|
|
|
|
// login ----------------------------------------------------
|
|
|
|
// INFO: login functions need to call stop&start io where applicable
|
|
|
|
// login.newLogin:
|
|
// do instead in frontend:
|
|
// 1. call `add_account`
|
|
// 2. call `select_account`
|
|
// 3. set credentials via set config
|
|
// 4. call `sc_configure`
|
|
|
|
// login.getLogins - is already implemented: `get_all_accounts`
|
|
|
|
// login.loadAccount - Basically `select_account`
|
|
|
|
// login.logout -> TODO: unselect account, which isn't implemented in the core yet
|
|
|
|
// login.forgetAccount -> `remove_account`
|
|
|
|
// login.getLastLoggedInAccount -> `get_selected_account_id`
|
|
|
|
// login.updateCredentials -> do instead: set config then call `sc_configure`
|
|
|
|
// backup -------------------------------------------------------------
|
|
|
|
// INFO: backup functions need to call stop&start io
|
|
|
|
// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
|
|
async fn sc_backup_export(&self, out_dir: String) -> Result<()> {}
|
|
// NEEDS_THE_BLOB_QUESTION_ANSWERED_EVENTUALLY
|
|
async fn sc_backup_import(&self, file: String) -> Result<()> {} // will not return the same as in desktop because this function imports backup to the current context unlike it was in desktop
|
|
|
|
// chatList -----------------------------------------------------------
|
|
|
|
// chatList.selectChat - will be removed from desktop
|
|
// chatList.getSelectedChatId - will be removed from desktop
|
|
// chatList.onChatModified - will be removed from desktop
|
|
|
|
async fn sc_chatlist_get_general_fresh_message_counter(&self) -> Result<u32> // this method might be used for a favicon badge counter
|
|
|
|
// contacts ------------------------------------------------------------
|
|
|
|
async fn sc_contacts_change_nickname(&self, contact_id: u32, new_name: String) -> Result<()>
|
|
|
|
|
|
// contacts.getChatIdByContactId - very similar to sc_contacts_create_chat_by_contact_id
|
|
// contacts.getDMChatId - very similar to sc_contacts_create_chat_by_contact_id
|
|
|
|
async fn sc_contacts_get_encryption_info(&self, contact_id: u32) -> Result<String>
|
|
|
|
async fn sc_contacts_lookup_contact_id_by_addr(&self, email: String) -> Result<u32>
|
|
|
|
|
|
}
|
|
|
|
```
|
|
```ts
|
|
|
|
class DeltaRemote {
|
|
// chat ---------------------------------------------------------------
|
|
call(
|
|
fnName: 'chat.getChatMedia',
|
|
chatId: number,
|
|
msgType1: number,
|
|
msgType2: number
|
|
): Promise<MessageType[]>
|
|
call(fnName: 'chat.getEncryptionInfo', chatId: number): Promise<string>
|
|
call(fnName: 'chat.getQrCode', chatId?: number): Promise<string>
|
|
call(fnName: 'chat.leaveGroup', chatId: number): Promise<void>
|
|
call(fnName: 'chat.setName', chatId: number, name: string): Promise<boolean>
|
|
call(
|
|
fnName: 'chat.modifyGroup',
|
|
chatId: number,
|
|
name: string,
|
|
image: string,
|
|
remove: number[],
|
|
add: number[]
|
|
): Promise<boolean>
|
|
call(
|
|
fnName: 'chat.addContactToChat',
|
|
chatId: number,
|
|
contactId: number
|
|
): Promise<boolean>
|
|
call(
|
|
fnName: 'chat.setProfileImage',
|
|
chatId: number,
|
|
newImage: string
|
|
): Promise<boolean>
|
|
call(
|
|
fnName: 'chat.setMuteDuration',
|
|
chatId: number,
|
|
duration: MuteDuration
|
|
): Promise<boolean>
|
|
call(
|
|
fnName: 'chat.createGroupChat',
|
|
verified: boolean,
|
|
name: string
|
|
): Promise<number>
|
|
call(fnName: 'chat.delete', chatId: number): Promise<void>
|
|
call(
|
|
fnName: 'chat.setVisibility',
|
|
chatId: number,
|
|
visibility:
|
|
| C.DC_CERTCK_AUTO
|
|
| C.DC_CERTCK_STRICT
|
|
| C.DC_CHAT_VISIBILITY_PINNED
|
|
): Promise<void>
|
|
call(fnName: 'chat.getChatContacts', chatId: number): Promise<number[]>
|
|
call(fnName: 'chat.markNoticedChat', chatId: number): Promise<void>
|
|
call(fnName: 'chat.getChatEphemeralTimer', chatId: number): Promise<number>
|
|
call(
|
|
fnName: 'chat.setChatEphemeralTimer',
|
|
chatId: number,
|
|
ephemeralTimer: number
|
|
): Promise<void>
|
|
call(fnName: 'chat.sendVideoChatInvitation', chatId: number): Promise<number>
|
|
call(
|
|
fnName: 'chat.decideOnContactRequest',
|
|
messageId: number,
|
|
decision:
|
|
| C.DC_DECISION_START_CHAT
|
|
| C.DC_DECISION_NOT_NOW
|
|
| C.DC_DECISION_BLOCK
|
|
): Promise<number>
|
|
// locations ----------------------------------------------------------
|
|
call(
|
|
fnName: 'locations.setLocation',
|
|
latitude: number,
|
|
longitude: number,
|
|
accuracy: number
|
|
): Promise<void>
|
|
call(
|
|
fnName: 'locations.getLocations',
|
|
chatId: number,
|
|
contactId: number,
|
|
timestampFrom: number,
|
|
timestampTo: number
|
|
): Promise<JsonLocations>
|
|
|
|
// NOTHING HERE that is called directly from the frontend, yet
|
|
// messageList --------------------------------------------------------
|
|
call(
|
|
fnName: 'messageList.sendMessage',
|
|
chatId: number,
|
|
params: sendMessageParams
|
|
): Promise<[number, MessageType | null]>
|
|
call(
|
|
fnName: 'messageList.sendSticker',
|
|
chatId: number,
|
|
stickerPath: string
|
|
): Promise<void>
|
|
call(fnName: 'messageList.deleteMessage', id: number): Promise<void>
|
|
call(fnName: 'messageList.getMessageInfo', msgId: number): Promise<string>
|
|
call(
|
|
fnName: 'messageList.getDraft',
|
|
chatId: number
|
|
): Promise<MessageType | null>
|
|
call(
|
|
fnName: 'messageList.setDraft',
|
|
chatId: number,
|
|
{
|
|
text,
|
|
file,
|
|
quotedMessageId,
|
|
}: { text?: string; file?: string; quotedMessageId?: number }
|
|
): Promise<void>
|
|
call(
|
|
fnName: 'messageList.messageIdToJson',
|
|
id: number
|
|
): Promise<{ msg: null } | MessageType>
|
|
call(
|
|
fnName: 'messageList.forwardMessage',
|
|
msgId: number,
|
|
chatId: number
|
|
): Promise<void>
|
|
call(
|
|
fnName: 'messageList.searchMessages',
|
|
query: string,
|
|
chatId?: number
|
|
): Promise<number[]>
|
|
call(
|
|
fnName: 'messageList.msgIds2SearchResultItems',
|
|
msgIds: number[]
|
|
): Promise<{ [id: number]: MessageSearchResult }>
|
|
call(
|
|
fnName: 'messageList.saveMessageHTML2Disk',
|
|
messageId: number
|
|
): Promise<string>
|
|
// settings -----------------------------------------------------------
|
|
call(fnName: 'settings.keysImport', directory: string): Promise<void>
|
|
call(fnName: 'settings.keysExport', directory: string): Promise<void>
|
|
call(
|
|
fnName: 'settings.serverFlags',
|
|
{
|
|
mail_security,
|
|
send_security,
|
|
}: {
|
|
mail_security?: string
|
|
send_security?: string
|
|
}
|
|
): Promise<number | ''>
|
|
call(
|
|
fnName: 'settings.setDesktopSetting',
|
|
key: keyof DesktopSettings,
|
|
value: string | number | boolean
|
|
): Promise<boolean>
|
|
call(fnName: 'settings.getDesktopSettings'): Promise<DesktopSettings>
|
|
call(
|
|
fnName: 'settings.saveBackgroundImage',
|
|
file: string,
|
|
isDefaultPicture: boolean
|
|
): Promise<string>
|
|
call(
|
|
fnName: 'settings.estimateAutodeleteCount',
|
|
fromServer: boolean,
|
|
seconds: number
|
|
): Promise<number>
|
|
// stickers -----------------------------------------------------------
|
|
call(
|
|
fnName: 'stickers.getStickers'
|
|
): Promise<{
|
|
[key: string]: string[]
|
|
}> // todo move to extras? because its not directly elated to core
|
|
// context ------------------------------------------------------------
|
|
call(fnName: 'context.maybeNetwork'): Promise<void>
|
|
// burner accounts ------------------------------------------------------------
|
|
call(
|
|
fnName: 'burnerAccounts.create',
|
|
url: string
|
|
): Promise<{ email: string; password: string }> // think about how to improve that api - probably use core api instead
|
|
// extras -------------------------------------------------------------
|
|
call(fnName: 'extras.getLocaleData', locale: string): Promise<LocaleData>
|
|
call(fnName: 'extras.setLocale', locale: string): Promise<void>
|
|
call(
|
|
fnName: 'extras.getActiveTheme'
|
|
): Promise<{
|
|
theme: Theme
|
|
data: string
|
|
} | null>
|
|
call(fnName: 'extras.setThemeFilePath', address: string): void
|
|
call(fnName: 'extras.getAvailableThemes'): Promise<Theme[]>
|
|
call(fnName: 'extras.setTheme', address: string): Promise<boolean>
|
|
// catchall: ----------------------------------------------------------
|
|
call(fnName: string): Promise<any>
|
|
call(fnName: string, ...args: any[]): Promise<any> {
|
|
return _callDcMethodAsync(fnName, ...args)
|
|
}
|
|
}
|
|
|
|
export const DeltaBackend = new DeltaRemote()
|
|
```
|
|
|
|
|
|
after that, or while doing it adjust api to be more complete
|
|
|
|
|
|
|
|
|
|
TODO different test to simulate two devices:
|
|
|
|
to test autocrypt_initiate_key_transfer & autocrypt_continue_key_transfer |