Skip to content
This repository has been archived by the owner on Jan 17, 2025. It is now read-only.

Commit

Permalink
fix: 0.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
teidesu committed Feb 19, 2024
1 parent 4d0b8f6 commit 45e1061
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 134 deletions.
35 changes: 31 additions & 4 deletions guide/dispatcher/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ a [storage](#storage) to the constructor:
```ts
interface BotState { ... }

const dp = Dispatcher.for<BotState>(tg)
const dp = Dispatcher.for<BotState>(tg, {
storage: new MemoryStateStorage()
})
// or, for children
const dp = Dispatcher.child<BotState>()
```
Expand Down Expand Up @@ -233,12 +235,37 @@ const state = await dp.getState<SomeInternalState>(...)
## Storage

Storage is the backend used by Dispatcher to store state related information.
A storage is a class that implements `IStateStorage`.

By default, the dispatcher will try using client's storage, but you can also provide your own:
A storage is a class that implements [`IStateStorageProvider`](https://ref.mtcute.dev/types/_mtcute_dispatcher.IStateStorageProvider.html).

```ts
const dp = Dispatcher.for<BotState>(tg, { storage: new MemoryStorage() })
// or, locally for a child dispatcher:
const dp = Dispatcher.child<BotState>({ storage: new MemoryStorage() })
```

### SQLite storage

You can re-use your existing SQLite storage for FSM:

```ts
import { SqliteStorage } from '@mtcute/sqlite'
import { SqliteStateStorage } from '@mtcute/dispatcher'

const storage = new SqliteStorage('my-account')
const tg = new TelegramClient({ ..., storage })

const dp = Dispatcher.for<BotState>(tg, {
storage: SqliteStateStorage.from(storage)
})
```

Alternatively, you can create a new SQLite storage specifically for FSM:

```ts
import { SqliteStorageDriver } from '@mtcute/sqlite'
import { SqliteStateStorage } from '@mtcute/dispatcher'

const dp = Dispatcher.for<BotState>(tg, {
storage: new SqliteStateStorage(new SqliteStorageDriver('my-state'))
})
```
13 changes: 9 additions & 4 deletions guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Usage in browsers is a less common use case, so there's no scaffolding tool for
You can still add the library manually, though:

```bash
pnpm add @mtcute/client
pnpm add @mtcute/core
```

> For vite, you'll also need to deoptimize `@mtcute/wasm` (see [vite#8427](https://github.com/vitejs/vite/issues/8427)):
Expand All @@ -93,11 +93,12 @@ pnpm add @mtcute/client
and then use it as you wish:
```ts
import { TelegramClient } from '@mtcute/client'
import { TelegramClient } from '@mtcute/core'
const tg = new TelegramClient({
apiId: 123456,
apiHash: '0123456789abcdef0123456789abcdef',
storage: 'my-account' // will use IndexedDB-based storage
})
tg.call({ _: 'help.getConfig' }).then((res) => console.log(res))
Expand All @@ -108,13 +109,13 @@ See also: [Tree-shaking](/guide/topics/treeshaking.md)
## Other runtimes

### Bun/Deno
These runtimes are not actively supported and tested, but mtcute *seems* to work fine with them.
These runtimes are not actively supported and tested yet, but mtcute *seems* to work fine with them.

For Deno, however, you'll have to manually use the web crypto provider, since the Node
compatibility layer is not good enough yet:

```ts
import { TelegramClient } from 'npm:@mtcute/client'
import { TelegramClient } from 'npm:@mtcute/core'
import { WebCryptoProvider } from 'npm:@mtcute/core/utils/crypto/web.js'

const tg = new TelegramClient({
Expand All @@ -138,6 +139,10 @@ as long as it also supports these featues:

Of course, nothing is stopping you from bundling the library with Webpack or Rollup and using some polyfills.

> **Note on bundling**
> Some of the default exports will currently import Node.js built-in libraries.
>
You'll also likely have to implement custom storage, networking and crypto,
see [Storage](/guide/topics/storage.md) and [Transport](/guide/topics/transport.md) for more info.

Expand Down
3 changes: 1 addition & 2 deletions guide/intro/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ mtcute has a group of its own errors that are used to indicate
that the provided input is invalid, or that the server
returned something weird.

Some errors are thrown by the core library, and some are only
exported by the `@mtcute/client` package, yet still all these errors are subclassed from `MtcuteError`:
All these errors are subclassed from `MtcuteError` and are exported from `@mtcute/core`:

| Name | Description | Package |
|---|---|---|
Expand Down
1 change: 0 additions & 1 deletion guide/intro/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ put under observation to prevent violation of the ToS

In some cases, even logging in with an unofficial client to an
account created using an official one may trigger the system
(I've had that issue multiple times with US VOIP phone numbers)

If you are planning to implement an active userbot, be extra careful,
avoid using VOIP numbers and try to minimize server load generated
Expand Down
4 changes: 2 additions & 2 deletions guide/intro/sign-in.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ tg.run({
`tg.input` is a tiny wrapper over `readline` module in NodeJS,
that will ask you for input in the console.

It's not available in `@mtcute/client`, since it is platform-agnostic
It's not available in `@mtcute/core`, since it is platform-agnostic
:::

`.run()` is a convenient wrapper method over `.start()` that logs you in using
the given parameters and then calls a given function with the current user, and
handles all errors with the client's error handler.

When using ESM, you may want to use `.start()` directly:
When top-level await is available, you can use `.start()` directly instead:

```ts
const self = await tg.start({ ... })
Expand Down
50 changes: 1 addition & 49 deletions guide/intro/updates.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const tg = new TelegramClient({

The parameters themselves will be explained a bit below, for now let's just focus on how they are passed.

### `@mtcute/client`
`TelegramClient` has updates handling turned on by default, and you can configure it using `updates` parameter

```ts
Expand All @@ -37,7 +36,7 @@ const tg = new TelegramClient({
})
```

The updates themselves are dispatched on the client itself as events (see [reference](https://ref.mtcute.dev/classes/_mtcute_client.index.TelegramClient.html#on)):
The updates themselves are dispatched on the client as events (see [reference](https://ref.mtcute.dev/classes/_mtcute_core.index.TelegramClient.html#on)):
```ts
tg.on('new_message', (msg) => {
console.log(msg.text)
Expand Down Expand Up @@ -71,53 +70,6 @@ tg.on('new_message', async (msg) => {
```
:::

### `@mtcute/core`

`BaseTelegramClient` only dispatches raw [TL Updates](https://corefork.telegram.org/type/Updates),
and does not do any additional processing (like recovering gaps, ordering and parsing).

If you want to make it do that, you can use [`enableUpdatesProcessing`](https://ref.mtcute.dev/functions/_mtcute_client.methods_updates.enableUpdatesProcessing.html)
exported by `@mtcute/client`, and manually start updates loop once the client is guaranteed to be logged in:

```ts
import { enableUpdatesProcessing, startUpdatesLoop } from '@mtcute/client/methods/updates/index.js'

const tg = new BaseTelegramClient(...)
enableUpdatesProcessing(tg, {
catchUp: true,
onUpdate: (upd, peers) => console.log(upd, peers)
})

// later
const user = await start(tg, { ... })
await startUpdatesLoop(tg)
```

This will add update processing capabilities to the client, and make it dispatch all updates to the `onUpdate` callback.
However, the above code *does not* enable updates parsing.
This means that `upd` will be a raw [TL Update](https://corefork.telegram.org/type/Update)
(nice naming, I know, don't blame me), which is guaranteed to be ordered properly however.

To also parse the incoming updates, you can use [`makeParsedUpdateHandler`](https://ref.mtcute.dev/functions/_mtcute_client.methods_updates.makeParsedUpdateHandler.html):
```ts
const tg = new BaseTelegramClient(...)
enableUpdatesProcessing(tg, {
catchUp: true,
onUpdate: makeParsedUpdateHandler({
onUpdate: (upd) => console.log(upd),
}),
})
```

This way, `upd` will be one of [`ParsedUpdate` union](https://ref.mtcute.dev/types/_mtcute_client.index.ParsedUpdate.html).

::: tip
This approach makes the updates handling tree-shakeable.

Updates handling logic is fairly complex and takes a lot of space, and the parsed update classes are also quite large,
so you can just not use them and save some bundle size.
:::

### Missed updates

When your client is offline, updates are still stored by Telegram,
Expand Down
2 changes: 1 addition & 1 deletion guide/topics/inline-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const results = await tg.call({
Then, for example, to send the first result:

```ts
import { randomLong } from '@mtcute/client/utils.js'
import { randomLong } from '@mtcute/core/utils.js'

const first = results.results[0]

Expand Down
4 changes: 2 additions & 2 deletions guide/topics/keyboards.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ button's text.
/>

```ts
import { BotKeyboard } from '@mtcute/client'
import { BotKeyboard } from '@mtcute/core'

await tg.sendText('username', 'Awesome keyboard!', {
replyMarkup: BotKeyboard.reply([
Expand Down Expand Up @@ -81,7 +81,7 @@ button instructs it to do.
/>

```ts
import { BotKeyboard } from '@mtcute/client'
import { BotKeyboard } from '@mtcute/core'

await tg.sendText('username', 'Awesome keyboard!', {
replyMarkup: BotKeyboard.inline([
Expand Down
4 changes: 2 additions & 2 deletions guide/topics/peers.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ Official clients silently "migrate" legacy groups to supergroups
(actually channels) whenever the user wants to use a feature not supported
by the Chat, like setting a username. Channel cannot be migrated back to Chat.

### Chat in `@mtcute/client`
### Chat in mtcute

In addition to the mess described above, `@mtcute/client` also has a Chat type.
In addition to the mess described above, mtcute also has a [Chat](https://ref.mtcute.dev/classes/_mtcute_core.index.Chat.html) type.
It is used to represent anything where there can be messages,
including users, legacy groups, supergroups, channels, etc.

Expand Down
4 changes: 2 additions & 2 deletions guide/topics/raw-api.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Raw API

`@mtcute/client` implements a lot of methods to simplify using the
`@mtcute/core` implements a lot of methods to simplify using the
Telegram APIs. However, it does not cover the entirety of the API,
and in that case, you can resort to using the API directly.

Expand Down Expand Up @@ -48,7 +48,7 @@ briefly described below:
| `hash` | Hash of the previously stored content, used to avoid re-fetching the content that is not modified. Methods that use this parameter have `*NotModified` class as one of the possible return types. It is not returned if `hash=0`. | `0`
| `offset` | Offset for pagination | `0`
| `limit` | Maximum number of items for pagination, max. limit is different for every method. | `0`, this will usually default to ~20
| `randomId` | Random message ID to avoid sending the same message. | `randomLong()` (exported by `@mtcute/client/utils.js`)
| `randomId` | Random message ID to avoid sending the same message. | `randomLong()` (exported by `@mtcute/core/utils.js`)

Learn more about pagination in [Telegram docs](https://core.telegram.org/api/offsets)

Expand Down
83 changes: 24 additions & 59 deletions guide/topics/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const tg = new NodeTelegramClient({
To improve performance, `@mtcute/sqlite` by default uses
WAL mode ([Learn more](https://github.com/JoshuaWise/better-sqlite3/blob/master/docs/performance.md)).

When using WAL, along with your SQLite file there will also
When using WAL, along with your SQLite file there may also
be `-shm` and `-wal` files. If you don't like seeing those files,
instead of disabling WAL altogether, consider putting your storage in a folder
(i.e. `new SqliteStorage('storage/my-account')`).
Expand All @@ -74,15 +74,15 @@ The preferred storage for a Web application is the one using IndexedDB,
which is basically a browser's version of SQLite.

```ts{4}
import { IdbStorage } from '@mtcute/core/storage/idb.js'
import { IdbStorage } from '@mtcute/core'
const tg = new BaseTelegramClient({
storage: new IdbStorage('my-account')
})
```

::: tip
If you are using `@mtcute/client`, IndexedDB storage is the default,
In the browser, IndexedDB storage is the default,
and you can simply pass a string with file name instead
of instantiating `IdbStorage` manually:

Expand All @@ -94,47 +94,6 @@ const tg = new TelegramClient({
:::


## JSON-based storage

`MemoryStorage` internally uses a simple JavaScript object
containing all the data that should be persisted.
`JsonMemoryStorage` is a subclass that implements methods
to import and export the current storage to/from JSON string.

::: warning
JSON based storages are **not recommended** because of numerous issues
that are out of library's control, and also because they don't persist
everything, only the most important data.

Instead, prefer SQLite or IndexedDB storage.
:::

### `localStorage` storage

On top of `JsonMemoryStorage`, mtcute implements `LocalstorageStorage`
that persists that JSON string in browser's `localStorage`:

```ts{4}
import { LocalstorageStorage } from '@mcute/core'
const tg = new TelegramClient({
storage: new LocalstorageStorage('mtcute:my-account')
})
```

### JSON file storage

For Node JS, there is an option to use JSON file based storage.
mtcute implements that in `JsonFileStorage` class:

```ts{4}
import { JsonFileStorage } from '@mcute/core'
const tg = new TelegramClient({
storage: new JsonFileStorage('my-account.json')
})
```

## Session strings

Sometimes it might be useful to export storage data to a string, and
Expand All @@ -155,7 +114,7 @@ which can then be imported:
```ts
const tg = new TelegramClient({...})

tg.importSession(SESSION_STRING)
await tg.importSession(SESSION_STRING)
// or
tg.run({ session: SESSION_STRING })
```
Expand All @@ -178,11 +137,6 @@ string from multiple IPs at once, and that would immediately
revoke that session.
:::

::: tip
Calling `importSession` does not immediately import the session.
Instead, it will be imported once the storage is ready.
:::

::: details What is included?
You might be curious about the information that the session
string includes, and why is it so long.
Expand All @@ -196,15 +150,26 @@ is included, which results in an average of **407** characters

## Implementing custom storage

The easiest way to implement a custom storage would be to
make a subclass of `MemoryStorage` or `JsonMemoryStorage`,
The easiest way to implement a custom storage would be to make a subclass of `MemoryStorage`,
or check the [source code of SqliteStorage](https://github.com/mtcute/mtcute/blob/master/packages/sqlite/src/index.ts)
and implement something similar with your DB of choice.

## Storage for Dispatcher

We are getting a bit ahead of ourselves, but it is still important
to mention.

All of the storages provided by `@mtcute/*` packages are also compatible with
Dispatcher's FSM and Scenes storage interface, and can be re-used there.
### Architecture

A storage provider in mtcute is composed of:
- **Driver**: the core of the storage, which handles reading and writing data to the storage and implements
lifecycle methods like `load` and `save`. Driver also manages migrations for the storage, however the migrations
themselves are not part of the driver, but are registered separately by repositories
- **Repository**: a set of methods to read and write data of a specific entity to the storage, allowing for
more efficient and organized access to the data. Repositories are registered in the driver and are used to
access the data in the storage

Such composable architecture allows for custom storages to implement a specific set of repositories,
and to reuse the same driver for different providers.

In mtcute, these sets of repositories are defined:
- [IMtStorageProvider](https://ref.mtcute.dev/types/_mtcute_core.index.IMtStorageProvider.html), used by `BaseTelegramClient` for low-level
MTProto data storage
- [ITelegramStorageProvider](https://ref.mtcute.dev/interfaces/_mtcute_core.index.ITelegramStorageProvider.html), used by `TelegramClient` for basic caching
and update handling operations required for the client to work
- [IStateStorageProvider](https://ref.mtcute.dev/types/_mtcute_dispatcher.IStateStorageProvider.html), used by `Dispatcher` for FSM and Scenes storage
Loading

0 comments on commit 45e1061

Please sign in to comment.