refactor!: merged client into core + fixed dispatcher for new storage

This commit is contained in:
alina 🌸 2024-01-31 19:29:49 +03:00
parent eca99a7535
commit c8e026dc03
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
524 changed files with 6262 additions and 5215 deletions

View file

@ -160,7 +160,7 @@ module.exports = {
'simple-import-sort/imports': [
'error',
{
groups: [['^[a-z]'], ['^@mtcute'], ['^@/'], ['^~/'], ['^\\.']],
groups: [['^[a-z]'], ['^@?mtcute'], ['^@/'], ['^~/'], ['^\\.']],
},
],
'simple-import-sort/exports': 'error',

View file

@ -1,5 +1,5 @@
{
"name": "mtcute",
"name": "mtcute-workspace",
"private": true,
"version": "0.6.0",
"description": "Type-safe library for MTProto (Telegram API) for browser and NodeJS",

View file

@ -1,35 +0,0 @@
# @mtcute/client
📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_client.html)
High-level Telegram client implementation over the `@mtcute/core` base library.
## Features
- **Updates handling**: Implements proper updates handling, including ordering and gap recovery ([learn more](https://core.telegram.org/api/updates))
- **Wrapper classes**: Easy-to-use classes that wrap the complex TL objects and provide a clean interface
- **High-level methods**: Methods that wrap the low-level API calls and provide a clean interface
- **Tree-shaking**: Only import the methods you need, and the rest will not be included into the bundle
- **Web support**: Works in the browser with no additional configuration
## Usage
```ts
import { TelegramClient } from '@mtcute/client'
const tg = new TelegramClient({
apiId: 12345,
apiHash: '0123456789abcdef0123456789abcdef',
// ... + supports all options from @mtcute/core ...
})
tg.start({
phone: '+1234567890',
password: () => prompt('Enter password'),
code: () => prompt('Enter code'),
}, (user) => {
console.log(`Logged in as ${user.displayName}`)
})
```
> **Note**: for web, prefer BaseTelegramClient over TelegramClient,
> as it is tree-shakeable [learn more](https://mtcute.dev/guide/topics/treeshaking.html)

View file

@ -1,3 +0,0 @@
module.exports = {
esmOnlyDirectives: true,
}

View file

@ -1,43 +0,0 @@
{
"name": "@mtcute/client",
"private": true,
"version": "0.6.0",
"description": "High-level API over @mtcute/core",
"author": "Alina Sireneva <alina@tei.su>",
"license": "MIT",
"main": "src/index.ts",
"type": "module",
"scripts": {
"build": "pnpm run -w build-package client",
"gen-client": "node ./scripts/generate-client.cjs",
"gen-updates": "node ./scripts/generate-updates.cjs"
},
"distOnlyFields": {
"exports": {
".": {
"import": "./esm/index.js",
"require": "./cjs/index.js"
},
"./methods/*": {
"import": "./esm/methods/*",
"require": "./cjs/methods/*"
},
"./utils.js": {
"import": "./esm/utils/index.js",
"require": "./cjs/utils/index.js"
}
}
},
"browser": {
"./src/methods/files/_platform.js": "./src/methods/files/_platform.web.js",
"./src/methods/files/download-file.js": "./src/methods/files/download-file.web.js",
"./src/utils/platform/storage.js": "./src/utils/platform/storage.web.js"
},
"dependencies": {
"@mtcute/core": "workspace:^",
"@mtcute/file-id": "workspace:^"
},
"devDependencies": {
"@mtcute/test": "workspace:^"
}
}

View file

@ -1,5 +0,0 @@
export * from './client.js'
export * from './types/index.js'
export * from './utils/peer-utils.js'
export { createDummyUpdate } from './utils/updates-utils.js'
export * from '@mtcute/core'

View file

@ -1,111 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { BaseTelegramClientOptions, IMtStorageProvider } from '@mtcute/core'
// @copy
import { MemoryStorage } from '@mtcute/core/src/storage/providers/memory/index.js'
import { TelegramClient } from '../client.js'
// @copy
import { Conversation } from '../types/conversation.js'
// @copy
import { _defaultStorageFactory } from '../utils/platform/storage.js'
// @copy
import {
enableUpdatesProcessing,
makeParsedUpdateHandler,
ParsedUpdateHandlerParams,
UpdatesManagerParams,
} from './updates/index.js'
// @extension
interface TelegramClientExt {
_disableUpdatesManager: boolean
}
// @copy
interface TelegramClientOptions extends Omit<BaseTelegramClientOptions, 'storage'> {
/**
* Storage to use for this client.
*
* If a string is passed, it will be used as:
* - a path to a JSON file for Node.js
* - IndexedDB database name for browsers
*
* If omitted, {@link MemoryStorage} is used
*/
storage?: string | IMtStorageProvider
/**
* Parameters for updates manager.
*/
updates?: Omit<ParsedUpdateHandlerParams & UpdatesManagerParams, 'onUpdate' | 'onRawUpdate'>
/**
* **ADVANCED**
*
* If set to `true`, updates manager will not be created,
* and only raw TL Updates will be emitted.
*
* Unlike {@link TelegramClientOptions.disableUpdates}, this
* does not prevent the updates from being sent by the server,
* but disables proper handling of them (see [Working with Updates](https://core.telegram.org/api/updates))
*
* This may be useful in some cases when you require more control over
* the updates or to minimize additional overhead from properly handling them
* for some very particular use cases.
*
* The updates **will not** be dispatched the normal way, instead
* you should manually add a handler using `client.network.setUpdateHandler`.
*/
disableUpdatesManager?: boolean
/**
* If `true`, the updates that were handled by some {@link Conversation}
* will not be dispatched any further.
*
* @default true
*/
skipConversationUpdates?: boolean
}
// @initialize=super
/** @internal */
function _initializeClientSuper(this: TelegramClient, opts: TelegramClientOptions) {
if (typeof opts.storage === 'string') {
opts.storage = _defaultStorageFactory(opts.storage)
} else if (!opts.storage) {
opts.storage = new MemoryStorage()
}
/* eslint-disable @typescript-eslint/no-unsafe-call */
// @ts-expect-error codegen
super(opts)
/* eslint-enable @typescript-eslint/no-unsafe-call */
}
// @initialize
/** @internal */
function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) {
this._disableUpdatesManager = opts.disableUpdatesManager ?? false
const skipConversationUpdates = opts.skipConversationUpdates ?? true
if (!opts.disableUpdates && !opts.disableUpdatesManager) {
const { messageGroupingInterval, ...managerParams } = opts.updates ?? {}
enableUpdatesProcessing(this, {
...managerParams,
onUpdate: makeParsedUpdateHandler({
messageGroupingInterval,
onUpdate: (update) => {
if (Conversation.handleUpdate(this, update) && skipConversationUpdates) return
this.emit('update', update)
this.emit(update.name, update.data)
},
onRawUpdate: (update, peers) => {
this.emit('raw_update', update, peers)
},
}),
})
}
}

View file

@ -1,52 +0,0 @@
/* eslint-disable no-inner-declarations */
import { BaseTelegramClient, MtUnsupportedError, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { User } from '../../types/peers/user.js'
/** @internal */
export async function _onAuthorization(
client: BaseTelegramClient,
auth: tl.auth.TypeAuthorization,
): Promise<User> {
if (auth._ === 'auth.authorizationSignUpRequired') {
throw new MtUnsupportedError(
'Signup is no longer supported by Telegram for non-official clients. Please use your mobile device to sign up.',
)
}
assertTypeIs('_onAuthorization (@ auth.authorization -> user)', auth.user, 'user')
// todo: selfUsername
await client.notifyLoggedIn(auth)
// telegram ignores invokeWithoutUpdates for auth methods
if (client.network.params.disableUpdates) client.network.resetSessions()
return new User(auth.user)
}
/**
* Check if the given peer/input peer is referring to the current user
*/
export function isSelfPeer(
client: BaseTelegramClient,
peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser,
): boolean {
const state = client.storage.self.getCached()
if (!state) return false
switch (peer._) {
case 'inputPeerSelf':
case 'inputUserSelf':
return true
case 'inputPeerUser':
case 'inputPeerUserFromMessage':
case 'inputUser':
case 'inputUserFromMessage':
case 'peerUser':
return peer.userId === state.userId
default:
return false
}
}

View file

@ -1,8 +0,0 @@
import { BaseTelegramClient, getMarkedPeerId, tl } from '@mtcute/core'
/** @internal */
export function _getPeerChainId(client: BaseTelegramClient, peer: tl.TypeInputPeer, prefix = 'peer') {
const id = peer._ === 'inputPeerSelf' ? client.storage.self.getCached()!.userId : getMarkedPeerId(peer)
return `${prefix}:${id}`
}

File diff suppressed because it is too large Load diff

View file

@ -1,42 +0,0 @@
import { tl } from '@mtcute/core'
export function messageToUpdate(message: tl.TypeMessage): tl.TypeUpdate {
switch (message.peerId!._) {
case 'peerUser':
case 'peerChat':
return {
_: 'updateNewMessage',
message,
pts: 0,
ptsCount: 0,
}
case 'peerChannel':
return {
_: 'updateNewChannelMessage',
message,
pts: 0,
ptsCount: 0,
}
}
}
export function extractChannelIdFromUpdate(upd: tl.TypeUpdate): number | undefined {
// holy shit
let res = 0
if ('channelId' in upd) {
res = upd.channelId
} else if (
'message' in upd &&
typeof upd.message !== 'string' &&
'peerId' in upd.message &&
upd.message.peerId &&
'channelId' in upd.message.peerId
) {
res = upd.message.peerId.channelId
}
if (res === 0) return undefined
return res
}

View file

@ -1,5 +0,0 @@
/** @internal */
export const _defaultStorageFactory = (name: string) => {
// todo: move sqlite to core?
throw new Error('Not implemented')
}

View file

@ -1,49 +0,0 @@
import { MtTypeAssertionError, tl } from '@mtcute/core'
// dummy updates which are used for methods that return messages.affectedHistory.
// that is not an update, but it carries info about pts, and we need to handle it
/**
* Create a dummy `updates` container with given updates.
*/
export function createDummyUpdatesContainer(updates: tl.TypeUpdate[], seq = 0): tl.TypeUpdates {
return {
_: 'updates',
seq,
date: 0,
chats: [],
users: [],
updates,
}
}
/**
* Create a dummy update from PTS and PTS count.
*
* @param pts PTS
* @param ptsCount PTS count
* @param channelId Channel ID (bare), if applicable
*/
export function createDummyUpdate(pts: number, ptsCount: number, channelId = 0): tl.TypeUpdates {
return createDummyUpdatesContainer([
{
_: 'mtcute.dummyUpdate',
channelId,
pts,
ptsCount,
},
])
}
/** @internal */
export function assertIsUpdatesGroup(
ctx: string,
upd: tl.TypeUpdates,
): asserts upd is tl.RawUpdates | tl.RawUpdatesCombined {
switch (upd._) {
case 'updates':
case 'updatesCombined':
return
}
throw new MtTypeAssertionError(ctx, 'updates | updatesCombined', upd._)
}

View file

@ -1,14 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist/esm",
"rootDir": "./src"
},
"include": [
"./src",
],
"references": [
{ "path": "../core" },
{ "path": "../test" }
]
}

View file

@ -1,9 +0,0 @@
module.exports = {
extends: ['../../.config/typedoc/config.base.cjs'],
entryPoints: [
'./src/index.ts',
'./src/utils/index.ts',
'./src/methods/updates/index.ts',
],
entryPointStrategy: 'expand',
}

View file

@ -1,4 +0,0 @@
// this file only exists as a hint to IDEs that we can use @mtcute/core/utils instead of @mtcute/core/src/utils.
// it is not present in the built package, just a DX improvement
export * from './src/utils/index.js'

View file

@ -1,5 +1,6 @@
module.exports = ({ path, transformFile, packageDir, outDir }) => ({
esmOnlyDirectives: true,
esmImportDirectives: true,
final() {
const version = require(path.join(packageDir, 'package.json')).version
const replaceVersion = (content) => content.replace('%VERSION%', version)

View file

@ -2,13 +2,15 @@
"name": "@mtcute/core",
"private": true,
"version": "0.6.0",
"description": "Core functions and base MTProto client",
"description": "Type-safe library for MTProto (Telegram API)",
"author": "Alina Sireneva <alina@tei.su>",
"license": "MIT",
"main": "src/index.ts",
"type": "module",
"scripts": {
"build": "pnpm run -w build-package core"
"build": "pnpm run -w build-package mtcute",
"gen-client": "node ./scripts/generate-client.cjs",
"gen-updates": "node ./scripts/generate-updates.cjs"
},
"browser": {
"./src/utils/platform/crypto.js": "./src/utils/platform/crypto.web.js",
@ -16,6 +18,11 @@
"./src/utils/platform/logging.js": "./src/utils/platform/logging.web.js",
"./src/utils/platform/random.js": "./src/utils/platform/random.web.js",
"./src/utils/platform/exit-hook.js": "./src/utils/platform/exit-hook.web.js",
"./src/highlevel/worker/platform/connect.js": "./src/highlevel/worker/platform/connect.web.js",
"./src/highlevel/worker/platform/register.js": "./src/highlevel/worker/platform/register.web.js",
"./src/highlevel/methods/files/_platform.js": "./src/highlevel/methods/files/_platform.web.js",
"./src/highlevel/methods/files/download-file.js": "./src/highlevel/methods/files/download-file.web.js",
"./src/highlevel/utils/platform/storage.js": "./src/highlevel/utils/platform/storage.web.js",
"./src/storage/json-file.js": false
},
"distOnlyFields": {
@ -39,6 +46,14 @@
"./storage/*": {
"import": "./esm/storage/*",
"require": "./cjs/storage/*"
},
"./highlevel/*": {
"import": "./esm/highlevel/*",
"require": "./cjs/highlevel/*"
},
"./methods/*": {
"import": "./esm/highlevel/methods/*",
"require": "./cjs/highlevel/methods/*"
}
}
},
@ -46,13 +61,13 @@
"@mtcute/tl": "workspace:^",
"@mtcute/tl-runtime": "workspace:^",
"@mtcute/wasm": "workspace:^",
"@mtcute/file-id": "workspace:^",
"@types/events": "3.0.0",
"events": "3.2.0",
"long": "5.2.3"
},
"devDependencies": {
"@types/ws": "8.5.4",
"node-forge": "1.3.1",
"@mtcute/test": "workspace:^",
"ws": "8.13.0"
}

View file

@ -13,7 +13,7 @@ function findMethodAvailability(method) {
return entry.available ?? null
}
const targetDir = path.join(__dirname, '../src')
const targetDir = path.join(__dirname, '../src/highlevel')
async function* getFiles(dir) {
const dirents = await fs.promises.readdir(dir, { withFileTypes: true })
@ -315,7 +315,7 @@ async function addSingleMethod(state, fileName) {
const firstArg = stmt.parameters[0]
if (isExported && (!firstArg || firstArg.type.getText() !== 'BaseTelegramClient')) {
if (isExported && (!firstArg || firstArg.type.getText() !== 'ITelegramClient')) {
continue
}
@ -421,7 +421,8 @@ async function addSingleMethod(state, fileName) {
}
async function main() {
const output = fs.createWriteStream(path.join(__dirname, '../src/client.ts'))
const targetFile = path.join(__dirname, '../src/highlevel/client.ts')
const output = fs.createWriteStream(targetFile)
const state = {
imports: {},
fields: [],
@ -435,7 +436,7 @@ async function main() {
files: {},
}
for await (const file of getFiles(path.join(__dirname, '../src/methods'))) {
for await (const file of getFiles(path.join(__dirname, '../src/highlevel/methods'))) {
if (!file.startsWith('.') && file.endsWith('.ts') && !file.endsWith('.web.ts') && !file.endsWith('.test.ts')) {
await addSingleMethod(state, file)
}
@ -444,7 +445,9 @@ async function main() {
output.write(
'/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging, @typescript-eslint/unified-signatures */\n' +
'/* eslint-disable @typescript-eslint/no-unsafe-argument */\n' +
'/* THIS FILE WAS AUTO-GENERATED */\n',
'/* THIS FILE WAS AUTO-GENERATED */\n' +
"import EventEmitter from 'events'\n" +
"import Long from 'long'\n",
)
Object.entries(state.imports).forEach(([module, items]) => {
items = [...items]
@ -458,7 +461,7 @@ async function main() {
output.write(`// from ${from}\n${code}\n`)
})
output.write('\nexport interface TelegramClient extends BaseTelegramClient {\n')
output.write('\nexport interface TelegramClient extends ITelegramClient {\n')
output.write(`/**
* Register a raw update handler
@ -547,7 +550,7 @@ on(name: string, handler: (...args: any[]) => void): this\n`)
`<${func.typeParameters.map((it) => it.getFullText()).join(', ')}>` :
''
const rawParams = (func.parameters || []).filter(
(it) => !it.type || it.type.getText() !== 'BaseTelegramClient',
(it) => !it.type || it.type.getText() !== 'ITelegramClient',
)
const parameters = rawParams
.map((it) => {
@ -648,8 +651,8 @@ on(name: string, handler: (...args: any[]) => void): this\n`)
if (hasOverloads) {
classProtoDecls.push('// @ts-expect-error this kinda breaks typings for overloads, idc')
}
classProtoDecls.push(` return ${origName}(this, ...args);`)
classProtoDecls.push('}\n')
classProtoDecls.push(` return ${origName}(this._client, ...args);`)
classProtoDecls.push('}')
}
}
},
@ -657,11 +660,13 @@ on(name: string, handler: (...args: any[]) => void): this\n`)
output.write('}\n')
output.write('\nexport type { TelegramClientOptions }\n')
output.write('\nexport class TelegramClient extends BaseTelegramClient {\n')
output.write('\nexport class TelegramClient extends EventEmitter implements ITelegramClient {\n')
output.write(' _client: ITelegramClient\n')
state.fields.forEach(({ code }) => output.write(`protected ${code}\n`))
output.write('constructor(opts: TelegramClientOptions) {\n')
output.write(' super()\n')
state.init.forEach((code) => {
output.write(code + '\n')
})
@ -670,10 +675,45 @@ on(name: string, handler: (...args: any[]) => void): this\n`)
classContents.forEach((line) => output.write(line + '\n'))
output.write('}\n')
classProtoDecls.forEach((line) => output.write(line + '\n'))
// proxied methods
;[
'prepare',
'connect',
'close',
'notifyLoggedIn',
'notifyLoggedOut',
'notifyChannelOpened',
'notifyChannelClosed',
'call',
'importSession',
'exportSession',
'onError',
'emitError',
'handleClientUpdate',
'getApiCrenetials',
'getPoolSize',
'getPrimaryDcId',
'computeSrpParams',
'computeNewPasswordHash',
].forEach((name) => {
output.write(
`TelegramClient.prototype.${name} = function(...args) {\n` +
` return this._client.${name}(...args)\n` +
'}\n',
)
})
// disabled methods - they are used internally and we don't want to expose them
// if the user *really* needs them, they can use `client._client` to access the underlying client
;['onServerUpdate', 'onUpdate'].forEach((name) => {
output.write(
`TelegramClient.prototype.${name} = function() {\n` +
` throw new Error('${name} is not available for TelegramClient, use .on() methods instead')\n` +
'}\n',
)
})
state.impls.forEach(({ name, code }) => output.write(`TelegramClient.prototype.${name} = ${code}\n`))
// format the resulting file with prettier
const targetFile = path.join(__dirname, '../src/client.ts')
const prettierConfig = await prettier.resolveConfig(targetFile)
let fullSource = await fs.promises.readFile(targetFile, 'utf-8')
fullSource = await prettier.format(fullSource, {

View file

@ -1,4 +1,3 @@
/* eslint-disable no-restricted-globals */
const fs = require('fs')
const path = require('path')
const prettier = require('prettier')
@ -90,7 +89,7 @@ function toSentence(type, stype = 'inline') {
}
function generateParsedUpdate() {
replaceSections('types/updates/index.ts', {
replaceSections('highlevel/types/updates/index.ts', {
codegen:
'export type ParsedUpdate =\n' +
types.map((typ) => ` | { name: '${typ.typeName}'; data: ${typ.updateType} }\n`).join(''),

View file

@ -1,354 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import EventEmitter from 'events'
import { tl } from '@mtcute/tl'
import { __tlReaderMap as defaultReaderMap } from '@mtcute/tl/binary/reader.js'
import { __tlWriterMap as defaultWriterMap } from '@mtcute/tl/binary/writer.js'
import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
import { BaseTelegramClientOptions } from './base-client.types.js'
import { ConfigManager } from './network/config-manager.js'
import { SessionConnection } from './network/index.js'
import { NetworkManager, RpcCallOptions } from './network/network-manager.js'
import { StorageManager } from './storage/storage.js'
import { MustEqual } from './types/index.js'
import {
ControllablePromise,
createControllablePromise,
DcOptions,
defaultCryptoProviderFactory,
defaultProductionDc,
defaultProductionIpv6Dc,
defaultTestDc,
defaultTestIpv6Dc,
ICryptoProvider,
LogManager,
readStringSession,
StringSessionData,
writeStringSession,
} from './utils/index.js'
/**
* Basic Telegram client that only implements the bare minimum
* to make RPC calls and receive low-level updates.
*/
export class BaseTelegramClient extends EventEmitter {
/**
* Crypto provider taken from {@link BaseTelegramClientOptions.crypto}
*/
readonly crypto: ICryptoProvider
/** Storage manager */
readonly storage: StorageManager
/**
* "Test mode" taken from {@link BaseTelegramClientOptions.testMode}
*/
protected readonly _testMode: boolean
/**
* Primary DCs taken from {@link BaseTelegramClientOptions.defaultDcs},
* loaded from session or changed by other means (like redirecting).
*/
protected _defaultDcs: DcOptions
private _niceStacks: boolean
/** TL layer used by the client */
readonly _layer: number
/** TL readers map used by the client */
readonly _readerMap: TlReaderMap
/** TL writers map used by the client */
readonly _writerMap: TlWriterMap
readonly _config = new ConfigManager(() => this.call({ _: 'help.getConfig' }))
// not really connected, but rather "connect() was called"
private _connected: ControllablePromise<void> | boolean = false
_emitError: (err: unknown, connection?: SessionConnection) => void = console.error.bind(console)
private _importFrom?: StringSessionData
private _importForce?: boolean
readonly log = new LogManager('client')
readonly network: NetworkManager
constructor(readonly params: BaseTelegramClientOptions) {
super()
if (params.logLevel !== undefined) {
this.log.level = params.logLevel
}
this.crypto = (params.crypto ?? defaultCryptoProviderFactory)()
this._testMode = Boolean(params.testMode)
let dc = params.defaultDcs
if (!dc) {
if (params.testMode) {
dc = params.useIpv6 ? defaultTestIpv6Dc : defaultTestDc
} else {
dc = params.useIpv6 ? defaultProductionIpv6Dc : defaultProductionDc
}
}
this._defaultDcs = dc
this._niceStacks = params.niceStacks ?? true
this._layer = params.overrideLayer ?? tl.LAYER
this._readerMap = params.readerMap ?? defaultReaderMap
this._writerMap = params.writerMap ?? defaultWriterMap
this.storage = new StorageManager({
provider: params.storage,
log: this.log,
readerMap: this._readerMap,
writerMap: this._writerMap,
...params.storageOptions,
})
this.network = new NetworkManager(
{
apiId: params.apiId,
crypto: this.crypto,
disableUpdates: params.disableUpdates ?? false,
initConnectionOptions: params.initConnectionOptions,
layer: this._layer,
log: this.log,
readerMap: this._readerMap,
writerMap: this._writerMap,
reconnectionStrategy: params.reconnectionStrategy,
storage: this.storage,
testMode: Boolean(params.testMode),
transport: params.transport,
_emitError: this._emitError.bind(this),
floodSleepThreshold: params.floodSleepThreshold ?? 10000,
maxRetryCount: params.maxRetryCount ?? 5,
isPremium: false,
useIpv6: Boolean(params.useIpv6),
enableErrorReporting: params.enableErrorReporting ?? false,
onUsable: () => this.emit('usable'),
...params.network,
},
this._config,
)
}
/**
* Initialize the connection to the primary DC.
*
* You shouldn't usually call this method directly as it is called
* implicitly the first time you call {@link call}.
*/
async connect(): Promise<void> {
if (this._connected) {
// avoid double-connect
await this._connected
return
}
const promise = (this._connected = createControllablePromise())
await this.crypto.initialize?.()
await this.storage.load()
const primaryDc = await this.storage.dcs.fetch()
if (primaryDc !== null) this._defaultDcs = primaryDc
const self = await this.storage.self.fetch()
this.log.prefix = `[USER ${self?.userId ?? 'n/a'}] `
const defaultDcAuthKey = await this.storage.provider.authKeys.get(this._defaultDcs.main.id)
if ((this._importForce || !defaultDcAuthKey) && this._importFrom) {
const data = this._importFrom
if (data.testMode !== this._testMode) {
throw new Error(
'This session string is not for the current backend. ' +
`Session is ${data.testMode ? 'test' : 'prod'}, but the client is ${
this._testMode ? 'test' : 'prod'
}`,
)
}
this._defaultDcs = data.primaryDcs
await this.storage.dcs.store(data.primaryDcs)
if (data.self) {
await this.storage.self.store(data.self)
}
await this.storage.provider.authKeys.set(data.primaryDcs.main.id, data.authKey)
await this.storage.save()
}
this.emit('before_connect')
this.network
.connect(this._defaultDcs)
.then(() => {
promise.resolve()
this._connected = true
})
.catch((err: Error) => this._emitError(err))
}
/**
* Close all connections and finalize the client.
*/
async close(): Promise<void> {
this.emit('before_close')
this._config.destroy()
this.network.destroy()
await this.storage.save()
await this.storage.destroy?.()
this.emit('closed')
}
/**
* Make an RPC call to the primary DC.
* This method handles DC migration, flood waits and retries automatically.
*
* If you want more low-level control, use
* `primaryConnection.sendForResult()` (which is what this method wraps)
*
* This method is still quite low-level and you shouldn't use this
* when using high-level API provided by `@mtcute/client`.
*
* @param message RPC method to call
* @param params Additional call parameters
*/
async call<T extends tl.RpcMethod>(
message: MustEqual<T, tl.RpcMethod>,
params?: RpcCallOptions,
): Promise<tl.RpcCallReturn[T['_']]> {
if (this._connected !== true) {
await this.connect()
}
const stack = this._niceStacks ? new Error().stack : undefined
const res = await this.network.call(message, params, stack)
await this.storage.peers.updatePeersFrom(res)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return res
}
/**
* Create a Proxy that will call all methods with given call parameters
* (see {@link RpcCallOptions}})
*
* This is useful when you don't call `call()` directly, but rather
* use high-level API provided by `@mtcute/client`, for example:
*
* ```ts
* const client = new TelegramClient(...)
*
* const someone = await client
* .withCallParams({ timeout: 500 })
* .getUsers(...)
* ```
*/
withCallParams(params: RpcCallOptions): this {
return new Proxy(this, {
get(target, prop, receiver) {
if (prop === 'call') {
return (message: tl.RpcMethod, paramsCustom?: RpcCallOptions) =>
target.call(message, {
...params,
...paramsCustom,
})
}
return Reflect.get(target, prop, receiver)
},
})
}
/**
* Shorthand for `withCallParams({ abortSignal })`
*/
withAbortSignal(signal: AbortSignal): this {
return this.withCallParams({ abortSignal: signal })
}
/**
* Register an error handler for the client
*
* @param handler
* Error handler. Called with one or two parameters.
* The first one is always the error, and the second is
* the connection in which the error has occurred, in case
* this was connection-related error.
*/
onError(handler: (err: unknown, connection?: SessionConnection) => void): void {
this._emitError = handler
}
async notifyLoggedIn(auth: tl.auth.RawAuthorization): Promise<void> {
this.network.notifyLoggedIn(auth)
this.log.prefix = `[USER ${auth.user.id}] `
await this.storage.self.store({
userId: auth.user.id,
isBot: auth.user._ === 'user' && auth.user.bot!,
})
this.emit('logged_in', auth)
}
/**
* Export current session to a single *LONG* string, containing
* all the needed information.
*
* > **Warning!** Anyone with this string will be able
* > to authorize as you and do anything. Treat this
* > as your password, and never give it away!
* >
* > In case you have accidentally leaked this string,
* > make sure to revoke this session in account settings:
* > "Privacy & Security" > "Active sessions" >
* > find the one containing `mtcute` > Revoke,
* > or, in case this is a bot, revoke bot token
* > with [@BotFather](//t.me/botfather)
*/
async exportSession(): Promise<string> {
const primaryDcs = (await this.storage.dcs.fetch()) ?? this._defaultDcs
const authKey = await this.storage.provider.authKeys.get(primaryDcs.main.id)
if (!authKey) throw new Error('Auth key is not ready yet')
return writeStringSession(this._writerMap, {
version: 2,
self: await this.storage.self.fetch(),
testMode: this._testMode,
primaryDcs,
authKey,
})
}
/**
* Request the session to be imported from the given session string.
*
* Note that the session will not be imported right away,
* instead, it will be imported once `connect()` is called
*
* Also note that the session will only be imported in case
* the storage is missing authorization (i.e. does not contain
* auth key for the primary DC), otherwise it will be ignored (unless `force`).
*
* @param session Session string to import
* @param force Whether to overwrite existing session
*/
importSession(session: string | StringSessionData, force = false): void {
this._importFrom = typeof session === 'string' ? readStringSession(this._readerMap, session) : session
this._importForce = force
}
}

View file

@ -1,177 +0,0 @@
import { tl } from '@mtcute/tl'
import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
import { NetworkManagerExtraParams, ReconnectionStrategy, TransportFactory } from './network/index.js'
import { PersistentConnectionParams } from './network/persistent-connection.js'
import { IMtStorageProvider } from './storage/provider.js'
import { StorageManagerExtraOptions } from './storage/storage.js'
import { CryptoProviderFactory, DcOptions } from './utils/index.js'
/** Options for {@link BaseTelegramClient} */
export interface BaseTelegramClientOptions {
/**
* API ID from my.telegram.org
*/
apiId: number
/**
* API hash from my.telegram.org
*/
apiHash: string
/**
* Storage to use for this client.
*/
storage: IMtStorageProvider
/** Additional options for the storage manager */
storageOptions?: StorageManagerExtraOptions
/**
* Cryptography provider factory to allow delegating
* crypto to native addon, worker, etc.
*/
crypto?: CryptoProviderFactory
/**
* Whether to use IPv6 datacenters
* (IPv6 will be preferred when choosing a DC by id)
* (default: false)
*/
useIpv6?: boolean
/**
* Primary DC to use for initial connection.
* This does not mean this will be the only DC used,
* nor that this DC will actually be primary, this only
* determines the first DC the library will try to connect to.
* Can be used to connect to other networks (like test DCs).
*
* When session already contains primary DC, this parameter is ignored.
*
* @default Production DC 2.
*/
defaultDcs?: DcOptions
/**
* Whether to connect to test servers.
*
* If passed, {@link defaultDc} defaults to Test DC 2.
*
* **Must** be passed if using test servers, even if
* you passed custom {@link defaultDc}
*/
testMode?: boolean
/**
* Additional options for initConnection call.
* `apiId` and `query` are not available and will be ignored.
* Omitted values will be filled with defaults
*/
initConnectionOptions?: Partial<Omit<tl.RawInitConnectionRequest, 'apiId' | 'query'>>
/**
* Transport factory to use in the client.
*
* @default platform-specific transport: WebSocket on the web, TCP in node
*/
transport?: TransportFactory
/**
* Reconnection strategy.
*
* @default simple reconnection strategy: first 0ms, then up to 5s (increasing by 1s)
*/
reconnectionStrategy?: ReconnectionStrategy<PersistentConnectionParams>
/**
* Maximum duration of a flood_wait that will be waited automatically.
* Flood waits above this threshold will throw a FloodWaitError.
* Set to 0 to disable. Can be overridden with `throwFlood` parameter in call() params
*
* @default 10000
*/
floodSleepThreshold?: number
/**
* Maximum number of retries when calling RPC methods.
* Call is retried when InternalError or FloodWaitError is encountered.
* Can be set to Infinity.
*
* @default 5
*/
maxRetryCount?: number
/**
* If true, every single API call will be wrapped with `tl.invokeWithoutUpdates`,
* effectively disabling the server-sent events for the clients.
* May be useful in some cases.
*
* Note that this only wraps calls made with `.call()` within the primary
* connection. Additional connections and direct `.sendForResult()` calls
* must be wrapped manually.
*
* @default false
*/
disableUpdates?: boolean
/**
* mtcute can send all unknown RPC errors to [danog](https://github.com/danog)'s
* [error reporting service](https://rpc.pwrtelegram.xyz/).
*
* This is fully anonymous (except maybe IP) and is only used to improve the library
* and developer experience for everyone working with MTProto. This is fully opt-in,
* and if you're too paranoid, you can disable it by manually passing `enableErrorReporting: false` to the client.
*
* @default false
*/
enableErrorReporting?: boolean
/**
* If true, RPC errors will have a stack trace of the initial `.call()`
* or `.sendForResult()` call position, which drastically improves
* debugging experience.<br>
* If false, they will have a stack trace of mtcute internals.
*
* Internally this creates a stack capture before every RPC call
* and stores it until the result is received. This might
* use a lot more memory than normal, thus can be disabled here.
*
* @default true
*/
niceStacks?: boolean
/**
* Extra parameters for {@link NetworkManager}
*/
network?: NetworkManagerExtraParams
/**
* Set logging level for the client.
*
* See static members of {@link LogManager} for possible values.
*/
logLevel?: number
/**
* **EXPERT USE ONLY!**
*
* Override TL layer used for the connection.
*
* **Does not** change the schema used.
*/
overrideLayer?: number
/**
* **EXPERT USE ONLY**
*
* Override reader map used for the connection.
*/
readerMap?: TlReaderMap
/**
* **EXPERT USE ONLY**
*
* Override writer map used for the connection.
*/
writerMap?: TlWriterMap
}

View file

@ -0,0 +1,287 @@
import { tl } from '@mtcute/tl'
import { MtClient, MtClientOptions } from '../network/client.js'
import { ConnectionKind, RpcCallOptions } from '../network/network-manager.js'
import { StorageManagerExtraOptions } from '../storage/storage.js'
import { MtArgumentError } from '../types/errors.js'
import { MustEqual } from '../types/utils.js'
import { asyncResettable, computeNewPasswordHash, computeSrpParams, readStringSession, StringSessionData, writeStringSession } from '../utils/index.js'
import { LogManager } from '../utils/logger.js'
import { ITelegramClient } from './client.types.js'
import { ITelegramStorageProvider } from './storage/provider.js'
import { TelegramStorageManager, TelegramStorageManagerExtraOptions } from './storage/storage.js'
import { UpdatesManager } from './updates/manager.js'
import { RawUpdateHandler, UpdatesManagerParams } from './updates/types.js'
export interface BaseTelegramClientOptions extends MtClientOptions {
storage: ITelegramStorageProvider
storageOptions?: StorageManagerExtraOptions & TelegramStorageManagerExtraOptions
updates?: UpdatesManagerParams | false
}
export class BaseTelegramClient implements ITelegramClient {
readonly updates?: UpdatesManager
private _serverUpdatesHandler: (updates: tl.TypeUpdates) => void = () => {}
constructor(readonly params: BaseTelegramClientOptions) {
if (!params.disableUpdates && params.updates !== false) {
this.updates = new UpdatesManager(this, params.updates)
this._serverUpdatesHandler = this.updates.handleUpdate.bind(this.updates)
}
this.mt.on('update', (update) => {
this._serverUpdatesHandler(update)
})
}
readonly log = this.params.logger ?? new LogManager('client')
readonly mt = new MtClient({
...this.params,
logger: this.log.create('mtproto'),
})
readonly crypto = this.mt.crypto
readonly storage = new TelegramStorageManager(this.mt.storage, {
provider: this.params.storage,
...this.params.storageOptions,
})
private _prepare = asyncResettable(async () => {
await this.mt.prepare()
const self = await this.storage.self.fetch()
this.log.prefix = `[USER ${self?.userId ?? 'n/a'}] `
this.mt.network.setIsPremium(self?.isPremium ?? false)
await this.updates?.prepare()
})
/**
* **ADVANCED**
*
* Do all the preparations, but don't connect just yet.
* Useful when you want to do some preparations before
* connecting, like setting up session.
*
* Call {@link connect} to actually connect.
*/
prepare() {
return this._prepare.run()
}
// used in a hot path, avoid extra function calls
private _connected = false
private _connect = asyncResettable(async () => {
await this._prepare.run()
await this.mt.connect()
this._connected = true
})
/**
* Initialize the connection to the primary DC.
*
* You shouldn't usually call this method directly as it is called
* implicitly the first time you call {@link call}.
*/
async connect(): Promise<void> {
return this._connect.run()
}
get isConnected(): boolean {
return this._connected
}
async close(): Promise<void> {
await this.mt.close()
this.updates?.stopLoop()
this._prepare.reset()
this._connect.reset()
this._connected = false
}
async notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise<tl.RawUser> {
const user = this.mt.network.notifyLoggedIn(auth)
this.log.prefix = `[USER ${user.id}] `
const self = await this.storage.self.storeFrom(user)
this.updates?.notifyLoggedIn(self)
return user
}
async notifyLoggedOut(): Promise<void> {
this.mt.network.notifyLoggedOut()
this.log.prefix = '[USER n/a] '
await this.storage.self.store(null)
}
async notifyChannelOpened(channelId: number, pts?: number): Promise<boolean> {
return this.updates?.notifyChannelOpened(channelId, pts) ?? false
}
async notifyChannelClosed(channelId: number): Promise<boolean> {
return this.updates?.notifyChannelClosed(channelId) ?? false
}
/**
* Make an RPC call
*
* This method is still quite low-level and you shouldn't use this
* when using high-level API provided by `@mtcute/client`.
*
* @param message RPC method to call
* @param params Additional call parameters
*/
async call<T extends tl.RpcMethod>(
message: MustEqual<T, tl.RpcMethod>,
params?: RpcCallOptions,
): Promise<tl.RpcCallReturn[T['_']]> {
if (!this._connected) {
await this._connect.run()
}
const res = await this.mt.call(message, params)
await this.storage.peers.updatePeersFrom(res)
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return res
}
/**
* Import the session from the given session string.
*
* Note that the session will only be imported in case
* the storage is missing authorization (i.e. does not contain
* auth key for the primary DC), otherwise it will be ignored (unless `force`).
*
* @param session Session string to import
* @param force Whether to overwrite existing session
*/
async importSession(session: string | StringSessionData, force = false): Promise<void> {
await this.prepare()
const defaultDcAuthKey = await this.mt.storage.provider.authKeys.get(this.mt._defaultDcs.main.id)
if (defaultDcAuthKey && !force) return
const data = typeof session === 'string' ? readStringSession(this.mt._readerMap, session) : session
if (data.testMode && !this.params.testMode) {
throw new Error(
'This session string is not for the current backend. ' +
`Session is ${data.testMode ? 'test' : 'prod'}, ` +
`but the client is ${this.params.testMode ? 'test' : 'prod'}`,
)
}
this.mt._defaultDcs = data.primaryDcs
await this.mt.storage.dcs.store(data.primaryDcs)
if (data.self) {
await this.storage.self.store(data.self)
}
await this.mt.storage.provider.authKeys.set(data.primaryDcs.main.id, data.authKey)
await this.mt.storage.save()
}
/**
* Export current session to a single *LONG* string, containing
* all the needed information.
*
* > **Warning!** Anyone with this string will be able
* > to authorize as you and do anything. Treat this
* > as your password, and never give it away!
* >
* > In case you have accidentally leaked this string,
* > make sure to revoke this session in account settings:
* > "Privacy & Security" > "Active sessions" >
* > find the one containing `mtcute` > Revoke,
* > or, in case this is a bot, revoke bot token
* > with [@BotFather](//t.me/botfather)
*/
async exportSession(): Promise<string> {
await this._prepare.run()
const primaryDcs = (await this.mt.storage.dcs.fetch()) ?? this.mt._defaultDcs
const authKey = await this.mt.storage.provider.authKeys.get(primaryDcs.main.id)
if (!authKey) throw new Error('Auth key is not ready yet')
return writeStringSession(this.mt._writerMap, {
version: 2,
self: await this.storage.self.fetch(),
testMode: Boolean(this.params.testMode),
primaryDcs,
authKey,
})
}
/**
* Register an error handler for the client
*
* @param handler Error handler.
*/
onError(handler: (err: unknown) => void): void {
this.mt.onError(handler)
}
emitError(err: unknown): void {
this.mt.emitError(err)
}
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void {
this.updates?.handleClientUpdate(updates, noDispatch)
}
onServerUpdate(handler: (update: tl.TypeUpdates) => void): void {
this._serverUpdatesHandler = handler
}
onUpdate(handler: RawUpdateHandler): void {
if (!this.updates) {
throw new MtArgumentError('Updates manager is disabled')
}
this.updates.setHandler(handler)
}
async getApiCrenetials() {
return {
id: this.params.apiId,
hash: this.params.apiHash,
}
}
async getPoolSize(kind: ConnectionKind, dcId?: number): Promise<number> {
if (!this._connected) {
await this._connect.run()
}
return this.mt.network.getPoolSize(kind, dcId)
}
async getPrimaryDcId(): Promise<number> {
if (!this._connected) {
await this._connect.run()
}
return this.mt.network.getPrimaryDcId()
}
computeSrpParams(
request: tl.account.RawPassword,
password: string,
): Promise<tl.RawInputCheckPasswordSRP> {
return computeSrpParams(this.crypto, request, password)
}
computeNewPasswordHash(
algo: tl.TypePasswordKdfAlgo,
password: string,
): Promise<Uint8Array> {
return computeNewPasswordHash(this.crypto, algo, password)
}
}

View file

@ -0,0 +1,53 @@
import { tl } from '@mtcute/tl'
import type { ConnectionKind, RpcCallOptions } from '../network/index.js'
import type { MustEqual, PublicPart } from '../types/utils.js'
import type { Logger } from '../utils/logger.js'
import type { StringSessionData } from '../utils/string-session.js'
import type { TelegramStorageManager } from './storage/storage.js'
import type { RawUpdateHandler } from './updates/types.js'
// NB: when adding new methods, don't forget to add them to:
// - worker/port.ts
// - generate-client script
export interface ITelegramClient {
readonly log: Logger
readonly storage: PublicPart<TelegramStorageManager>
prepare(): Promise<void>
connect(): Promise<void>
close(): Promise<void>
notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): Promise<tl.RawUser>
notifyLoggedOut(): Promise<void>
notifyChannelOpened(channelId: number, pts?: number): Promise<boolean>
notifyChannelClosed(channelId: number): Promise<boolean>
call<T extends tl.RpcMethod>(
message: MustEqual<T, tl.RpcMethod>,
params?: RpcCallOptions,
): Promise<tl.RpcCallReturn[T['_']]>
importSession(session: string | StringSessionData, force?: boolean): Promise<void>
exportSession(): Promise<string>
onError(handler: (err: unknown) => void): void
emitError(err: unknown): void
handleClientUpdate(updates: tl.TypeUpdates, noDispatch?: boolean): void
onServerUpdate(handler: (update: tl.TypeUpdates) => void): void
onUpdate(handler: RawUpdateHandler): void
getApiCrenetials(): Promise<{ id: number; hash: string }>
// todo - this is only used for file dl/ul, which should probably be moved
// to the client to allow moving the thing to worker
// or at least load this once at startup (and then these methods can be made sync)
getPoolSize(kind: ConnectionKind, dcId?: number): Promise<number>
getPrimaryDcId(): Promise<number>
computeSrpParams(
request: tl.account.RawPassword,
password: string,
): Promise<tl.RawInputCheckPasswordSRP>
computeNewPasswordHash(
algo: tl.TypePasswordKdfAlgo,
password: string,
): Promise<Uint8Array>
}

View file

@ -0,0 +1,6 @@
export * from './base.js'
export * from './client.js'
export * from './client.types.js'
export * from './storage/index.js'
export * from './types/index.js'
export * from './updates/index.js'

View file

@ -57,7 +57,7 @@ Example:
```typescript
// @initialize
function _initializeAwesomeExtension(client: BaseTelegramClient) {
function _initializeAwesomeExtension(client: ITelegramClient) {
this._field1 = 42
this._field2 = 'uwu'
}
@ -74,7 +74,7 @@ Example:
// @exported
export type FooOrBar = Foo | Bar
export function getFooOrBar(client: BaseTelegramClient): FooOrBar {
export function getFooOrBar(client: ITelegramClient): FooOrBar {
return new Foo()
}
```

View file

@ -1,19 +1,18 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// @copy
import {
BaseTelegramClient,
BaseTelegramClientOptions,
IMtStorageProvider,
Long,
MaybeArray,
MaybeAsync,
PartialExcept,
PartialOnly,
tl,
} from '@mtcute/core'
// @copy
import { tdFileId } from '@mtcute/file-id'
// @copy
import { tl } from '@mtcute/tl'
// @copy
import { MaybeArray, MaybeAsync, PartialExcept, PartialOnly } from '../../types/index.js'
// @copy
import { StringSessionData } from '../../utils/string-session.js'
// @copy
import { BaseTelegramClient, BaseTelegramClientOptions } from '../base.js'
// @copy
import { ITelegramClient } from '../client.types.js'
// @copy
import {
AllStories,

View file

@ -0,0 +1,97 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// @copy
import { MemoryStorage } from '../../storage/providers/memory/index.js'
import { BaseTelegramClient, BaseTelegramClientOptions } from '../base.js'
import { TelegramClient } from '../client.js'
import { ITelegramClient } from '../client.types.js'
// @copy
import { ITelegramStorageProvider } from '../storage/provider.js'
// @copy
import { Conversation } from '../types/conversation.js'
// @copy
import { makeParsedUpdateHandler, ParsedUpdateHandlerParams } from '../updates/parsed.js'
// @copy
import { _defaultStorageFactory } from '../utils/platform/storage.js'
// @copy
type TelegramClientOptions = ((Omit<BaseTelegramClientOptions, 'storage'> & {
/**
* Storage to use for this client.
*
* If a string is passed, it will be used as:
* - a path to a JSON file for Node.js
* - IndexedDB database name for browsers
*
* If omitted, {@link MemoryStorage} is used
*/
storage?: string | ITelegramStorageProvider
}) | ({ client: ITelegramClient })) & {
updates?: Omit<ParsedUpdateHandlerParams, 'onUpdate'>
/**
* If `true`, the updates that were handled by some {@link Conversation}
* will not be dispatched any further.
*
* @default true
*/
skipConversationUpdates?: boolean
}
// // @initialize=super
// /** @internal */
// function _initializeClientSuper(this: TelegramClient, opts: TelegramClientOptions) {
// if (typeof opts.storage === 'string') {
// opts.storage = _defaultStorageFactory(opts.storage)
// } else if (!opts.storage) {
// opts.storage = new MemoryStorage()
// }
// /* eslint-disable @typescript-eslint/no-unsafe-call */
// // @ts-expect-error codegen
// super(opts)
// /* eslint-enable @typescript-eslint/no-unsafe-call */
// }
// @initialize
/** @internal */
function _initializeClient(this: TelegramClient, opts: TelegramClientOptions) {
if ('client' in opts) {
this._client = opts.client
} else {
let storage: ITelegramStorageProvider
if (typeof opts.storage === 'string') {
storage = _defaultStorageFactory(opts.storage)
} else if (!opts.storage) {
storage = new MemoryStorage()
} else {
storage = opts.storage
}
this._client = new BaseTelegramClient({
...opts,
storage,
})
}
// @ts-expect-error codegen
this.log = this._client.log
// @ts-expect-error codegen
this.storage = this._client.storage
const skipConversationUpdates = opts.skipConversationUpdates ?? true
const { messageGroupingInterval } = opts.updates ?? {}
this._client.onUpdate(makeParsedUpdateHandler({
messageGroupingInterval,
onUpdate: (update) => {
if (Conversation.handleUpdate(this._client, update) && skipConversationUpdates) return
this.emit('update', update)
this.emit(update.name, update.data)
},
onRawUpdate: (update, peers) => {
this.emit('raw_update', update, peers)
},
}))
}

View file

@ -1,8 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { computeSrpParams } from '@mtcute/core/utils.js'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/index.js'
import { _onAuthorization } from './_state.js'
import { _onAuthorization } from './utils.js'
/**
* Check your Two-Step verification password and log in
@ -11,11 +9,10 @@ import { _onAuthorization } from './_state.js'
* @returns The authorized user
* @throws BadRequestError In case the password is invalid
*/
export async function checkPassword(client: BaseTelegramClient, password: string): Promise<User> {
export async function checkPassword(client: ITelegramClient, password: string): Promise<User> {
const res = await client.call({
_: 'auth.checkPassword',
password: await computeSrpParams(
client.crypto,
password: await client.computeSrpParams(
await client.call({
_: 'account.getPassword',
}),

View file

@ -1,11 +1,11 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
/**
* Get your Two-Step Verification password hint.
*
* @returns The password hint as a string, if any
*/
export function getPasswordHint(client: BaseTelegramClient): Promise<string | null> {
export function getPasswordHint(client: ITelegramClient): Promise<string | null> {
return client
.call({
_: 'account.getPassword',

View file

@ -1,4 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
/**
* Log out from Telegram account and optionally reset the session storage.
@ -8,15 +8,9 @@ import { BaseTelegramClient } from '@mtcute/core'
*
* @returns On success, `true` is returned
*/
export async function logOut(client: BaseTelegramClient): Promise<true> {
export async function logOut(client: ITelegramClient): Promise<true> {
await client.call({ _: 'auth.logOut' })
await client.storage.self.store(null)
// authState.selfUsername = null todo
client.emit('logged_out')
await client.storage.clear()
await client.notifyLoggedOut()
return true
}

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/index.js'
import { _onAuthorization } from './_state.js'
import { _onAuthorization } from './utils.js'
/**
* Recover your password with a recovery code and log in.
@ -10,7 +9,7 @@ import { _onAuthorization } from './_state.js'
* @throws BadRequestError In case the code is invalid
*/
export async function recoverPassword(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** The recovery code sent via email */
recoveryCode: string

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { SentCode } from '../../types/index.js'
import { assertTypeIs } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { SentCode } from '../../types/auth/sent-code.js'
import { normalizePhoneNumber } from '../../utils/misc-utils.js'
/**
@ -11,7 +10,7 @@ import { normalizePhoneNumber } from '../../utils/misc-utils.js'
* {@link SentCode} object returned by {@link sendCode}
*/
export async function resendCode(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Phone number in international format */
phone: string

View file

@ -1,6 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { TelegramClient } from '../../index.js'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/index.js'
import { start } from './start.js'
@ -14,22 +12,13 @@ import { start } from './start.js'
*
* @param params Parameters to be passed to {@link start}
* @param then Function to be called after {@link start} returns
* @manual=noemit
*/
export function run(
client: BaseTelegramClient,
client: ITelegramClient,
params: Parameters<typeof start>[1],
then?: (user: User) => void | Promise<void>,
): void {
start(client, params)
.then(then)
.catch((err) => client._emitError(err))
}
// @manual-impl=run
/** @internal */
function _run(this: TelegramClient, params: Parameters<typeof start>[1], then?: (user: User) => void | Promise<void>) {
this.start(params)
.then(then)
.catch((err) => this._emitError(err))
.catch((err) => client.emitError(err))
}

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { SentCode } from '../../types/index.js'
import { assertTypeIs } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { SentCode } from '../../types/auth/sent-code.js'
import { normalizePhoneNumber } from '../../utils/misc-utils.js'
/**
@ -10,7 +9,7 @@ import { normalizePhoneNumber } from '../../utils/misc-utils.js'
* @returns An object containing information about the sent confirmation code
*/
export async function sendCode(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Phone number in international format */
phone: string
@ -18,11 +17,13 @@ export async function sendCode(
): Promise<SentCode> {
const phone = normalizePhoneNumber(params.phone)
const { id, hash } = await client.getApiCrenetials()
const res = await client.call({
_: 'auth.sendCode',
phoneNumber: phone,
apiId: client.params.apiId,
apiHash: client.params.apiHash,
apiId: id,
apiHash: hash,
settings: { _: 'codeSettings' },
})

View file

@ -1,11 +1,11 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
/**
* Send a code to email needed to recover your password
*
* @returns String containing email pattern to which the recovery code was sent
*/
export function sendRecoveryCode(client: BaseTelegramClient): Promise<string> {
export function sendRecoveryCode(client: ITelegramClient): Promise<string> {
return client
.call({
_: 'auth.requestPasswordRecovery',

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { User } from '../../types/index.js'
import { _onAuthorization } from './_state.js'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/peers/user.js'
import { _onAuthorization } from './utils.js'
/**
* Authorize a bot using its token issued by [@BotFather](//t.me/BotFather)
@ -10,12 +9,14 @@ import { _onAuthorization } from './_state.js'
* @returns Bot's {@link User} object
* @throws BadRequestError In case the bot token is invalid
*/
export async function signInBot(client: BaseTelegramClient, token: string): Promise<User> {
export async function signInBot(client: ITelegramClient, token: string): Promise<User> {
const { id, hash } = await client.getApiCrenetials()
const res = await client.call({
_: 'auth.importBotAuthorization',
flags: 0,
apiId: client.params.apiId,
apiHash: client.params.apiHash,
apiId: id,
apiHash: hash,
botAuthToken: token,
})

View file

@ -1,8 +1,7 @@
import { BaseTelegramClient } from '@mtcute/core'
import { User } from '../../types/index.js'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/peers/user.js'
import { normalizePhoneNumber } from '../../utils/misc-utils.js'
import { _onAuthorization } from './_state.js'
import { _onAuthorization } from './utils.js'
/**
* Authorize a user in Telegram with a valid confirmation code.
@ -12,7 +11,7 @@ import { _onAuthorization } from './_state.js'
* @throws SessionPasswordNeededError In case a password is needed to sign in
*/
export async function signIn(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Phone number in international format */
phone: string

View file

@ -1,5 +1,5 @@
import { BaseTelegramClient, MtArgumentError } from '@mtcute/core'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/index.js'
import { logOut } from './log-out.js'
import { start } from './start.js'
@ -15,7 +15,7 @@ import { start } from './start.js'
* @param params Additional parameters
*/
export async function startTest(
client: BaseTelegramClient,
client: ITelegramClient,
params?: {
/**
* Whether to log out if current session is logged in.
@ -63,7 +63,7 @@ export async function startTest(
throw new MtArgumentError(`${phone} has invalid DC ID (${id})`)
}
} else {
let dcId = client.network.getPrimaryDcId()
let dcId = await client.getPrimaryDcId()
if (params.dcId) {
if (!availableDcs.find((dc) => dc.id === params!.dcId)) {

View file

@ -1,8 +1,13 @@
/* eslint-disable no-console */
import { BaseTelegramClient, MaybeAsync, MtArgumentError, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import type { TelegramClient } from '../../client.js'
import { MaybeDynamic, SentCode, User } from '../../types/index.js'
import { MtArgumentError } from '../../../types/errors.js'
import { MaybeAsync } from '../../../types/utils.js'
import { StringSessionData } from '../../../utils/string-session.js'
import { ITelegramClient } from '../../client.types.js'
import { SentCode } from '../../types/auth/sent-code.js'
import { User } from '../../types/peers/user.js'
import { MaybeDynamic } from '../../types/utils.js'
import { normalizePhoneNumber, resolveMaybeDynamic } from '../../utils/misc-utils.js'
import { getMe } from '../users/get-me.js'
import { checkPassword } from './check-password.js'
@ -11,7 +16,6 @@ import { sendCode } from './send-code.js'
import { signIn } from './sign-in.js'
import { signInBot } from './sign-in-bot.js'
// @manual
// @available=both
/**
* Start the client in an interactive and declarative manner,
@ -27,7 +31,7 @@ import { signInBot } from './sign-in-bot.js'
* you'll probably need to use other auth methods.
*/
export async function start(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* String session exported using {@link TelegramClient.exportSession}.
@ -37,7 +41,7 @@ export async function start(
* Note that passed session will be ignored in case storage already
* contains authorization.
*/
session?: string
session?: string | StringSessionData
/**
* Whether to overwrite existing session.
@ -93,7 +97,7 @@ export async function start(
},
): Promise<User> {
if (params.session) {
client.importSession(params.session, params.sessionForce)
await client.importSession(params.session, params.sessionForce)
}
try {
@ -103,7 +107,7 @@ export async function start(
client.log.info('Logged in as %s (ID: %s, username: %s, bot: %s)', me.displayName, me.id, me.username, me.isBot)
client.network.setIsPremium(me.isPremium)
await client.notifyLoggedIn(me.raw)
return me
} catch (e) {
@ -211,15 +215,3 @@ export async function start(
throw new MtArgumentError('Failed to log in with provided credentials')
}
// @manual-impl=start
/** @internal */
async function _start(this: TelegramClient, params: Parameters<typeof start>[1]) {
const user = await start(this, params)
if (!this.network.params.disableUpdates && !this._disableUpdatesManager) {
await this.startUpdatesLoop()
}
return user
}

View file

@ -0,0 +1,39 @@
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { User } from '../../types/peers/user.js'
/** @internal */
export async function _onAuthorization(
client: ITelegramClient,
auth: tl.auth.TypeAuthorization,
): Promise<User> {
const user = await client.notifyLoggedIn(auth)
return new User(user)
}
/**
* Check if the given peer/input peer is referring to the current user
*/
export function isSelfPeer(
client: ITelegramClient,
peer: tl.TypeInputPeer | tl.TypePeer | tl.TypeInputUser,
): boolean {
const state = client.storage.self.getCached()
if (!state) return false
switch (peer._) {
case 'inputPeerSelf':
case 'inputUserSelf':
return true
case 'inputPeerUser':
case 'inputPeerUserFromMessage':
case 'inputUser':
case 'inputUserFromMessage':
case 'peerUser':
return peer.userId === state.userId
default:
return false
}
}

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, Long } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import Long from 'long'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { CallbackQuery } from '../../types/updates/callback-query.js'
/**
@ -10,7 +11,7 @@ import { CallbackQuery } from '../../types/updates/callback-query.js'
* @param params Parameters of the answer
*/
export async function answerCallbackQuery(
client: BaseTelegramClient,
client: ITelegramClient,
queryId: Long | CallbackQuery,
params?: {
/**

View file

@ -1,5 +1,8 @@
import { BaseTelegramClient, Long, tl } from '@mtcute/core'
import Long from 'long'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { BotInline, InputInlineResult } from '../../types/bots/index.js'
import { InlineQuery } from '../../types/updates/inline-query.js'
@ -11,7 +14,7 @@ import { InlineQuery } from '../../types/updates/inline-query.js'
* @param params Additional parameters
*/
export async function answerInlineQuery(
client: BaseTelegramClient,
client: ITelegramClient,
queryId: tl.Long | InlineQuery,
results: InputInlineResult[],
params?: {

View file

@ -1,6 +1,9 @@
import { BaseTelegramClient, Long, tl } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import Long from 'long'
import { tl } from '@mtcute/tl'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import type { PreCheckoutQuery } from '../../types/updates/pre-checkout-query.js'
/**
@ -9,7 +12,7 @@ import type { PreCheckoutQuery } from '../../types/updates/pre-checkout-query.js
* @param queryId Pre-checkout query ID
*/
export async function answerPreCheckoutQuery(
client: BaseTelegramClient,
client: ITelegramClient,
queryId: tl.Long | PreCheckoutQuery,
params?: {
/** If pre-checkout is rejected, error message to show to the user */

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { BotCommands } from '../../types/index.js'
import { _normalizeCommandScope } from './normalize-command-scope.js'
@ -11,7 +12,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js'
* Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope)
*/
export async function deleteMyCommands(
client: BaseTelegramClient,
client: ITelegramClient,
params?: {
/**
* Scope of the commands.

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -8,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Gets information about a bot the current uzer owns (or the current bot)
*/
export async function getBotInfo(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* When called by a user, a bot the user owns must be specified.

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -7,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
/**
* Fetches the menu button set for the given user.
*/
export async function getBotMenuButton(client: BaseTelegramClient, user: InputPeerLike): Promise<tl.TypeBotMenuButton> {
export async function getBotMenuButton(client: ITelegramClient, user: InputPeerLike): Promise<tl.TypeBotMenuButton> {
return await client.call({
_: 'bots.getBotMenuButton',
userId: toInputUser(await resolvePeer(client, user), user),

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { computeSrpParams, utf8EncodeToBuffer } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { utf8EncodeToBuffer } from '@mtcute/tl-runtime'
import { ITelegramClient } from '../../client.types.js'
import { InputMessageId, normalizeInputMessageId } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -11,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param params
*/
export async function getCallbackAnswer(
client: BaseTelegramClient,
client: ITelegramClient,
params: InputMessageId & {
/** Data contained in the button */
data: string | Uint8Array
@ -44,7 +45,7 @@ export async function getCallbackAnswer(
if (params?.password) {
const pwd = await client.call({ _: 'account.getPassword' })
password = await computeSrpParams(client.crypto, pwd, params.password)
password = await client.computeSrpParams(pwd, params.password)
}
return await client.call(

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { GameHighScore, InputMessageId, InputPeerLike, normalizeInputMessageId, PeersIndex } from '../../types/index.js'
import { normalizeInlineId } from '../../utils/inline-utils.js'
import { toInputUser } from '../../utils/peer-utils.js'
@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Get high scores of a game
*/
export async function getGameHighScores(
client: BaseTelegramClient,
client: ITelegramClient,
params: InputMessageId & {
/** ID of the user to find high scores for */
userId?: InputPeerLike
@ -47,7 +48,7 @@ export async function getGameHighScores(
* @param userId ID of the user to find high scores for
*/
export async function getInlineGameHighScores(
client: BaseTelegramClient,
client: ITelegramClient,
messageId: string | tl.TypeInputBotInlineMessageID,
userId?: InputPeerLike,
): Promise<GameHighScore[]> {

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { BotCommands } from '../../types/index.js'
import { _normalizeCommandScope } from './normalize-command-scope.js'
@ -10,7 +11,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js'
* Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope)
*/
export async function getMyCommands(
client: BaseTelegramClient,
client: ITelegramClient,
params?: {
/**
* Scope of the commands.

View file

@ -1,12 +1,14 @@
import { assertNever, BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { assertNever } from '../../../types/utils.js'
import { ITelegramClient } from '../../client.types.js'
import { BotCommands } from '../../types/index.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/** @internal */
export async function _normalizeCommandScope(
client: BaseTelegramClient,
client: ITelegramClient,
scope: tl.TypeBotCommandScope | BotCommands.IntermediateScope,
): Promise<tl.TypeBotCommandScope> {
if (tl.isAnyBotCommandScope(scope)) return scope

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Sets information about a bot the current uzer owns (or the current bot)
*/
export async function setBotInfo(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* When called by a user, a bot the user owns must be specified.

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Sets a menu button for the given user.
*/
export async function setBotMenuButton(
client: BaseTelegramClient,
client: ITelegramClient,
user: InputPeerLike,
button: tl.TypeBotMenuButton,
): Promise<void> {

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputMessageId, InputPeerLike, Message, normalizeInputMessageId } from '../../types/index.js'
import { normalizeInlineId } from '../../utils/inline-utils.js'
import { toInputUser } from '../../utils/peer-utils.js'
@ -14,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @returns The modified message
*/
export async function setGameScore(
client: BaseTelegramClient,
client: ITelegramClient,
params: InputMessageId & {
/** ID of the user who has scored */
userId: InputPeerLike
@ -67,7 +68,7 @@ export async function setGameScore(
* @param params
*/
export async function setInlineGameScore(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** ID of the inline message */
messageId: string | tl.TypeInputBotInlineMessageID

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { BotCommands } from '../../types/index.js'
import { _normalizeCommandScope } from './normalize-command-scope.js'
@ -10,7 +11,7 @@ import { _normalizeCommandScope } from './normalize-command-scope.js'
* Learn more about scopes in the [Bot API docs](https://core.telegram.org/bots/api#botcommandscope)
*/
export async function setMyCommands(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* New list of bot commands for the given scope.

View file

@ -1,11 +1,13 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
/**
* Sets the default chat permissions for the bot in the supergroup or channel.
*/
export async function setMyDefaultRights(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Whether to target groups or channels. */
target: 'channel' | 'group'

View file

@ -1,5 +1,5 @@
import { BaseTelegramClient, MaybeArray } from '@mtcute/core'
import { MaybeArray } from '../../../types/utils.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel, toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -12,7 +12,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js'
* @param users ID(s) of the user(s) to add
*/
export async function addChatMembers(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
users: MaybeArray<InputPeerLike>,
params: {
@ -41,7 +41,7 @@ export async function addChatMembers(
userId: p,
fwdLimit: forwardCount,
})
client.network.handleUpdate(updates)
client.handleClientUpdate(updates)
}
} else if (isInputPeerChannel(chat)) {
const updates = await client.call({
@ -49,7 +49,6 @@ export async function addChatMembers(
channel: toInputChannel(chat),
users: await resolvePeerMany(client, users, toInputUser),
})
client.network.handleUpdate(updates)
client.handleClientUpdate(updates)
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')
}

View file

@ -1,5 +1,5 @@
import { BaseTelegramClient, MaybeArray } from '@mtcute/core'
import { MaybeArray } from '../../../types/utils.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeerMany } from '../users/resolve-peer-many.js'
@ -8,7 +8,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js'
*
* @param chats Chat ID(s), username(s), phone number(s), `"me"` or `"self"`
*/
export async function archiveChats(client: BaseTelegramClient, chats: MaybeArray<InputPeerLike>): Promise<void> {
export async function archiveChats(client: ITelegramClient, chats: MaybeArray<InputPeerLike>): Promise<void> {
if (!Array.isArray(chats)) chats = [chats]
const resolvedPeers = await resolvePeerMany(client, chats)
@ -21,5 +21,5 @@ export async function archiveChats(client: BaseTelegramClient, chats: MaybeArray
folderId: 1,
})),
})
client.network.handleUpdate(updates)
client.handleClientUpdate(updates)
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, Message, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel, toInputUser } from '../../utils/peer-utils.js'
import { _findMessageInUpdate } from '../messages/find-in-update.js'
@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @returns Service message about removed user, if one was generated.
*/
export async function banChatMember(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID */
chatId: InputPeerLike

View file

@ -1,5 +1,7 @@
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import {
isInputPeerChannel,
isInputPeerChat,
@ -129,7 +131,7 @@ export const _getChannelsBatched = batchedQuery<tl.TypeInputChannel, tl.RawChann
/** @internal */
export function _getRawPeerBatched(
client: BaseTelegramClient,
client: ITelegramClient,
peer: tl.TypeInputPeer,
): Promise<tl.TypeUser | tl.TypeChat | null> {
if (isInputPeerUser(peer)) {

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { Chat } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
/**
* Create a new broadcast channel
@ -9,7 +8,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
* @returns Newly created channel
*/
export async function createChannel(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* Channel title
@ -33,7 +32,7 @@ export async function createChannel(
assertIsUpdatesGroup('channels.createChannel', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}

View file

@ -1,8 +1,8 @@
import { BaseTelegramClient, MaybeArray } from '@mtcute/core'
import { MaybeArray } from '../../../types/utils.js'
import { ITelegramClient } from '../../client.types.js'
import { Chat, InputPeerLike } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
import { toInputUser } from '../../utils/peer-utils.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { resolvePeerMany } from '../users/resolve-peer-many.js'
/**
@ -12,7 +12,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js'
* instead.
*/
export async function createGroup(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* Group title
@ -48,7 +48,7 @@ export async function createGroup(
assertIsUpdatesGroup('messages.createChat', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}

View file

@ -1,7 +1,6 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { Chat } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
/**
* Create a new supergroup
@ -9,7 +8,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
* @returns Newly created supergroup
*/
export async function createSupergroup(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* Supergroup title
@ -47,7 +46,7 @@ export async function createSupergroup(
assertIsUpdatesGroup('channels.createChannel', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -10,10 +9,10 @@ import { resolvePeer } from '../users/resolve-peer.js'
*
* @param chatId Chat ID or username
*/
export async function deleteChannel(client: BaseTelegramClient, chatId: InputPeerLike): Promise<void> {
export async function deleteChannel(client: ITelegramClient, chatId: InputPeerLike): Promise<void> {
const res = await client.call({
_: 'channels.deleteChannel',
channel: toInputChannel(await resolvePeer(client, chatId), chatId),
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -11,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
*
* @param chatId Chat ID or username
*/
export async function deleteChatPhoto(client: BaseTelegramClient, chatId: InputPeerLike): Promise<void> {
export async function deleteChatPhoto(client: ITelegramClient, chatId: InputPeerLike): Promise<void> {
const chat = await resolvePeer(client, chatId)
let res
@ -29,5 +28,5 @@ export async function deleteChatPhoto(client: BaseTelegramClient, chatId: InputP
})
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChat } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
*
* @param chatId Chat ID
*/
export async function deleteGroup(client: BaseTelegramClient, chatId: InputPeerLike): Promise<void> {
export async function deleteGroup(client: ITelegramClient, chatId: InputPeerLike): Promise<void> {
const chat = await resolvePeer(client, chatId)
if (!isInputPeerChat(chat)) throw new MtInvalidPeerTypeError(chatId, 'chat')
@ -20,7 +19,7 @@ export async function deleteGroup(client: BaseTelegramClient, chatId: InputPeerL
chatId: chat.chatId,
userId: { _: 'inputUserSelf' },
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
const r = await client.call({
_: 'messages.deleteChat',

View file

@ -1,15 +1,14 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { createDummyUpdate } from '../../updates/utils.js'
import { isInputPeerChannel } from '../../utils/peer-utils.js'
import { createDummyUpdate } from '../../utils/updates-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
* Delete communication history (for private chats and legacy groups)
*/
export async function deleteHistory(
client: BaseTelegramClient,
client: ITelegramClient,
chat: InputPeerLike,
params?: {
/**
@ -45,8 +44,8 @@ export async function deleteHistory(
})
if (isInputPeerChannel(peer)) {
client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId))
client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, peer.channelId))
} else {
client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount))
client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount))
}
}

View file

@ -1,15 +1,16 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { createDummyUpdate } from '../../updates/utils.js'
import { toInputChannel } from '../../utils/peer-utils.js'
import { createDummyUpdate } from '../../utils/updates-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
* Delete all messages of a user (or channel) in a supergroup
*/
export async function deleteUserHistory(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID */
chatId: InputPeerLike
@ -29,5 +30,5 @@ export async function deleteUserHistory(
participant: peer,
})
client.network.handleUpdate(createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId))
client.handleClientUpdate(createDummyUpdate(res.pts, res.ptsCount, (channel as tl.RawInputChannel).channelId))
}

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputChannel, toInputUser } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -8,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Edit supergroup/channel admin rights of a user.
*/
export async function editAdminRights(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID */
chatId: InputPeerLike
@ -36,5 +37,5 @@ export async function editAdminRights(
rank,
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,8 @@
import { BaseTelegramClient, Long, tl } from '@mtcute/core'
import Long from 'long'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { ChatEvent, InputPeerLike, PeersIndex } from '../../types/index.js'
import { InputChatEventFilters, normalizeChatEventFilters } from '../../types/peers/chat-event/filters.js'
import { toInputChannel, toInputUser } from '../../utils/peer-utils.js'
@ -20,7 +23,7 @@ import { resolvePeerMany } from '../users/resolve-peer-many.js'
* @param params
*/
export async function getChatEventLog(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
params?: {
/**

View file

@ -1,6 +1,7 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { assertTypeIs } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { ChatMember, InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, isInputPeerUser, toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -13,7 +14,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @returns Chat member, or `null` if user is not a member of the chat
*/
export async function getChatMember(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID or username */
chatId: InputPeerLike

View file

@ -1,6 +1,10 @@
import { assertNever, BaseTelegramClient, Long, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import Long from 'long'
import { tl } from '@mtcute/tl'
import { assertNever } from '../../../types/utils.js'
import { assertTypeIs } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { ArrayWithTotal, ChatMember, InputPeerLike, MtInvalidPeerTypeError, PeersIndex } from '../../types/index.js'
import { makeArrayWithTotal } from '../../utils/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js'
@ -15,7 +19,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param params Additional parameters
*/
export async function getChatMembers(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
params?: {
/**

View file

@ -1,5 +1,5 @@
import { BaseTelegramClient, MtArgumentError } from '@mtcute/core'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import { ChatPreview, MtPeerNotFoundError } from '../../types/index.js'
import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js'
@ -12,7 +12,7 @@ import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js'
* In case you are trying to get info about private chat that you have already joined.
* Use {@link getChat} or {@link getFullChat} instead.
*/
export async function getChatPreview(client: BaseTelegramClient, inviteLink: string): Promise<ChatPreview> {
export async function getChatPreview(client: ITelegramClient, inviteLink: string): Promise<ChatPreview> {
const m = inviteLink.match(INVITE_LINK_REGEX)
if (!m) throw new MtArgumentError('Invalid invite link')

View file

@ -1,5 +1,5 @@
import { BaseTelegramClient, MtArgumentError } from '@mtcute/core'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import { Chat, InputPeerLike, MtPeerNotFoundError } from '../../types/index.js'
import { INVITE_LINK_REGEX } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -14,7 +14,7 @@ import { _getRawPeerBatched } from './batched-queries.js'
* In case you are trying to get info about private chat that you haven't joined.
* Use {@link getChatPreview} instead.
*/
export async function getChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise<Chat> {
export async function getChat(client: ITelegramClient, chatId: InputPeerLike): Promise<Chat> {
if (typeof chatId === 'string') {
const m = chatId.match(INVITE_LINK_REGEX)

View file

@ -1,5 +1,7 @@
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import { Chat, InputPeerLike } from '../../types/index.js'
import {
INVITE_LINK_REGEX,
@ -20,7 +22,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* In case you are trying to get info about private chat that you haven't joined.
* Use {@link getChatPreview} instead.
*/
export async function getFullChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise<Chat> {
export async function getFullChat(client: ITelegramClient, chatId: InputPeerLike): Promise<Chat> {
if (typeof chatId === 'string') {
const m = chatId.match(INVITE_LINK_REGEX)

View file

@ -1,8 +1,10 @@
import { BaseTelegramClient, getMarkedPeerId, tl } from '@mtcute/core'
import { assertTypeIs } from '@mtcute/core/utils.js'
import { tl } from '@mtcute/tl'
import { getMarkedPeerId } from '../../../utils/peer-utils.js'
import { assertTypeIs } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { Chat } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
/**
* Get nearby chats
@ -10,7 +12,7 @@ import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
* @param latitude Latitude of the location
* @param longitude Longitude of the location
*/
export async function getNearbyChats(client: BaseTelegramClient, latitude: number, longitude: number): Promise<Chat[]> {
export async function getNearbyChats(client: ITelegramClient, latitude: number, longitude: number): Promise<Chat[]> {
const res = await client.call({
_: 'contacts.getLocated',
geoPoint: {
@ -21,7 +23,7 @@ export async function getNearbyChats(client: BaseTelegramClient, latitude: numbe
})
assertIsUpdatesGroup('contacts.getLocated', res)
client.network.handleUpdate(res, true)
// client.handleClientUpdate(res, true)
if (!res.updates.length) return []

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { ArrayWithTotal, Chat, InputPeerLike } from '../../types/index.js'
import { makeArrayWithTotal } from '../../utils/misc-utils.js'
import { toInputChannel } from '../../utils/peer-utils.js'
@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* > Returns empty array in case there are no similar channels available.
*/
export async function getSimilarChannels(
client: BaseTelegramClient,
client: ITelegramClient,
channel: InputPeerLike,
): Promise<ArrayWithTotal<Chat>> {
const res = await client.call({

View file

@ -1,5 +1,8 @@
import { BaseTelegramClient, Long, tl } from '@mtcute/core'
import Long from 'long'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { ChatEvent, InputPeerLike } from '../../types/index.js'
import { normalizeChatEventFilters } from '../../types/peers/chat-event/filters.js'
import { toInputChannel, toInputUser } from '../../utils/peer-utils.js'
@ -16,7 +19,7 @@ import { getChatEventLog } from './get-chat-event-log.js'
* @param params
*/
export async function* iterChatEventLog(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
params?: Parameters<typeof getChatEventLog>[2] & {
/**

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { ChatMember, InputPeerLike } from '../../types/index.js'
import { isInputPeerChat } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -16,7 +15,7 @@ import { getChatMembers } from './get-chat-members.js'
* @param params Additional parameters
*/
export async function* iterChatMembers(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
params?: Parameters<typeof getChatMembers>[2] & {
/**

View file

@ -1,8 +1,7 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { Chat, InputPeerLike } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
import { INVITE_LINK_REGEX, toInputChannel } from '../../utils/peer-utils.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
@ -16,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Chat identifier. Either an invite link (`t.me/joinchat/*`), a username (`@username`)
* or ID of the linked supergroup or channel.
*/
export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike): Promise<Chat> {
export async function joinChat(client: ITelegramClient, chatId: InputPeerLike): Promise<Chat> {
if (typeof chatId === 'string') {
const m = chatId.match(INVITE_LINK_REGEX)
@ -27,7 +26,7 @@ export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike
})
assertIsUpdatesGroup('messages.importChatInvite', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}
@ -40,7 +39,7 @@ export async function joinChat(client: BaseTelegramClient, chatId: InputPeerLike
assertIsUpdatesGroup('channels.joinChannel', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { sleep } from '@mtcute/core/utils.js'
import { sleep } from '../../../utils/misc-utils.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, Message } from '../../types/index.js'
import { isInputPeerChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -15,7 +14,7 @@ import { unbanChatMember } from './unban-chat-member.js'
* @returns Service message about removed user, if one was generated.
*/
export async function kickChatMember(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID */
chatId: InputPeerLike

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -11,7 +10,7 @@ import { deleteHistory } from './delete-history.js'
* @param chatId Chat ID or username
*/
export async function leaveChat(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
params?: {
/**
@ -27,14 +26,14 @@ export async function leaveChat(
_: 'channels.leaveChannel',
channel: toInputChannel(chat),
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
} else if (isInputPeerChat(chat)) {
const res = await client.call({
_: 'messages.deleteChatUser',
chatId: chat.chatId,
userId: { _: 'inputUserSelf' },
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
if (params?.clear) {
await deleteHistory(client, chat)

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
*
* @param chatId Chat ID
*/
export async function markChatUnread(client: BaseTelegramClient, chatId: InputPeerLike): Promise<void> {
export async function markChatUnread(client: ITelegramClient, chatId: InputPeerLike): Promise<void> {
const r = await client.call({
_: 'messages.markDialogUnread',
peer: {

View file

@ -1,9 +1,7 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/peers/index.js'
import { isInputPeerChannel } from '../../utils/peer-utils.js'
import { getPeerDialogs } from '../dialogs/get-peer-dialogs.js'
import { notifyChannelClosed, notifyChannelOpened } from '../updates/manager.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
@ -14,15 +12,13 @@ import { resolvePeer } from '../users/resolve-peer.js'
*
* @param chat Chat to open
*/
export async function openChat(client: BaseTelegramClient, chat: InputPeerLike): Promise<void> {
export async function openChat(client: ITelegramClient, chat: InputPeerLike): Promise<void> {
const peer = await resolvePeer(client, chat)
if (isInputPeerChannel(peer)) {
const [dialog] = await getPeerDialogs(client, peer)
if (!client.network.params.disableUpdates) {
notifyChannelOpened(client, peer.channelId, dialog.raw.pts)
}
await client.notifyChannelOpened(peer.channelId, dialog.raw.pts)
}
// todo: once we have proper dialogs/peers db, we should also
@ -38,11 +34,11 @@ export async function openChat(client: BaseTelegramClient, chat: InputPeerLike):
*
* @param chat Chat to open
*/
export async function closeChat(client: BaseTelegramClient, chat: InputPeerLike): Promise<void> {
export async function closeChat(client: ITelegramClient, chat: InputPeerLike): Promise<void> {
const peer = await resolvePeer(client, chat)
if (isInputPeerChannel(peer) && !client.network.params.disableUpdates) {
notifyChannelClosed(client, peer.channelId)
if (isInputPeerChannel(peer)) {
await client.notifyChannelClosed(peer.channelId)
}
// todo: once we have proper dialogs/peers db, we should also

View file

@ -1,8 +1,8 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js'
import { isSelfPeer } from '../auth/_state.js'
import { isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js'
import { isSelfPeer } from '../auth/utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
@ -11,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param peerId Bot, channel or "me"/"self"
*/
export async function reorderUsernames(
client: BaseTelegramClient,
client: ITelegramClient,
peerId: InputPeerLike,
order: string[],
): Promise<void> {

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { normalizeDate } from '../../utils/misc-utils.js'
import { isInputPeerChannel, toInputChannel } from '../../utils/peer-utils.js'
@ -9,7 +10,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Restrict a user in a supergroup.
*/
export async function restrictChatMember(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID */
chatId: InputPeerLike
@ -56,5 +57,5 @@ export async function restrictChatMember(
...restrictions,
},
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,6 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -10,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param draft Draft message, or `null` to delete.
*/
export async function saveDraft(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
draft: null | Omit<tl.RawDraftMessage, '_' | 'date'>,
): Promise<void> {

View file

@ -1,8 +1,11 @@
import { BaseTelegramClient, MtTypeAssertionError, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { MtTypeAssertionError } from '../../../types/errors.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel } from '../../utils/index.js'
import { isSelfPeer } from '../auth/_state.js'
import { isInputPeerChannel, isInputPeerUser, toInputChannel } from '../../utils/index.js'
import { isSelfPeer } from '../auth/utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
// @available=user
@ -10,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Set peer color and optionally background pattern
*/
export async function setChatColor(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/**
* Peer where to update the color.
@ -58,7 +61,7 @@ export async function setChatColor(
backgroundEmojiId,
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return
}

View file

@ -1,7 +1,8 @@
import { BaseTelegramClient, tl } from '@mtcute/core'
import { tl } from '@mtcute/tl'
import { ITelegramClient } from '../../client.types.js'
import { Chat, InputPeerLike } from '../../types/index.js'
import { assertIsUpdatesGroup } from '../../utils/updates-utils.js'
import { assertIsUpdatesGroup } from '../../updates/utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
@ -17,7 +18,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* and passing `{}` (empty object) will lift any restrictions
*/
export async function setChatDefaultPermissions(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
restrictions: Omit<tl.RawChatBannedRights, '_' | 'untilDate'>,
): Promise<Chat> {
@ -35,7 +36,7 @@ export async function setChatDefaultPermissions(
assertIsUpdatesGroup('messages.editChatDefaultBannedRights', res)
client.network.handleUpdate(res)
client.handleClientUpdate(res)
return new Chat(res.chats[0])
}

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -13,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param description New chat description, 0-255 characters
*/
export async function setChatDescription(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
description: string,
): Promise<void> {

View file

@ -1,7 +1,10 @@
import { BaseTelegramClient, MtArgumentError, tl } from '@mtcute/core'
import { fileIdToInputPhoto, tdFileId } from '@mtcute/file-id'
import { tdFileId } from '@mtcute/file-id'
import { tl } from '@mtcute/tl'
import { MtArgumentError } from '../../../types/errors.js'
import { ITelegramClient } from '../../client.types.js'
import { InputFileLike, InputPeerLike, isUploadedFile, MtInvalidPeerTypeError } from '../../types/index.js'
import { fileIdToInputPhoto } from '../../utils/convert-file-id.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js'
import { uploadFile } from '../files/upload-file.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -12,7 +15,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* You must be an administrator and have the appropriate permissions.
*/
export async function setChatPhoto(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Chat ID or username */
chatId: InputPeerLike
@ -99,5 +102,5 @@ export async function setChatPhoto(
photo,
})
}
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike, MtInvalidPeerTypeError } from '../../types/index.js'
import { isInputPeerChannel, isInputPeerChat, toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -12,7 +11,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param chatId Chat ID or username
* @param title New chat title, 1-255 characters
*/
export async function setChatTitle(client: BaseTelegramClient, chatId: InputPeerLike, title: string): Promise<void> {
export async function setChatTitle(client: ITelegramClient, chatId: InputPeerLike, title: string): Promise<void> {
const chat = await resolvePeer(client, chatId)
let res
@ -30,5 +29,5 @@ export async function setChatTitle(client: BaseTelegramClient, chatId: InputPeer
})
} else throw new MtInvalidPeerTypeError(chatId, 'chat or channel')
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -9,7 +8,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param chatId Chat ID
* @param period New TTL period, in seconds (or 0 to disable)
*/
export async function setChatTtl(client: BaseTelegramClient, chatId: InputPeerLike, period: number): Promise<void> {
export async function setChatTtl(client: ITelegramClient, chatId: InputPeerLike, period: number): Promise<void> {
await client.call({
_: 'messages.setHistoryTTL',
peer: await resolvePeer(client, chatId),

View file

@ -1,6 +1,5 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '@mtcute/core/utils.js'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param username New username, or `null` to remove
*/
export async function setChatUsername(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
username: string | null,
): Promise<void> {

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -13,11 +12,11 @@ import { resolvePeer } from '../users/resolve-peer.js'
* Users will be able to send a message only once per this interval.
* Valid values are: `0 (off), 10, 30, 60 (1m), 300 (5m), 900 (15m) or 3600 (1h)`
*/
export async function setSlowMode(client: BaseTelegramClient, chatId: InputPeerLike, seconds = 0): Promise<void> {
export async function setSlowMode(client: ITelegramClient, chatId: InputPeerLike, seconds = 0): Promise<void> {
const res = await client.call({
_: 'channels.toggleSlowMode',
channel: toInputChannel(await resolvePeer(client, chatId), chatId),
seconds,
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -10,7 +9,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param enabled Whether content protection should be enabled
*/
export async function toggleContentProtection(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
enabled = false,
): Promise<void> {
@ -19,5 +18,5 @@ export async function toggleContentProtection(
peer: await resolvePeer(client, chatId),
enabled,
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

View file

@ -1,8 +1,8 @@
import { BaseTelegramClient } from '@mtcute/core'
import { assertTrue } from '../../../utils/type-assertions.js'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { assertTrue, isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js'
import { isSelfPeer } from '../auth/_state.js'
import { isInputPeerChannel, isInputPeerUser, toInputChannel, toInputUser } from '../../utils/index.js'
import { isSelfPeer } from '../auth/utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
/**
@ -12,7 +12,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* > using {@link setUsername}/{@link setChatUsername}
*/
export async function toggleFragmentUsername(
client: BaseTelegramClient,
client: ITelegramClient,
params: {
/** Peer ID whose username to toggle */
peerId: InputPeerLike

View file

@ -1,5 +1,4 @@
import { BaseTelegramClient } from '@mtcute/core'
import { ITelegramClient } from '../../client.types.js'
import { InputPeerLike } from '../../types/index.js'
import { toInputChannel } from '../../utils/peer-utils.js'
import { resolvePeer } from '../users/resolve-peer.js'
@ -14,7 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
* @param enabled Whether join requests should be enabled
*/
export async function toggleJoinRequests(
client: BaseTelegramClient,
client: ITelegramClient,
chatId: InputPeerLike,
enabled = false,
): Promise<void> {
@ -23,5 +22,5 @@ export async function toggleJoinRequests(
channel: toInputChannel(await resolvePeer(client, chatId), chatId),
enabled,
})
client.network.handleUpdate(res)
client.handleClientUpdate(res)
}

Some files were not shown because too many files have changed in this diff Show more