chore: enabled isolatedDeclarations
This commit is contained in:
parent
a0ed9c2426
commit
b76463ccc0
126 changed files with 676 additions and 490 deletions
|
@ -28,12 +28,10 @@ describe('@mtcute/tl', () => {
|
||||||
|
|
||||||
it('readers map works with TlBinaryReader', () => {
|
it('readers map works with TlBinaryReader', () => {
|
||||||
const buf = p.hexDecode('4ca5e8dd7b00000000000000c801000000000000')
|
const buf = p.hexDecode('4ca5e8dd7b00000000000000c801000000000000')
|
||||||
// eslint-disable-next-line
|
|
||||||
const obj = TlBinaryReader.deserializeObject<any>(__tlReaderMap, buf)
|
const obj = TlBinaryReader.deserializeObject<any>(__tlReaderMap, buf)
|
||||||
|
|
||||||
expect(obj._).equal('inputPeerUser')
|
expect(obj._).equal('inputPeerUser')
|
||||||
expect(obj.userId).equal(123)
|
expect(obj.userId).equal(123)
|
||||||
// eslint-disable-next-line
|
|
||||||
expect(obj.accessHash.toString()).equal('456')
|
expect(obj.accessHash.toString()).equal('456')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,12 @@ export default antfu({
|
||||||
SwitchCase: 1,
|
SwitchCase: 1,
|
||||||
VariableDeclarator: 1,
|
VariableDeclarator: 1,
|
||||||
}],
|
}],
|
||||||
|
'style/max-len': ['error', {
|
||||||
|
code: 120,
|
||||||
|
ignoreComments: true,
|
||||||
|
ignoreStrings: true,
|
||||||
|
ignoreTemplateLiterals: true,
|
||||||
|
}],
|
||||||
'curly': ['error', 'multi-line'],
|
'curly': ['error', 'multi-line'],
|
||||||
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
'style/brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
||||||
'node/prefer-global/process': ['error', 'always'],
|
'node/prefer-global/process': ['error', 'always'],
|
||||||
|
@ -60,7 +66,7 @@ export default antfu({
|
||||||
'ts/no-redeclare': 'off',
|
'ts/no-redeclare': 'off',
|
||||||
'eslint-comments/no-unlimited-disable': 'off',
|
'eslint-comments/no-unlimited-disable': 'off',
|
||||||
'no-cond-assign': 'off',
|
'no-cond-assign': 'off',
|
||||||
'ts/explicit-function-return-type': 'off', // todo: enable once we move to isolatedDeclarations
|
'ts/explicit-function-return-type': 'off',
|
||||||
'no-labels': 'off',
|
'no-labels': 'off',
|
||||||
'no-restricted-syntax': 'off',
|
'no-restricted-syntax': 'off',
|
||||||
'unicorn/no-new-array': 'off',
|
'unicorn/no-new-array': 'off',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Interface as RlInterface } from 'node:readline'
|
import type { Interface as RlInterface } from 'node:readline'
|
||||||
import { createInterface } from 'node:readline'
|
import { createInterface } from 'node:readline'
|
||||||
|
import type { Readable } from 'node:stream'
|
||||||
|
|
||||||
import type { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'
|
import type { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'
|
||||||
import type {
|
import type {
|
||||||
|
@ -145,7 +146,7 @@ export class TelegramClient extends TelegramClientBase {
|
||||||
return downloadToFile(this, filename, location, params)
|
return downloadToFile(this, filename, location, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined) {
|
downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined): Readable {
|
||||||
return downloadAsNodeStream(this, location, params)
|
return downloadAsNodeStream(this, location, params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ export interface SqliteStorageDriverOptions {
|
||||||
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export { SqliteStorageDriver } from './driver.js'
|
||||||
export class SqliteStorage extends BaseSqliteStorage {
|
export class SqliteStorage extends BaseSqliteStorage {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super(new SqliteStorageDriver(filename, params))
|
super(new SqliteStorageDriver(filename, params))
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ export class BunCryptoProvider extends BaseCryptoProvider implements ICryptoProv
|
||||||
return gunzip(data)
|
return gunzip(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
randomFill(buf: Uint8Array) {
|
randomFill(buf: Uint8Array): void {
|
||||||
crypto.getRandomValues(buf)
|
crypto.getRandomValues(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,11 @@ function isBunFile(file: unknown): file is BunFile {
|
||||||
return file instanceof Blob && 'name' in file && file.name.length > 0
|
return file instanceof Blob && 'name' in file && file.name.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function normalizeFile(file: UploadFileLike) {
|
export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
|
file: UploadFileLike
|
||||||
|
fileName?: string | undefined
|
||||||
|
fileSize?: number
|
||||||
|
} | null> {
|
||||||
if (typeof file === 'string') {
|
if (typeof file === 'string') {
|
||||||
file = Bun.file(file)
|
file = Bun.file(file)
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,5 +149,5 @@ export abstract class BaseTcpTransport extends EventEmitter implements ITelegram
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TcpTransport extends BaseTcpTransport {
|
export class TcpTransport extends BaseTcpTransport {
|
||||||
_packetCodec = new IntermediatePacketCodec()
|
_packetCodec: IntermediatePacketCodec = new IntermediatePacketCodec()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export function parseIpFromBytes(data: Uint8Array): string {
|
||||||
throw new MtArgumentError('Invalid IP address length')
|
throw new MtArgumentError('Invalid IP address length')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serializeIpv4ToBytes(ip: string, buf: Uint8Array) {
|
export function serializeIpv4ToBytes(ip: string, buf: Uint8Array): void {
|
||||||
const parts = ip.split('.')
|
const parts = ip.split('.')
|
||||||
|
|
||||||
if (parts.length !== 4) {
|
if (parts.length !== 4) {
|
||||||
|
@ -33,7 +33,7 @@ export function serializeIpv4ToBytes(ip: string, buf: Uint8Array) {
|
||||||
buf[3] = Number(parts[3])
|
buf[3] = Number(parts[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
export function serializeIpv6ToBytes(ip: string, buf: Uint8Array) {
|
export function serializeIpv6ToBytes(ip: string, buf: Uint8Array): void {
|
||||||
const parts = ip.split(':')
|
const parts = ip.split(':')
|
||||||
|
|
||||||
if (parts.length !== 8) {
|
if (parts.length !== 8) {
|
||||||
|
|
|
@ -94,7 +94,7 @@ export class BaseTelegramClient implements ITelegramClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly appConfig = new AppConfigManager(this)
|
readonly appConfig: AppConfigManager = new AppConfigManager(this)
|
||||||
|
|
||||||
private _prepare = asyncResettable(async () => {
|
private _prepare = asyncResettable(async () => {
|
||||||
await this.mt.prepare()
|
await this.mt.prepare()
|
||||||
|
@ -115,7 +115,7 @@ export class BaseTelegramClient implements ITelegramClient {
|
||||||
*
|
*
|
||||||
* Call {@link connect} to actually connect.
|
* Call {@link connect} to actually connect.
|
||||||
*/
|
*/
|
||||||
prepare() {
|
prepare(): Promise<void> {
|
||||||
return this._prepare.run()
|
return this._prepare.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +327,10 @@ export class BaseTelegramClient implements ITelegramClient {
|
||||||
this._connectionStateHandler = handler
|
this._connectionStateHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
async getApiCrenetials() {
|
async getApiCrenetials(): Promise<{
|
||||||
|
id: number
|
||||||
|
hash: string
|
||||||
|
}> {
|
||||||
return {
|
return {
|
||||||
id: this.params.apiId,
|
id: this.params.apiId,
|
||||||
hash: this.params.apiHash,
|
hash: this.params.apiHash,
|
||||||
|
|
|
@ -519,7 +519,6 @@ export interface TelegramClient extends ITelegramClient {
|
||||||
*/
|
*/
|
||||||
on(name: 'delete_business_message', handler: ((upd: DeleteBusinessMessageUpdate) => void)): this
|
on(name: 'delete_business_message', handler: ((upd: DeleteBusinessMessageUpdate) => void)): this
|
||||||
|
|
||||||
// eslint-disable-next-line ts/no-explicit-any
|
|
||||||
on(name: string, handler: (...args: any[]) => void): this
|
on(name: string, handler: (...args: any[]) => void): this
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,10 +9,11 @@ import {
|
||||||
toInputChannel,
|
toInputChannel,
|
||||||
toInputUser,
|
toInputUser,
|
||||||
} from '../../utils/peer-utils.js'
|
} from '../../utils/peer-utils.js'
|
||||||
|
import type { BatchedQuery } from '../../utils/query-batcher.js'
|
||||||
import { batchedQuery } from '../../utils/query-batcher.js'
|
import { batchedQuery } from '../../utils/query-batcher.js'
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const _getUsersBatched = batchedQuery<tl.TypeInputUser, tl.TypeUser, number>({
|
export const _getUsersBatched: BatchedQuery<tl.TypeInputUser, tl.TypeUser> = batchedQuery({
|
||||||
fetch: (client, items) =>
|
fetch: (client, items) =>
|
||||||
client
|
client
|
||||||
.call({
|
.call({
|
||||||
|
@ -63,7 +64,7 @@ export const _getUsersBatched = batchedQuery<tl.TypeInputUser, tl.TypeUser, numb
|
||||||
})
|
})
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const _getChatsBatched = batchedQuery<number, tl.RawChat, number>({
|
export const _getChatsBatched: BatchedQuery<number, tl.RawChat> = batchedQuery({
|
||||||
fetch: (client, items) =>
|
fetch: (client, items) =>
|
||||||
client
|
client
|
||||||
.call({
|
.call({
|
||||||
|
@ -78,7 +79,10 @@ export const _getChatsBatched = batchedQuery<number, tl.RawChat, number>({
|
||||||
})
|
})
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const _getChannelsBatched = batchedQuery<tl.TypeInputChannel, tl.RawChannel | tl.RawChannelForbidden, number>({
|
export const _getChannelsBatched: BatchedQuery<
|
||||||
|
tl.TypeInputChannel,
|
||||||
|
tl.RawChannel | tl.RawChannelForbidden
|
||||||
|
> = batchedQuery({
|
||||||
fetch: (client, items) =>
|
fetch: (client, items) =>
|
||||||
client
|
client
|
||||||
.call({
|
.call({
|
||||||
|
|
|
@ -156,7 +156,13 @@ export async function _processCommonSendParameters(
|
||||||
client: ITelegramClient,
|
client: ITelegramClient,
|
||||||
chatId: InputPeerLike,
|
chatId: InputPeerLike,
|
||||||
params: CommonSendParams,
|
params: CommonSendParams,
|
||||||
) {
|
): Promise<{
|
||||||
|
peer: tl.TypeInputPeer
|
||||||
|
replyTo: tl.TypeInputReplyTo | undefined
|
||||||
|
scheduleDate: number | undefined
|
||||||
|
quickReplyShortcut: tl.TypeInputQuickReplyShortcut | undefined
|
||||||
|
chainId: string
|
||||||
|
}> {
|
||||||
let peer = await resolvePeer(client, chatId)
|
let peer = await resolvePeer(client, chatId)
|
||||||
|
|
||||||
let replyTo = normalizeMessageId(params.replyTo)
|
let replyTo = normalizeMessageId(params.replyTo)
|
||||||
|
|
|
@ -62,7 +62,7 @@ export class PeersService extends BaseService {
|
||||||
this._cache = new LruMap(options.cacheSize ?? 100)
|
this._cache = new LruMap(options.cacheSize ?? 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePeersFrom(obj: tl.TlObject | tl.TlObject[]) {
|
async updatePeersFrom(obj: tl.TlObject | tl.TlObject[]): Promise<boolean> {
|
||||||
let count = 0
|
let count = 0
|
||||||
|
|
||||||
for (const peer of getAllPeersFrom(obj)) {
|
for (const peer of getAllPeersFrom(obj)) {
|
||||||
|
|
|
@ -23,9 +23,9 @@ export interface TelegramStorageManagerExtraOptions {
|
||||||
export class TelegramStorageManager {
|
export class TelegramStorageManager {
|
||||||
private provider
|
private provider
|
||||||
|
|
||||||
readonly updates
|
readonly updates: UpdatesStateService
|
||||||
readonly self: PublicPart<CurrentUserService>
|
readonly self: PublicPart<CurrentUserService>
|
||||||
readonly refMsgs
|
readonly refMsgs: RefMessagesService
|
||||||
readonly peers: PublicPart<PeersService>
|
readonly peers: PublicPart<PeersService>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -56,7 +56,7 @@ export class TelegramStorageManager {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear(withAuthKeys = false) {
|
async clear(withAuthKeys = false): Promise<void> {
|
||||||
await this.provider.peers.deleteAll()
|
await this.provider.peers.deleteAll()
|
||||||
await this.provider.refMessages.deleteAll()
|
await this.provider.refMessages.deleteAll()
|
||||||
await this.mt.clear(withAuthKeys)
|
await this.mt.clear(withAuthKeys)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Voice } from './voice.js'
|
||||||
export type ParsedDocument = Sticker | Voice | Audio | Video | Document
|
export type ParsedDocument = Sticker | Voice | Audio | Video | Document
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function parseSticker(doc: tl.RawDocument) {
|
export function parseSticker(doc: tl.RawDocument): Sticker | undefined {
|
||||||
const stickerAttr = doc.attributes.find(
|
const stickerAttr = doc.attributes.find(
|
||||||
a => a._ === 'documentAttributeSticker' || a._ === 'documentAttributeCustomEmoji',
|
a => a._ === 'documentAttributeSticker' || a._ === 'documentAttributeCustomEmoji',
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class Invoice {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly raw: tl.RawMessageMediaInvoice,
|
readonly raw: tl.RawMessageMediaInvoice,
|
||||||
private readonly _extendedMedia?: MessageMedia,
|
private readonly _extendedMedia?: MessageMedia | undefined,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,7 @@ export class Photo extends FileLocation {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly raw: tl.RawPhoto,
|
readonly raw: tl.RawPhoto,
|
||||||
readonly media?: tl.RawMessageMediaPhoto,
|
readonly media?: tl.RawMessageMediaPhoto | undefined,
|
||||||
) {
|
) {
|
||||||
const location = {
|
const location = {
|
||||||
_: 'inputPhotoFileLocation',
|
_: 'inputPhotoFileLocation',
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { PeersIndex } from '../peers/peers-index.js'
|
||||||
export class PollAnswer {
|
export class PollAnswer {
|
||||||
constructor(
|
constructor(
|
||||||
readonly raw: tl.TypePollAnswer,
|
readonly raw: tl.TypePollAnswer,
|
||||||
readonly result?: tl.TypePollAnswerVoters,
|
readonly result?: tl.RawPollAnswerVoters | undefined,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +71,7 @@ export class Poll {
|
||||||
constructor(
|
constructor(
|
||||||
readonly raw: tl.TypePoll,
|
readonly raw: tl.TypePoll,
|
||||||
readonly _peers: PeersIndex,
|
readonly _peers: PeersIndex,
|
||||||
readonly results?: tl.TypePollResults,
|
readonly results?: tl.RawPollResults | undefined,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -72,7 +72,7 @@ export class Sticker extends RawDocument {
|
||||||
constructor(
|
constructor(
|
||||||
doc: tl.RawDocument,
|
doc: tl.RawDocument,
|
||||||
readonly attr: tl.RawDocumentAttributeSticker | tl.RawDocumentAttributeCustomEmoji,
|
readonly attr: tl.RawDocumentAttributeSticker | tl.RawDocumentAttributeCustomEmoji,
|
||||||
readonly attr2?: tl.RawDocumentAttributeImageSize | tl.RawDocumentAttributeVideo,
|
readonly attr2?: tl.RawDocumentAttributeImageSize | tl.RawDocumentAttributeVideo | undefined,
|
||||||
) {
|
) {
|
||||||
super(doc)
|
super(doc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class Video extends RawDocument {
|
||||||
constructor(
|
constructor(
|
||||||
doc: tl.RawDocument,
|
doc: tl.RawDocument,
|
||||||
readonly attr: tl.RawDocumentAttributeVideo | tl.RawDocumentAttributeImageSize,
|
readonly attr: tl.RawDocumentAttributeVideo | tl.RawDocumentAttributeImageSize,
|
||||||
readonly media?: tl.RawMessageMediaDocument,
|
readonly media?: tl.RawMessageMediaDocument | undefined,
|
||||||
) {
|
) {
|
||||||
super(doc)
|
super(doc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,10 @@ export type InputMessageId = { chatId: InputPeerLike, message: number } | { mess
|
||||||
export type OmitInputMessageId<T> = Omit<T, 'chatId' | 'message'>
|
export type OmitInputMessageId<T> = Omit<T, 'chatId' | 'message'>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function normalizeInputMessageId(id: InputMessageId) {
|
export function normalizeInputMessageId(id: InputMessageId): {
|
||||||
|
chatId: InputPeerLike
|
||||||
|
message: number
|
||||||
|
} {
|
||||||
if ('chatId' in id) return id
|
if ('chatId' in id) return id
|
||||||
|
|
||||||
return { chatId: id.message.chat.inputPeer, message: id.message.id }
|
return { chatId: id.message.chat.inputPeer, message: id.message.id }
|
||||||
|
|
|
@ -67,7 +67,7 @@ export class MessageEntity {
|
||||||
*
|
*
|
||||||
* Since JS strings are UTF-16, you can use this as-is
|
* Since JS strings are UTF-16, you can use this as-is
|
||||||
*/
|
*/
|
||||||
get offset() {
|
get offset(): number {
|
||||||
return this.raw.offset
|
return this.raw.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ export class MessageEntity {
|
||||||
*
|
*
|
||||||
* Since JS strings are UTF-16, you can use this as-is
|
* Since JS strings are UTF-16, you can use this as-is
|
||||||
*/
|
*/
|
||||||
get length() {
|
get length(): number {
|
||||||
return this.raw.length
|
return this.raw.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { makeInspectable } from '../../utils/inspectable.js'
|
||||||
export class ChatColors {
|
export class ChatColors {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _peerId: number,
|
private readonly _peerId: number,
|
||||||
readonly raw?: tl.RawPeerColor,
|
readonly raw?: tl.RawPeerColor | undefined,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,7 +16,7 @@ export class ChatInviteLink {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
raw: tl.TypeExportedChatInvite,
|
raw: tl.TypeExportedChatInvite,
|
||||||
readonly _peers?: PeersIndex,
|
readonly _peers?: PeersIndex | undefined,
|
||||||
) {
|
) {
|
||||||
assertTypeIsNot('ChatInviteLink', raw, 'chatInvitePublicJoinRequests')
|
assertTypeIsNot('ChatInviteLink', raw, 'chatInvitePublicJoinRequests')
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class ForumTopic {
|
||||||
constructor(
|
constructor(
|
||||||
readonly raw: tl.RawForumTopic,
|
readonly raw: tl.RawForumTopic,
|
||||||
readonly _peers: PeersIndex,
|
readonly _peers: PeersIndex,
|
||||||
readonly _messages?: Map<number, tl.TypeMessage>,
|
readonly _messages?: Map<number, tl.TypeMessage> | undefined,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
static parseTlForumTopics(topics: tl.messages.TypeForumTopics): ForumTopic[] {
|
static parseTlForumTopics(topics: tl.messages.TypeForumTopics): ForumTopic[] {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { StoriesStealthMode } from './stealth-mode.js'
|
||||||
*/
|
*/
|
||||||
export class AllStories {
|
export class AllStories {
|
||||||
/** Peers index */
|
/** Peers index */
|
||||||
readonly _peers
|
readonly _peers: PeersIndex
|
||||||
constructor(
|
constructor(
|
||||||
/** Raw TL object */
|
/** Raw TL object */
|
||||||
readonly raw: tl.stories.RawAllStories,
|
readonly raw: tl.stories.RawAllStories,
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { normalizeInputReaction } from '../../reactions/index.js'
|
||||||
export class StoryElement {
|
export class StoryElement {
|
||||||
private constructor(private _position: tl.RawMediaAreaCoordinates) {}
|
private constructor(private _position: tl.RawMediaAreaCoordinates) {}
|
||||||
|
|
||||||
static at(params: { x: number, y: number, width: number, height: number, rotation?: number }) {
|
static at(params: { x: number, y: number, width: number, height: number, rotation?: number }): StoryElement {
|
||||||
return new StoryElement({
|
return new StoryElement({
|
||||||
_: 'mediaAreaCoordinates',
|
_: 'mediaAreaCoordinates',
|
||||||
x: params.x,
|
x: params.x,
|
||||||
|
|
|
@ -3,6 +3,9 @@ import { tl } from '@mtcute/tl'
|
||||||
import { MtArgumentError } from '../../types/errors.js'
|
import { MtArgumentError } from '../../types/errors.js'
|
||||||
import type { MaybePromise } from '../../types/utils.js'
|
import type { MaybePromise } from '../../types/utils.js'
|
||||||
import { assertNever } from '../../types/utils.js'
|
import { assertNever } from '../../types/utils.js'
|
||||||
|
import type {
|
||||||
|
Logger,
|
||||||
|
} from '../../utils/index.js'
|
||||||
import {
|
import {
|
||||||
AsyncLock,
|
AsyncLock,
|
||||||
ConditionVariable,
|
ConditionVariable,
|
||||||
|
@ -89,26 +92,32 @@ const UPDATES_TOO_LONG = { _: 'updatesTooLong' } as const
|
||||||
// todo: fix docs
|
// todo: fix docs
|
||||||
export class UpdatesManager {
|
export class UpdatesManager {
|
||||||
updatesLoopActive = false
|
updatesLoopActive = false
|
||||||
updatesLoopCv = new ConditionVariable()
|
updatesLoopCv: ConditionVariable = new ConditionVariable()
|
||||||
|
|
||||||
postponedTimer = new EarlyTimer()
|
postponedTimer: EarlyTimer = new EarlyTimer()
|
||||||
hasTimedoutPostponed = false
|
hasTimedoutPostponed = false
|
||||||
|
|
||||||
pendingUpdateContainers = new SortedLinkedList<PendingUpdateContainer>((a, b) => a.seqStart - b.seqStart)
|
pendingUpdateContainers: SortedLinkedList<PendingUpdateContainer>
|
||||||
pendingPtsUpdates = new SortedLinkedList<PendingUpdate>((a, b) => a.ptsBefore! - b.ptsBefore!)
|
= new SortedLinkedList((a, b) => a.seqStart - b.seqStart)
|
||||||
pendingPtsUpdatesPostponed = new SortedLinkedList<PendingUpdate>((a, b) => a.ptsBefore! - b.ptsBefore!)
|
|
||||||
pendingQtsUpdates = new SortedLinkedList<PendingUpdate>((a, b) => a.qtsBefore! - b.qtsBefore!)
|
|
||||||
pendingQtsUpdatesPostponed = new SortedLinkedList<PendingUpdate>((a, b) => a.qtsBefore! - b.qtsBefore!)
|
|
||||||
pendingUnorderedUpdates = new Deque<PendingUpdate>()
|
|
||||||
|
|
||||||
noDispatchEnabled
|
pendingPtsUpdates: SortedLinkedList<PendingUpdate> = new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!)
|
||||||
|
pendingPtsUpdatesPostponed: SortedLinkedList<PendingUpdate>
|
||||||
|
= new SortedLinkedList((a, b) => a.ptsBefore! - b.ptsBefore!)
|
||||||
|
|
||||||
|
pendingQtsUpdates: SortedLinkedList<PendingUpdate> = new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!)
|
||||||
|
pendingQtsUpdatesPostponed: SortedLinkedList<PendingUpdate>
|
||||||
|
= new SortedLinkedList((a, b) => a.qtsBefore! - b.qtsBefore!)
|
||||||
|
|
||||||
|
pendingUnorderedUpdates: Deque<PendingUpdate> = new Deque()
|
||||||
|
|
||||||
|
noDispatchEnabled: boolean
|
||||||
// channel id or 0 => msg id
|
// channel id or 0 => msg id
|
||||||
noDispatchMsg = new Map<number, Set<number>>()
|
noDispatchMsg: Map<number, Set<number>> = new Map()
|
||||||
// channel id or 0 => pts
|
// channel id or 0 => pts
|
||||||
noDispatchPts = new Map<number, Set<number>>()
|
noDispatchPts: Map<number, Set<number>> = new Map()
|
||||||
noDispatchQts = new Set<number>()
|
noDispatchQts: Set<number> = new Set()
|
||||||
|
|
||||||
lock = new AsyncLock()
|
lock: AsyncLock = new AsyncLock()
|
||||||
// rpsIncoming?: RpsMeter
|
// rpsIncoming?: RpsMeter
|
||||||
// rpsProcessing?: RpsMeter
|
// rpsProcessing?: RpsMeter
|
||||||
|
|
||||||
|
@ -129,14 +138,14 @@ export class UpdatesManager {
|
||||||
|
|
||||||
// whether to catch up channels from the locally stored pts
|
// whether to catch up channels from the locally stored pts
|
||||||
catchingUp = false
|
catchingUp = false
|
||||||
catchUpOnStart
|
catchUpOnStart: boolean
|
||||||
|
|
||||||
cpts = new Map<number, number>()
|
cpts: Map<number, number> = new Map()
|
||||||
cptsMod = new Map<number, number>()
|
cptsMod: Map<number, number> = new Map()
|
||||||
channelDiffTimeouts = new Map<number, NodeJS.Timeout>()
|
channelDiffTimeouts: Map<number, NodeJS.Timeout> = new Map()
|
||||||
channelsOpened = new Map<number, number>()
|
channelsOpened: Map<number, number> = new Map()
|
||||||
|
|
||||||
log
|
log: Logger
|
||||||
private _handler: RawUpdateHandler = () => {}
|
private _handler: RawUpdateHandler = () => {}
|
||||||
|
|
||||||
private _onCatchingUp: (catchingUp: boolean) => void = () => {}
|
private _onCatchingUp: (catchingUp: boolean) => void = () => {}
|
||||||
|
@ -189,7 +198,7 @@ export class UpdatesManager {
|
||||||
this._onCatchingUp = handler
|
this._onCatchingUp = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy(): void {
|
||||||
this.stopLoop()
|
this.stopLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function encodeInlineMessageId(id: tl.TypeInputBotInlineMessageID): strin
|
||||||
return getPlatform().base64Encode(writer.result(), true)
|
return getPlatform().base64Encode(writer.result(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeInlineId(id: string | tl.TypeInputBotInlineMessageID) {
|
export function normalizeInlineId(id: string | tl.TypeInputBotInlineMessageID): tl.TypeInputBotInlineMessageID {
|
||||||
if (typeof id === 'string') {
|
if (typeof id === 'string') {
|
||||||
return parseInlineMessageId(id)
|
return parseInlineMessageId(id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { assertNever } from '../../types/utils.js'
|
||||||
import { MtInvalidPeerTypeError } from '../types/errors.js'
|
import { MtInvalidPeerTypeError } from '../types/errors.js'
|
||||||
import type { InputPeerLike } from '../types/peers/index.js'
|
import type { InputPeerLike } from '../types/peers/index.js'
|
||||||
|
|
||||||
export const INVITE_LINK_REGEX
|
export const INVITE_LINK_REGEX: RegExp
|
||||||
= /^(?:https?:\/\/)?(?:www\.)?t(?:elegram)?\.(?:org|me|dog)\/(?:joinchat\/|\+)([\w-]+)$/i
|
= /^(?:https?:\/\/)?(?:www\.)?t(?:elegram)?\.(?:org|me|dog)\/(?:joinchat\/|\+)([\w-]+)$/i
|
||||||
|
|
||||||
// helpers to convert result of `resolvePeer` function
|
// helpers to convert result of `resolvePeer` function
|
||||||
|
@ -145,7 +145,7 @@ export function inputPeerToPeer(inp: tl.TypeInputPeer): tl.TypePeer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractUsernames(obj: tl.RawUser | tl.RawChannel) {
|
export function extractUsernames(obj: tl.RawUser | tl.RawChannel): string[] {
|
||||||
if (obj.usernames?.length) return obj.usernames.map(x => x.username.toLowerCase())
|
if (obj.usernames?.length) return obj.usernames.map(x => x.username.toLowerCase())
|
||||||
if (obj.username) return [obj.username.toLowerCase()]
|
if (obj.username) return [obj.username.toLowerCase()]
|
||||||
|
|
||||||
|
|
|
@ -4,14 +4,15 @@ import type { ITelegramClient } from '../client.types.js'
|
||||||
type Resolve<T> = (value: T | PromiseLike<T>) => void
|
type Resolve<T> = (value: T | PromiseLike<T>) => void
|
||||||
type Reject = (err?: unknown) => void
|
type Reject = (err?: unknown) => void
|
||||||
|
|
||||||
type WaitersMap<K, U, T> = Map<K, [T, Resolve<U | null>, Reject][]>
|
type WaitersMap<U, T> = Map<string | number, [T, Resolve<U | null>, Reject][]>
|
||||||
interface InternalState<K, U, T> {
|
interface InternalState<U, T> {
|
||||||
waiters: WaitersMap<K, U, T>
|
waiters: WaitersMap<U, T>
|
||||||
fetchingKeys: Set<K>
|
fetchingKeys: Set<string | number>
|
||||||
retryQueue: Deque<T>
|
retryQueue: Deque<T>
|
||||||
numRunning: number
|
numRunning: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BatchedQuery<T, U> = (client: ITelegramClient, item: T) => Promise<U | null>
|
||||||
// todo: should it be MtClient?
|
// todo: should it be MtClient?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +24,7 @@ interface InternalState<K, U, T> {
|
||||||
* - "key" - unique identifier of the item, which should be deriveable from both input and output.
|
* - "key" - unique identifier of the item, which should be deriveable from both input and output.
|
||||||
* used for matching input and output items and deduplicating them.
|
* used for matching input and output items and deduplicating them.
|
||||||
*/
|
*/
|
||||||
export function batchedQuery<T, U, K extends string | number>(params: {
|
export function batchedQuery<T, U>(params: {
|
||||||
/**
|
/**
|
||||||
* Fetcher function, taking an array of input items and returning an array of output items.
|
* Fetcher function, taking an array of input items and returning an array of output items.
|
||||||
*
|
*
|
||||||
|
@ -33,9 +34,9 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
fetch: (client: ITelegramClient, items: T[]) => Promise<U[]>
|
fetch: (client: ITelegramClient, items: T[]) => Promise<U[]>
|
||||||
|
|
||||||
/** Key derivation function for input items */
|
/** Key derivation function for input items */
|
||||||
inputKey: (item: T, client: ITelegramClient) => K
|
inputKey: (item: T, client: ITelegramClient) => string | number
|
||||||
/** Key derivation function for output items */
|
/** Key derivation function for output items */
|
||||||
outputKey: (item: U, client: ITelegramClient) => K
|
outputKey: (item: U, client: ITelegramClient) => string | number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of items to be passed to the `fetcher` function at once.
|
* Maximum number of items to be passed to the `fetcher` function at once.
|
||||||
|
@ -64,13 +65,13 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
* or an array of items for which the query should be retried (waiters for other items will throw `err`).
|
* or an array of items for which the query should be retried (waiters for other items will throw `err`).
|
||||||
*/
|
*/
|
||||||
retrySingleOnError?: (items: T[], err: unknown) => boolean | T[]
|
retrySingleOnError?: (items: T[], err: unknown) => boolean | T[]
|
||||||
}): (client: ITelegramClient, item: T) => Promise<U | null> {
|
}): BatchedQuery<T, U> {
|
||||||
const { inputKey, outputKey, fetch, maxBatchSize = Infinity, maxConcurrent = 1, retrySingleOnError } = params
|
const { inputKey, outputKey, fetch, maxBatchSize = Infinity, maxConcurrent = 1, retrySingleOnError } = params
|
||||||
|
|
||||||
const symbol = Symbol('batchedQueryState')
|
const symbol = Symbol('batchedQueryState')
|
||||||
|
|
||||||
function getState(client_: ITelegramClient) {
|
function getState(client_: ITelegramClient) {
|
||||||
const client = client_ as { [symbol]?: InternalState<K, U, T> }
|
const client = client_ as { [symbol]?: InternalState<U, T> }
|
||||||
|
|
||||||
if (!client[symbol]) {
|
if (!client[symbol]) {
|
||||||
client[symbol] = {
|
client[symbol] = {
|
||||||
|
@ -84,7 +85,7 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
return client[symbol]
|
return client[symbol]
|
||||||
}
|
}
|
||||||
|
|
||||||
function addWaiter(client: ITelegramClient, waiters: WaitersMap<K, U, T>, item: T) {
|
function addWaiter(client: ITelegramClient, waiters: WaitersMap<U, T>, item: T) {
|
||||||
const key = inputKey(item, client)
|
const key = inputKey(item, client)
|
||||||
|
|
||||||
let arr = waiters.get(key)
|
let arr = waiters.get(key)
|
||||||
|
@ -99,7 +100,7 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function popWaiters(waiters: WaitersMap<K, U, T>, key: K) {
|
function popWaiters(waiters: WaitersMap<U, T>, key: string | number) {
|
||||||
const arr = waiters.get(key)
|
const arr = waiters.get(key)
|
||||||
if (!arr) return []
|
if (!arr) return []
|
||||||
|
|
||||||
|
@ -108,19 +109,19 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
function startLoops(client: ITelegramClient, state: InternalState<K, U, T>) {
|
function startLoops(client: ITelegramClient, state: InternalState<U, T>) {
|
||||||
for (let i = state.numRunning; i <= maxConcurrent; i++) {
|
for (let i = state.numRunning; i <= maxConcurrent; i++) {
|
||||||
processPending(client, state)
|
processPending(client, state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processPending(client: ITelegramClient, state: InternalState<K, U, T>) {
|
function processPending(client: ITelegramClient, state: InternalState<U, T>) {
|
||||||
const { waiters, fetchingKeys, retryQueue } = state
|
const { waiters, fetchingKeys, retryQueue } = state
|
||||||
|
|
||||||
if (state.numRunning >= maxConcurrent) return
|
if (state.numRunning >= maxConcurrent) return
|
||||||
|
|
||||||
const request: T[] = []
|
const request: T[] = []
|
||||||
const requestKeys: K[] = []
|
const requestKeys: (string | number)[] = []
|
||||||
let isRetryRequest = false
|
let isRetryRequest = false
|
||||||
|
|
||||||
if (retryQueue.length > 0) {
|
if (retryQueue.length > 0) {
|
||||||
|
@ -152,7 +153,7 @@ export function batchedQuery<T, U, K extends string | number>(params: {
|
||||||
// eslint-disable-next-line ts/no-floating-promises
|
// eslint-disable-next-line ts/no-floating-promises
|
||||||
fetch(client, request)
|
fetch(client, request)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const receivedKeys = new Set<K>()
|
const receivedKeys = new Set()
|
||||||
|
|
||||||
for (const it of res) {
|
for (const it of res) {
|
||||||
const key = outputKey(it, client)
|
const key = outputKey(it, client)
|
||||||
|
|
|
@ -24,7 +24,10 @@ export async function streamToBuffer(stream: ReadableStream<Uint8Array>): Promis
|
||||||
return concatBuffers(chunks)
|
return concatBuffers(chunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createChunkedReader(stream: ReadableStream<Uint8Array>, chunkSize: number) {
|
export function createChunkedReader(stream: ReadableStream<Uint8Array>, chunkSize: number): {
|
||||||
|
ended: () => boolean
|
||||||
|
read: () => Promise<Uint8Array | null>
|
||||||
|
} {
|
||||||
const reader = stream.getReader()
|
const reader = stream.getReader()
|
||||||
const lock = new AsyncLock()
|
const lock = new AsyncLock()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import type { WorkerInvoker } from './invoker.js'
|
||||||
|
|
||||||
export class AppConfigManagerProxy implements PublicPart<AppConfigManager> {
|
export class AppConfigManagerProxy implements PublicPart<AppConfigManager> {
|
||||||
readonly get: AppConfigManager['get']
|
readonly get: AppConfigManager['get']
|
||||||
readonly getField
|
readonly getField: AppConfigManager['getField']
|
||||||
|
|
||||||
constructor(readonly invoker: WorkerInvoker) {
|
constructor(readonly invoker: WorkerInvoker) {
|
||||||
const bind = invoker.makeBinder<AppConfigManager>('app-config')
|
const bind = invoker.makeBinder<AppConfigManager>('app-config')
|
||||||
|
|
|
@ -57,7 +57,7 @@ export class WorkerInvoker {
|
||||||
return this._invoke(target, method, args, false, abortSignal) as Promise<unknown>
|
return this._invoke(target, method, args, false, abortSignal) as Promise<unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResult(msg: Extract<WorkerOutboundMessage, { type: 'result' }>) {
|
handleResult(msg: Extract<WorkerOutboundMessage, { type: 'result' }>): void {
|
||||||
const promise = this._pending.get(msg.id)
|
const promise = this._pending.get(msg.id)
|
||||||
if (!promise) return
|
if (!promise) return
|
||||||
|
|
||||||
|
|
|
@ -18,36 +18,36 @@ export interface TelegramWorkerPortOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> implements ITelegramClient {
|
export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> implements ITelegramClient {
|
||||||
readonly log
|
readonly log: LogManager
|
||||||
|
|
||||||
private _connection
|
private _connection
|
||||||
private _invoker
|
private _invoker
|
||||||
|
|
||||||
readonly storage
|
readonly storage: TelegramStorageProxy
|
||||||
readonly appConfig
|
readonly appConfig: AppConfigManagerProxy
|
||||||
|
|
||||||
// bound methods
|
// bound methods
|
||||||
readonly prepare
|
readonly prepare: ITelegramClient['prepare']
|
||||||
private _connect
|
private _connect
|
||||||
readonly close
|
readonly close: ITelegramClient['close']
|
||||||
readonly notifyLoggedIn
|
readonly notifyLoggedIn: ITelegramClient['notifyLoggedIn']
|
||||||
readonly notifyLoggedOut
|
readonly notifyLoggedOut: ITelegramClient['notifyLoggedOut']
|
||||||
readonly notifyChannelOpened
|
readonly notifyChannelOpened: ITelegramClient['notifyChannelOpened']
|
||||||
readonly notifyChannelClosed
|
readonly notifyChannelClosed: ITelegramClient['notifyChannelClosed']
|
||||||
readonly importSession
|
readonly importSession: ITelegramClient['importSession']
|
||||||
readonly exportSession
|
readonly exportSession: ITelegramClient['exportSession']
|
||||||
readonly handleClientUpdate
|
readonly handleClientUpdate: ITelegramClient['handleClientUpdate']
|
||||||
readonly getApiCrenetials
|
readonly getApiCrenetials: ITelegramClient['getApiCrenetials']
|
||||||
readonly getPoolSize
|
readonly getPoolSize: ITelegramClient['getPoolSize']
|
||||||
readonly getPrimaryDcId
|
readonly getPrimaryDcId: ITelegramClient['getPrimaryDcId']
|
||||||
readonly changePrimaryDc
|
readonly changePrimaryDc: ITelegramClient['changePrimaryDc']
|
||||||
readonly computeSrpParams
|
readonly computeSrpParams: ITelegramClient['computeSrpParams']
|
||||||
readonly computeNewPasswordHash
|
readonly computeNewPasswordHash: ITelegramClient['computeNewPasswordHash']
|
||||||
readonly startUpdatesLoop
|
readonly startUpdatesLoop: ITelegramClient['startUpdatesLoop']
|
||||||
readonly stopUpdatesLoop
|
readonly stopUpdatesLoop: ITelegramClient['stopUpdatesLoop']
|
||||||
|
|
||||||
private _abortController = new AbortController()
|
private _abortController = new AbortController()
|
||||||
readonly stopSignal = this._abortController.signal
|
readonly stopSignal: AbortSignal = this._abortController.signal
|
||||||
|
|
||||||
constructor(readonly options: TelegramWorkerPortOptions) {
|
constructor(readonly options: TelegramWorkerPortOptions) {
|
||||||
this.log = new LogManager('worker')
|
this.log = new LogManager('worker')
|
||||||
|
|
|
@ -60,12 +60,12 @@ class CurrentUserServiceProxy implements PublicPart<CurrentUserService> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeersServiceProxy implements PublicPart<PeersService> {
|
class PeersServiceProxy implements PublicPart<PeersService> {
|
||||||
readonly updatePeersFrom
|
readonly updatePeersFrom: PeersService['updatePeersFrom']
|
||||||
readonly store
|
readonly store: PeersService['store']
|
||||||
readonly getById
|
readonly getById: PeersService['getById']
|
||||||
readonly getByPhone
|
readonly getByPhone: PeersService['getByPhone']
|
||||||
readonly getByUsername
|
readonly getByUsername: PeersService['getByUsername']
|
||||||
readonly getCompleteById
|
readonly getCompleteById: PeersService['getCompleteById']
|
||||||
|
|
||||||
constructor(private _invoker: WorkerInvoker) {
|
constructor(private _invoker: WorkerInvoker) {
|
||||||
const bind = this._invoker.makeBinder<PeersService>('storage-peers')
|
const bind = this._invoker.makeBinder<PeersService>('storage-peers')
|
||||||
|
@ -80,10 +80,10 @@ class PeersServiceProxy implements PublicPart<PeersService> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TelegramStorageProxy implements PublicPart<TelegramStorageManager> {
|
export class TelegramStorageProxy implements PublicPart<TelegramStorageManager> {
|
||||||
readonly self
|
readonly self: CurrentUserServiceProxy
|
||||||
readonly peers
|
readonly peers: PeersServiceProxy
|
||||||
|
|
||||||
readonly clear
|
readonly clear: TelegramStorageManager['clear']
|
||||||
|
|
||||||
constructor(private _invoker: WorkerInvoker) {
|
constructor(private _invoker: WorkerInvoker) {
|
||||||
const bind = this._invoker.makeBinder<TelegramStorageManager>('storage')
|
const bind = this._invoker.makeBinder<TelegramStorageManager>('storage')
|
||||||
|
|
|
@ -24,7 +24,7 @@ export abstract class TelegramWorker<T extends WorkerCustomMethods> {
|
||||||
|
|
||||||
abstract registerWorker(handler: WorkerMessageHandler): RespondFn
|
abstract registerWorker(handler: WorkerMessageHandler): RespondFn
|
||||||
|
|
||||||
readonly pendingAborts = new Map<number, AbortController>()
|
readonly pendingAborts: Map<number, AbortController> = new Map()
|
||||||
|
|
||||||
constructor(readonly params: TelegramWorkerOptions<T>) {
|
constructor(readonly params: TelegramWorkerOptions<T>) {
|
||||||
this.broadcast = this.registerWorker((message, respond) => {
|
this.broadcast = this.registerWorker((message, respond) => {
|
||||||
|
|
|
@ -20,9 +20,8 @@ import type { SessionConnection } from './session-connection.js'
|
||||||
// see https://core.telegram.org/mtproto/security_guidelines
|
// see https://core.telegram.org/mtproto/security_guidelines
|
||||||
// const DH_SAFETY_RANGE = bigInt[2].pow(2048 - 64)
|
// const DH_SAFETY_RANGE = bigInt[2].pow(2048 - 64)
|
||||||
const DH_SAFETY_RANGE = 2n ** (2048n - 64n)
|
const DH_SAFETY_RANGE = 2n ** (2048n - 64n)
|
||||||
const KNOWN_DH_PRIME
|
// eslint-disable-next-line style/max-len
|
||||||
|
const KNOWN_DH_PRIME = 0xC71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5Bn
|
||||||
= 0xC71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5Bn
|
|
||||||
const TWO_POW_2047 = 2n ** 2047n
|
const TWO_POW_2047 = 2n ** 2047n
|
||||||
const TWO_POW_2048 = 2n ** 2048n
|
const TWO_POW_2048 = 2n ** 2048n
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ export class MtClient extends EventEmitter {
|
||||||
/** TL writers map used by the client */
|
/** TL writers map used by the client */
|
||||||
readonly _writerMap: TlWriterMap
|
readonly _writerMap: TlWriterMap
|
||||||
|
|
||||||
readonly _config = new ConfigManager(async () => {
|
readonly _config: ConfigManager = new ConfigManager(async () => {
|
||||||
const res = await this.call({ _: 'help.getConfig' })
|
const res = await this.call({ _: 'help.getConfig' })
|
||||||
|
|
||||||
if (isTlRpcError(res)) throw new Error(`Failed to get config: ${res.errorMessage}`)
|
if (isTlRpcError(res)) throw new Error(`Failed to get config: ${res.errorMessage}`)
|
||||||
|
@ -313,7 +313,7 @@ export class MtClient extends EventEmitter {
|
||||||
*
|
*
|
||||||
* Call {@link connect} to actually connect.
|
* Call {@link connect} to actually connect.
|
||||||
*/
|
*/
|
||||||
prepare() {
|
prepare(): Promise<void> {
|
||||||
return this._prepare.run()
|
return this._prepare.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,40 +92,40 @@ export type PendingMessage =
|
||||||
* all the relevant state
|
* all the relevant state
|
||||||
*/
|
*/
|
||||||
export class MtprotoSession {
|
export class MtprotoSession {
|
||||||
_sessionId = randomLong()
|
_sessionId: Long = randomLong()
|
||||||
|
|
||||||
_authKey: AuthKey
|
_authKey: AuthKey
|
||||||
_authKeyTemp: AuthKey
|
_authKeyTemp: AuthKey
|
||||||
_authKeyTempSecondary: AuthKey
|
_authKeyTempSecondary: AuthKey
|
||||||
|
|
||||||
_timeOffset = 0
|
_timeOffset = 0
|
||||||
_lastMessageId = Long.ZERO
|
_lastMessageId: Long = Long.ZERO
|
||||||
_seqNo = 0
|
_seqNo = 0
|
||||||
|
|
||||||
/// state ///
|
/// state ///
|
||||||
// recent msg ids
|
// recent msg ids
|
||||||
recentOutgoingMsgIds = new LruSet<Long>(1000, true)
|
recentOutgoingMsgIds: LruSet<Long> = new LruSet(1000, true)
|
||||||
recentIncomingMsgIds = new LruSet<Long>(1000, true)
|
recentIncomingMsgIds: LruSet<Long> = new LruSet(1000, true)
|
||||||
|
|
||||||
// queues
|
// queues
|
||||||
queuedRpc = new Deque<PendingRpc>()
|
queuedRpc: Deque<PendingRpc> = new Deque()
|
||||||
queuedAcks: Long[] = []
|
queuedAcks: Long[] = []
|
||||||
queuedStateReq: Long[] = []
|
queuedStateReq: Long[] = []
|
||||||
queuedResendReq: Long[] = []
|
queuedResendReq: Long[] = []
|
||||||
queuedCancelReq: Long[] = []
|
queuedCancelReq: Long[] = []
|
||||||
getStateSchedule = new SortedArray<PendingRpc>([], (a, b) => a.getState! - b.getState!)
|
getStateSchedule: SortedArray<PendingRpc> = new SortedArray<PendingRpc>([], (a, b) => a.getState! - b.getState!)
|
||||||
|
|
||||||
chains = new Map<string | number, Long>()
|
chains: Map<string | number, Long> = new Map()
|
||||||
chainsPendingFails = new Map<string | number, SortedArray<PendingRpc>>()
|
chainsPendingFails: Map<string | number, SortedArray<PendingRpc>> = new Map()
|
||||||
|
|
||||||
// requests info
|
// requests info
|
||||||
pendingMessages = new LongMap<PendingMessage>()
|
pendingMessages: LongMap<PendingMessage> = new LongMap()
|
||||||
destroySessionIdToMsgId = new LongMap<Long>()
|
destroySessionIdToMsgId: LongMap<Long> = new LongMap()
|
||||||
|
|
||||||
lastPingRtt = Number.NaN
|
lastPingRtt: number = Number.NaN
|
||||||
lastPingTime = 0
|
lastPingTime = 0
|
||||||
lastPingMsgId = Long.ZERO
|
lastPingMsgId: Long = Long.ZERO
|
||||||
lastSessionCreatedUid = Long.ZERO
|
lastSessionCreatedUid: Long = Long.ZERO
|
||||||
|
|
||||||
initConnectionCalled = false
|
initConnectionCalled = false
|
||||||
authorizationPending = false
|
authorizationPending = false
|
||||||
|
@ -176,7 +176,7 @@ export class MtprotoSession {
|
||||||
this._authKeyTempSecondary.reset()
|
this._authKeyTempSecondary.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTimeOffset(offset: number) {
|
updateTimeOffset(offset: number): void {
|
||||||
this.log.debug('time offset updated: %d', offset)
|
this.log.debug('time offset updated: %d', offset)
|
||||||
this._timeOffset = offset
|
this._timeOffset = offset
|
||||||
// lastMessageId was generated with (potentially) wrong time
|
// lastMessageId was generated with (potentially) wrong time
|
||||||
|
@ -330,7 +330,7 @@ export class MtprotoSession {
|
||||||
return messageId
|
return messageId
|
||||||
}
|
}
|
||||||
|
|
||||||
onTransportFlood(callback: () => void) {
|
onTransportFlood(callback: () => void): number | undefined {
|
||||||
if (this.current429Timeout) return // already waiting
|
if (this.current429Timeout) return // already waiting
|
||||||
|
|
||||||
// all active queries must be resent after a timeout
|
// all active queries must be resent after a timeout
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class MultiSessionConnection extends EventEmitter {
|
||||||
|
|
||||||
protected _connections: SessionConnection[] = []
|
protected _connections: SessionConnection[] = []
|
||||||
|
|
||||||
setCount(count: number, connect = this.params.isMainConnection): void {
|
setCount(count: number, connect: boolean = this.params.isMainConnection): void {
|
||||||
this._count = count
|
this._count = count
|
||||||
|
|
||||||
this._updateConnections(connect)
|
this._updateConnections(connect)
|
||||||
|
|
|
@ -456,7 +456,7 @@ export class DcConnectionManager {
|
||||||
this.main.setCount(count)
|
this.main.setCount(count)
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroy() {
|
async destroy(): Promise<void> {
|
||||||
await this.main.destroy()
|
await this.main.destroy()
|
||||||
await this.upload.destroy()
|
await this.upload.destroy()
|
||||||
await this.download.destroy()
|
await this.download.destroy()
|
||||||
|
@ -469,15 +469,15 @@ export class DcConnectionManager {
|
||||||
* Class that manages all connections to Telegram servers.
|
* Class that manages all connections to Telegram servers.
|
||||||
*/
|
*/
|
||||||
export class NetworkManager {
|
export class NetworkManager {
|
||||||
readonly _log
|
readonly _log: Logger
|
||||||
readonly _storage
|
readonly _storage: StorageManager
|
||||||
|
|
||||||
readonly _initConnectionParams: tl.RawInitConnectionRequest
|
readonly _initConnectionParams: tl.RawInitConnectionRequest
|
||||||
readonly _transportFactory: TransportFactory
|
readonly _transportFactory: TransportFactory
|
||||||
readonly _reconnectionStrategy: ReconnectionStrategy<PersistentConnectionParams>
|
readonly _reconnectionStrategy: ReconnectionStrategy<PersistentConnectionParams>
|
||||||
readonly _connectionCount: ConnectionCountDelegate
|
readonly _connectionCount: ConnectionCountDelegate
|
||||||
|
|
||||||
protected readonly _dcConnections = new Map<number, DcConnectionManager>()
|
protected readonly _dcConnections: Map<number, DcConnectionManager> = new Map()
|
||||||
protected _primaryDc?: DcConnectionManager
|
protected _primaryDc?: DcConnectionManager
|
||||||
|
|
||||||
private _updateHandler: (upd: tl.TypeUpdates, fromClient: boolean) => void
|
private _updateHandler: (upd: tl.TypeUpdates, fromClient: boolean) => void
|
||||||
|
@ -866,7 +866,7 @@ export class NetworkManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPoolSize(kind: ConnectionKind, dcId?: number) {
|
getPoolSize(kind: ConnectionKind, dcId?: number): number {
|
||||||
const dc = dcId ? this._dcConnections.get(dcId) : this._primaryDc
|
const dc = dcId ? this._dcConnections.get(dcId) : this._primaryDc
|
||||||
|
|
||||||
if (!dc) {
|
if (!dc) {
|
||||||
|
@ -882,7 +882,7 @@ export class NetworkManager {
|
||||||
return dc[kind].getPoolSize()
|
return dc[kind].getPoolSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
getPrimaryDcId() {
|
getPrimaryDcId(): number {
|
||||||
if (!this._primaryDc) throw new MtcuteError('Not connected to any DC')
|
if (!this._primaryDc) throw new MtcuteError('Not connected to any DC')
|
||||||
|
|
||||||
return this._primaryDc.dcId
|
return this._primaryDc.dcId
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { mtp } from '@mtcute/tl'
|
||||||
export class ServerSaltManager {
|
export class ServerSaltManager {
|
||||||
private _futureSalts: mtp.RawMt_future_salt[] = []
|
private _futureSalts: mtp.RawMt_future_salt[] = []
|
||||||
|
|
||||||
currentSalt = Long.ZERO
|
currentSalt: Long = Long.ZERO
|
||||||
|
|
||||||
isFetching = false
|
isFetching = false
|
||||||
|
|
||||||
|
|
|
@ -259,7 +259,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
this.emit('error', error)
|
this.emit('error', error)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onConnectionUsable() {
|
protected onConnectionUsable(): void {
|
||||||
super.onConnectionUsable()
|
super.onConnectionUsable()
|
||||||
|
|
||||||
if (this.params.withUpdates) {
|
if (this.params.withUpdates) {
|
||||||
|
@ -1499,7 +1499,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _onInactivityTimeout() {
|
protected _onInactivityTimeout(): void {
|
||||||
// we should send all pending acks and other service messages
|
// we should send all pending acks and other service messages
|
||||||
// before dropping the connection
|
// before dropping the connection
|
||||||
// additionally, if we are still waiting for some rpc results,
|
// additionally, if we are still waiting for some rpc results,
|
||||||
|
|
|
@ -62,7 +62,7 @@ export class PaddedIntermediatePacketCodec extends IntermediatePacketCodec {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _crypto!: ICryptoProvider
|
private _crypto!: ICryptoProvider
|
||||||
setup?(crypto: ICryptoProvider) {
|
setup?(crypto: ICryptoProvider): void {
|
||||||
this._crypto = crypto
|
this._crypto = crypto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { concatBuffers } from '../../utils/index.js'
|
||||||
* multiple transport packets.
|
* multiple transport packets.
|
||||||
*/
|
*/
|
||||||
export abstract class StreamedCodec extends EventEmitter {
|
export abstract class StreamedCodec extends EventEmitter {
|
||||||
protected _stream = new Uint8Array(0)
|
protected _stream: Uint8Array = new Uint8Array(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should return whether a full packet is available
|
* Should return whether a full packet is available
|
||||||
|
|
|
@ -11,5 +11,5 @@ export class MemoryStorageDriver implements IStorageDriver {
|
||||||
return this.states.get(repo) as T
|
return this.states.get(repo) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
load() {}
|
load(): void {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ export { MemoryStorageDriver } from './driver.js'
|
||||||
* or if you know exactly what you're doing.
|
* or if you know exactly what you're doing.
|
||||||
*/
|
*/
|
||||||
export class MemoryStorage implements IMtStorageProvider, ITelegramStorageProvider {
|
export class MemoryStorage implements IMtStorageProvider, ITelegramStorageProvider {
|
||||||
readonly driver = new MemoryStorageDriver()
|
readonly driver: MemoryStorageDriver = new MemoryStorageDriver()
|
||||||
readonly kv = new MemoryKeyValueRepository(this.driver)
|
readonly kv: MemoryKeyValueRepository = new MemoryKeyValueRepository(this.driver)
|
||||||
readonly authKeys = new MemoryAuthKeysRepository(this.driver)
|
readonly authKeys: MemoryAuthKeysRepository = new MemoryAuthKeysRepository(this.driver)
|
||||||
readonly peers = new MemoryPeersRepository(this.driver)
|
readonly peers: MemoryPeersRepository = new MemoryPeersRepository(this.driver)
|
||||||
readonly refMessages = new MemoryRefMessagesRepository(this.driver)
|
readonly refMessages: MemoryRefMessagesRepository = new MemoryRefMessagesRepository(this.driver)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ interface AuthKeysState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryAuthKeysRepository implements IAuthKeysRepository {
|
export class MemoryAuthKeysRepository implements IAuthKeysRepository {
|
||||||
readonly state
|
readonly state: AuthKeysState
|
||||||
constructor(readonly _driver: MemoryStorageDriver) {
|
constructor(readonly _driver: MemoryStorageDriver) {
|
||||||
this.state = this._driver.getState<AuthKeysState>('authKeys', () => ({
|
this.state = this._driver.getState('authKeys', () => ({
|
||||||
authKeys: new Map(),
|
authKeys: new Map(),
|
||||||
authKeysTemp: new Map(),
|
authKeysTemp: new Map(),
|
||||||
authKeysTempExpiry: new Map(),
|
authKeysTempExpiry: new Map(),
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { IKeyValueRepository } from '../../repository/key-value.js'
|
||||||
import type { MemoryStorageDriver } from '../driver.js'
|
import type { MemoryStorageDriver } from '../driver.js'
|
||||||
|
|
||||||
export class MemoryKeyValueRepository implements IKeyValueRepository {
|
export class MemoryKeyValueRepository implements IKeyValueRepository {
|
||||||
readonly state
|
readonly state: Map<string, Uint8Array>
|
||||||
constructor(readonly _driver: MemoryStorageDriver) {
|
constructor(readonly _driver: MemoryStorageDriver) {
|
||||||
this.state = this._driver.getState<Map<string, Uint8Array>>('kv', () => new Map())
|
this.state = this._driver.getState<Map<string, Uint8Array>>('kv', () => new Map())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ interface PeersState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryPeersRepository implements IPeersRepository {
|
export class MemoryPeersRepository implements IPeersRepository {
|
||||||
readonly state
|
readonly state: PeersState
|
||||||
constructor(readonly _driver: MemoryStorageDriver) {
|
constructor(readonly _driver: MemoryStorageDriver) {
|
||||||
this.state = this._driver.getState<PeersState>('peers', () => ({
|
this.state = this._driver.getState('peers', () => ({
|
||||||
entities: new Map(),
|
entities: new Map(),
|
||||||
usernameIndex: new Map(),
|
usernameIndex: new Map(),
|
||||||
phoneIndex: new Map(),
|
phoneIndex: new Map(),
|
||||||
|
|
|
@ -6,9 +6,9 @@ interface RefMessagesState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryRefMessagesRepository implements IReferenceMessagesRepository {
|
export class MemoryRefMessagesRepository implements IReferenceMessagesRepository {
|
||||||
readonly state
|
readonly state: RefMessagesState
|
||||||
constructor(readonly _driver: MemoryStorageDriver) {
|
constructor(readonly _driver: MemoryStorageDriver) {
|
||||||
this.state = this._driver.getState<RefMessagesState>('refMessages', () => ({
|
this.state = this._driver.getState('refMessages', () => ({
|
||||||
refs: new Map(),
|
refs: new Map(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ export { BaseSqliteStorageDriver }
|
||||||
export * from './types.js'
|
export * from './types.js'
|
||||||
|
|
||||||
export class BaseSqliteStorage implements IMtStorageProvider, ITelegramStorageProvider {
|
export class BaseSqliteStorage implements IMtStorageProvider, ITelegramStorageProvider {
|
||||||
readonly authKeys
|
readonly authKeys: SqliteAuthKeysRepository
|
||||||
readonly kv
|
readonly kv: SqliteKeyValueRepository
|
||||||
readonly refMessages
|
readonly refMessages: SqliteRefMessagesRepository
|
||||||
readonly peers
|
readonly peers: SqlitePeersRepository
|
||||||
|
|
||||||
constructor(readonly driver: BaseSqliteStorageDriver) {
|
constructor(readonly driver: BaseSqliteStorageDriver) {
|
||||||
this.authKeys = new SqliteAuthKeysRepository(this.driver)
|
this.authKeys = new SqliteAuthKeysRepository(this.driver)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { AuthKeysService } from './service/auth-keys.js'
|
||||||
import type { ServiceOptions } from './service/base.js'
|
import type { ServiceOptions } from './service/base.js'
|
||||||
import { DefaultDcsService } from './service/default-dcs.js'
|
import { DefaultDcsService } from './service/default-dcs.js'
|
||||||
import { FutureSaltsService } from './service/future-salts.js'
|
import { FutureSaltsService } from './service/future-salts.js'
|
||||||
|
import type { IStorageDriver } from './driver.js'
|
||||||
|
|
||||||
interface StorageManagerOptions {
|
interface StorageManagerOptions {
|
||||||
provider: IMtStorageProvider
|
provider: IMtStorageProvider
|
||||||
|
@ -41,12 +42,12 @@ export interface StorageManagerExtraOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StorageManager {
|
export class StorageManager {
|
||||||
readonly provider
|
readonly provider: IMtStorageProvider
|
||||||
readonly driver
|
readonly driver: IStorageDriver
|
||||||
readonly log
|
readonly log: Logger
|
||||||
readonly dcs
|
readonly dcs: DefaultDcsService
|
||||||
readonly salts
|
readonly salts: FutureSaltsService
|
||||||
readonly keys
|
readonly keys: AuthKeysService
|
||||||
|
|
||||||
constructor(readonly options: StorageManagerOptions & StorageManagerExtraOptions) {
|
constructor(readonly options: StorageManagerOptions & StorageManagerExtraOptions) {
|
||||||
this.provider = this.options.provider
|
this.provider = this.options.provider
|
||||||
|
@ -87,7 +88,7 @@ export class StorageManager {
|
||||||
await this.driver.save?.()
|
await this.driver.save?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear(withAuthKeys = false) {
|
async clear(withAuthKeys = false): Promise<void> {
|
||||||
if (withAuthKeys) {
|
if (withAuthKeys) {
|
||||||
await this.provider.authKeys.deleteAll()
|
await this.provider.authKeys.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { ICryptoProvider } from './crypto/abstract.js'
|
||||||
/**
|
/**
|
||||||
* Get the minimum number of bits required to represent a number
|
* Get the minimum number of bits required to represent a number
|
||||||
*/
|
*/
|
||||||
export function bigIntBitLength(n: bigint) {
|
export function bigIntBitLength(n: bigint): number {
|
||||||
// not the fastest way, but at least not .toString(2) and not too complex
|
// not the fastest way, but at least not .toString(2) and not too complex
|
||||||
// taken from: https://stackoverflow.com/a/76616288/22656950
|
// taken from: https://stackoverflow.com/a/76616288/22656950
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ export function buffersEqual(a: Uint8Array, b: Uint8Array): boolean {
|
||||||
* @param start Start offset
|
* @param start Start offset
|
||||||
* @param end End offset
|
* @param end End offset
|
||||||
*/
|
*/
|
||||||
export function cloneBuffer(buf: Uint8Array, start = 0, end = buf.length): Uint8Array {
|
export function cloneBuffer(buf: Uint8Array, start = 0, end: number = buf.length): Uint8Array {
|
||||||
const ret = new Uint8Array(end - start)
|
const ret = new Uint8Array(end - start)
|
||||||
ret.set(buf.subarray(start, end))
|
ret.set(buf.subarray(start, end))
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ export function dataViewFromBuffer(buf: Uint8Array): DataView {
|
||||||
/**
|
/**
|
||||||
* Reverse a buffer (or a part of it) into a new buffer
|
* Reverse a buffer (or a part of it) into a new buffer
|
||||||
*/
|
*/
|
||||||
export function bufferToReversed(buf: Uint8Array, start = 0, end = buf.length): Uint8Array {
|
export function bufferToReversed(buf: Uint8Array, start = 0, end: number = buf.length): Uint8Array {
|
||||||
const len = end - start
|
const len = end - start
|
||||||
const ret = new Uint8Array(len)
|
const ret = new Uint8Array(len)
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,11 @@ export interface ICryptoProvider {
|
||||||
export abstract class BaseCryptoProvider {
|
export abstract class BaseCryptoProvider {
|
||||||
abstract randomFill(buf: Uint8Array): void
|
abstract randomFill(buf: Uint8Array): void
|
||||||
|
|
||||||
factorizePQ(pq: Uint8Array) {
|
factorizePQ(pq: Uint8Array): [Uint8Array, Uint8Array] {
|
||||||
return factorizePQSync(this as unknown as ICryptoProvider, pq)
|
return factorizePQSync(this as unknown as ICryptoProvider, pq)
|
||||||
}
|
}
|
||||||
|
|
||||||
randomBytes(size: number) {
|
randomBytes(size: number): Uint8Array {
|
||||||
const buf = new Uint8Array(size)
|
const buf = new Uint8Array(size)
|
||||||
this.randomFill(buf)
|
this.randomFill(buf)
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ export class Deque<T> {
|
||||||
protected _capacity: number
|
protected _capacity: number
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly maxLength = Infinity,
|
readonly maxLength: number = Infinity,
|
||||||
minCapacity = maxLength === Infinity ? MIN_INITIAL_CAPACITY : maxLength,
|
minCapacity: number = maxLength === Infinity ? MIN_INITIAL_CAPACITY : maxLength,
|
||||||
) {
|
) {
|
||||||
let capacity = minCapacity
|
let capacity = minCapacity
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export class Deque<T> {
|
||||||
this._capacity = capacity
|
this._capacity = capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _resize() {
|
protected _resize(): void {
|
||||||
const p = this._head
|
const p = this._head
|
||||||
const n = this._capacity
|
const n = this._capacity
|
||||||
const r = n - p // number of elements to the right of the head
|
const r = n - p // number of elements to the right of the head
|
||||||
|
|
|
@ -39,7 +39,12 @@ export function throttle(func: () => void, delay: number): ThrottledFunction {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export function asyncResettable<T extends(...args: any[]) => Promise<any>>(func: T) {
|
export function asyncResettable<T extends(...args: any[]) => Promise<any>>(func: T): {
|
||||||
|
run: T
|
||||||
|
finished: () => boolean
|
||||||
|
wait: () => Promise<any> | null
|
||||||
|
reset: () => void
|
||||||
|
} {
|
||||||
let runningPromise: Promise<any> | null = null
|
let runningPromise: Promise<any> | null = null
|
||||||
let finished = false
|
let finished = false
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { tl } from '@mtcute/tl'
|
||||||
import { isPresent } from '../type-assertions.js'
|
import { isPresent } from '../type-assertions.js'
|
||||||
import { assertNever } from '../../types/utils.js'
|
import { assertNever } from '../../types/utils.js'
|
||||||
|
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,12 +11,12 @@ import { deeplinkBuilder } from './common.js'
|
||||||
*
|
*
|
||||||
* Used to link to bots with a start parameter
|
* Used to link to bots with a start parameter
|
||||||
*/
|
*/
|
||||||
export const botStart = deeplinkBuilder<{
|
export const botStart: Deeplink<{
|
||||||
/** Bot username */
|
/** Bot username */
|
||||||
username: string
|
username: string
|
||||||
/** Start parameter */
|
/** Start parameter */
|
||||||
parameter: string
|
parameter: string
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ username, parameter }) => ['resolve', { domain: username, start: parameter }],
|
internalBuild: ({ username, parameter }) => ['resolve', { domain: username, start: parameter }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'resolve') return null
|
if (path !== 'resolve') return null
|
||||||
|
@ -137,14 +138,14 @@ function parseBotAdmin(rights: string | null): BotAdminRight[] | undefined {
|
||||||
* Note that the user is still free to choose which rights to grant, and
|
* Note that the user is still free to choose which rights to grant, and
|
||||||
* whether to grant them at all.
|
* whether to grant them at all.
|
||||||
*/
|
*/
|
||||||
export const botAddToGroup = deeplinkBuilder<{
|
export const botAddToGroup: Deeplink<{
|
||||||
/** Bot username */
|
/** Bot username */
|
||||||
bot: string
|
bot: string
|
||||||
/** If specified, the client will call `/start parameter` on the bot once the bot has been added */
|
/** If specified, the client will call `/start parameter` on the bot once the bot has been added */
|
||||||
parameter?: string
|
parameter?: string
|
||||||
/** Admin rights to request */
|
/** Admin rights to request */
|
||||||
admin?: BotAdminRight[]
|
admin?: BotAdminRight[]
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ bot, parameter, admin }) => [
|
internalBuild: ({ bot, parameter, admin }) => [
|
||||||
'resolve',
|
'resolve',
|
||||||
{ domain: bot, startgroup: parameter ?? true, admin: normalizeBotAdmin(admin) },
|
{ domain: bot, startgroup: parameter ?? true, admin: normalizeBotAdmin(admin) },
|
||||||
|
@ -193,12 +194,12 @@ export const botAddToGroup = deeplinkBuilder<{
|
||||||
* Note that the user is still free to choose which rights to grant, and
|
* Note that the user is still free to choose which rights to grant, and
|
||||||
* whether to grant them at all.
|
* whether to grant them at all.
|
||||||
*/
|
*/
|
||||||
export const botAddToChannel = deeplinkBuilder<{
|
export const botAddToChannel: Deeplink<{
|
||||||
/** Bot username */
|
/** Bot username */
|
||||||
bot: string
|
bot: string
|
||||||
/** Admin rights to request */
|
/** Admin rights to request */
|
||||||
admin?: BotAdminRight[]
|
admin?: BotAdminRight[]
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ bot, admin }) => [
|
internalBuild: ({ bot, admin }) => [
|
||||||
'resolve',
|
'resolve',
|
||||||
{ domain: bot, startchannel: true, admin: normalizeBotAdmin(admin) },
|
{ domain: bot, startchannel: true, admin: normalizeBotAdmin(admin) },
|
||||||
|
@ -240,12 +241,12 @@ export const botAddToChannel = deeplinkBuilder<{
|
||||||
*
|
*
|
||||||
* Used to share games.
|
* Used to share games.
|
||||||
*/
|
*/
|
||||||
export const botGame = deeplinkBuilder<{
|
export const botGame: Deeplink<{
|
||||||
/** Bot username */
|
/** Bot username */
|
||||||
bot: string
|
bot: string
|
||||||
/** Game short name */
|
/** Game short name */
|
||||||
game: string
|
game: string
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ bot, game }) => ['resolve', { domain: bot, game }],
|
internalBuild: ({ bot, game }) => ['resolve', { domain: bot, game }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'resolve') return null
|
if (path !== 'resolve') return null
|
||||||
|
@ -280,14 +281,14 @@ export const botGame = deeplinkBuilder<{
|
||||||
* and a single bot can offer multiple named web apps, distinguished by
|
* and a single bot can offer multiple named web apps, distinguished by
|
||||||
* their `short_name`.
|
* their `short_name`.
|
||||||
*/
|
*/
|
||||||
export const botWebApp = deeplinkBuilder<{
|
export const botWebApp: Deeplink<{
|
||||||
/** Bot username */
|
/** Bot username */
|
||||||
bot: string
|
bot: string
|
||||||
/** App short name */
|
/** App short name */
|
||||||
app: string
|
app: string
|
||||||
/** Parameter to be passed by the client to messages.requestAppWebView as `start_param` */
|
/** Parameter to be passed by the client to messages.requestAppWebView as `start_param` */
|
||||||
parameter?: string
|
parameter?: string
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ bot, app, parameter }) => ['resolve', { domain: bot, appname: app, startapp: parameter }],
|
internalBuild: ({ bot, app, parameter }) => ['resolve', { domain: bot, appname: app, startapp: parameter }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'resolve') return null
|
if (path !== 'resolve') return null
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,7 +7,7 @@ import { deeplinkBuilder } from './common.js'
|
||||||
* Used to join video/voice chats in groups, and livestreams in channels.
|
* Used to join video/voice chats in groups, and livestreams in channels.
|
||||||
* Such links are generated using phone.exportGroupCallInvite.
|
* Such links are generated using phone.exportGroupCallInvite.
|
||||||
*/
|
*/
|
||||||
export const videoChat = deeplinkBuilder<{
|
export const videoChat: Deeplink<{
|
||||||
username: string
|
username: string
|
||||||
/**
|
/**
|
||||||
* Invite hash exported if the `can_self_unmute` flag is set when calling `phone.exportGroupCallInvite`:
|
* Invite hash exported if the `can_self_unmute` flag is set when calling `phone.exportGroupCallInvite`:
|
||||||
|
@ -15,7 +16,7 @@ export const videoChat = deeplinkBuilder<{
|
||||||
*/
|
*/
|
||||||
inviteHash?: string
|
inviteHash?: string
|
||||||
isLivestream?: boolean
|
isLivestream?: boolean
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ username, inviteHash, isLivestream }) => [
|
internalBuild: ({ username, inviteHash, isLivestream }) => [
|
||||||
'resolve',
|
'resolve',
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +6,7 @@ import { deeplinkBuilder } from './common.js'
|
||||||
*
|
*
|
||||||
* Used to invite users to private groups and channels
|
* Used to invite users to private groups and channels
|
||||||
*/
|
*/
|
||||||
export const chatInvite = deeplinkBuilder<{ hash: string }>({
|
export const chatInvite: Deeplink<{ hash: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ hash }) => ['join', { invite: hash }],
|
internalBuild: ({ hash }) => ['join', { invite: hash }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'join') return null
|
if (path !== 'join') return null
|
||||||
|
@ -32,7 +33,7 @@ export const chatInvite = deeplinkBuilder<{ hash: string }>({
|
||||||
/**
|
/**
|
||||||
* Chat folder links
|
* Chat folder links
|
||||||
*/
|
*/
|
||||||
export const chatFolder = deeplinkBuilder<{ slug: string }>({
|
export const chatFolder: Deeplink<{ slug: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ slug }) => ['addlist', { slug }],
|
internalBuild: ({ slug }) => ['addlist', { slug }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'addlist') return null
|
if (path !== 'addlist') return null
|
||||||
|
@ -76,127 +77,126 @@ function parseMediaTimestamp(timestamp: string) {
|
||||||
*
|
*
|
||||||
* Note: `channelId` is a non-marked channel ID
|
* Note: `channelId` is a non-marked channel ID
|
||||||
*/
|
*/
|
||||||
export const message = deeplinkBuilder<
|
export const message: Deeplink<({ username: string } | { channelId: number }) & {
|
||||||
({ username: string } | { channelId: number }) & {
|
/** Message ID */
|
||||||
/** Message ID */
|
id: number
|
||||||
id: number
|
/** Thread ID */
|
||||||
/** Thread ID */
|
threadId?: number
|
||||||
threadId?: number
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For comments, `id` will contain the message ID of the channel message that started
|
* For comments, `id` will contain the message ID of the channel message that started
|
||||||
* the comment section and this field will contain the message ID of the comment in
|
* the comment section and this field will contain the message ID of the comment in
|
||||||
* the discussion group.
|
* the discussion group.
|
||||||
*/
|
*/
|
||||||
commentId?: number
|
commentId?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timestamp at which to start playing the media file present
|
* Timestamp at which to start playing the media file present
|
||||||
* in the body or in the webpage preview of the message
|
* in the body or in the webpage preview of the message
|
||||||
*/
|
*/
|
||||||
mediaTimestamp?: number
|
mediaTimestamp?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this is a link to a specific message in the album or to the entire album
|
* Whether this is a link to a specific message in the album or to the entire album
|
||||||
*/
|
*/
|
||||||
single?: boolean
|
single?: boolean
|
||||||
}
|
}
|
||||||
>({
|
> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: (params) => {
|
internalBuild: (params) => {
|
||||||
const common = {
|
const common = {
|
||||||
post: params.id,
|
post: params.id,
|
||||||
thread: params.threadId,
|
thread: params.threadId,
|
||||||
comment: params.commentId,
|
comment: params.commentId,
|
||||||
t: params.mediaTimestamp,
|
t: params.mediaTimestamp,
|
||||||
single: params.single ? '' : undefined,
|
single: params.single ? '' : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('username' in params) {
|
if ('username' in params) {
|
||||||
return ['resolve', { domain: params.username, ...common }]
|
return ['resolve', { domain: params.username, ...common }]
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['privatepost', { channel: params.channelId, ...common }]
|
return ['privatepost', { channel: params.channelId, ...common }]
|
||||||
},
|
},
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
const common = {
|
const common = {
|
||||||
id: Number(query.get('post')),
|
id: Number(query.get('post')),
|
||||||
threadId: query.has('thread') ? Number(query.get('thread')) : undefined,
|
threadId: query.has('thread') ? Number(query.get('thread')) : undefined,
|
||||||
commentId: query.has('comment') ? Number(query.get('comment')) : undefined,
|
commentId: query.has('comment') ? Number(query.get('comment')) : undefined,
|
||||||
mediaTimestamp: query.has('t') ? parseMediaTimestamp(query.get('t')!) : undefined,
|
mediaTimestamp: query.has('t') ? parseMediaTimestamp(query.get('t')!) : undefined,
|
||||||
single: query.has('single'),
|
single: query.has('single'),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path === 'resolve') {
|
if (path === 'resolve') {
|
||||||
const username = query.get('domain')
|
const username = query.get('domain')
|
||||||
if (!username) return null
|
if (!username) return null
|
||||||
|
|
||||||
return { username, ...common }
|
return { username, ...common }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path === 'privatepost') {
|
if (path === 'privatepost') {
|
||||||
const channelId = Number(query.get('channel'))
|
const channelId = Number(query.get('channel'))
|
||||||
if (!channelId) return null
|
if (!channelId) return null
|
||||||
|
|
||||||
return { channelId, ...common }
|
return { channelId, ...common }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
|
|
||||||
externalBuild: (params) => {
|
externalBuild: (params) => {
|
||||||
const common = {
|
const common = {
|
||||||
comment: params.commentId,
|
comment: params.commentId,
|
||||||
t: params.mediaTimestamp,
|
t: params.mediaTimestamp,
|
||||||
single: params.single ? '' : undefined,
|
single: params.single ? '' : undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('username' in params) {
|
if ('username' in params) {
|
||||||
if (params.threadId) {
|
if (params.threadId) {
|
||||||
return [`${params.username}/${params.threadId}/${params.id}`, common]
|
return [`${params.username}/${params.threadId}/${params.id}`, common]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [`${params.username}/${params.id}`, common]
|
return [`${params.username}/${params.id}`, common]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.threadId) {
|
if (params.threadId) {
|
||||||
return [`c/${params.channelId}/${params.threadId}/${params.id}`, common]
|
return [`c/${params.channelId}/${params.threadId}/${params.id}`, common]
|
||||||
}
|
}
|
||||||
|
|
||||||
return [`c/${params.channelId}/${params.id}`, common]
|
return [`c/${params.channelId}/${params.id}`, common]
|
||||||
},
|
},
|
||||||
externalParse: (path, query) => {
|
externalParse: (path, query) => {
|
||||||
const chunks = path.split('/')
|
const chunks = path.split('/')
|
||||||
|
|
||||||
if (chunks.length < 2) return null
|
if (chunks.length < 2) return null
|
||||||
|
|
||||||
const id = Number(chunks[chunks.length - 1])
|
const id = Number(chunks[chunks.length - 1])
|
||||||
if (Number.isNaN(id)) return null
|
if (Number.isNaN(id)) return null
|
||||||
|
|
||||||
const common = {
|
const common = {
|
||||||
id,
|
id,
|
||||||
commentId: query.has('comment') ? Number(query.get('comment')) : undefined,
|
commentId: query.has('comment') ? Number(query.get('comment')) : undefined,
|
||||||
mediaTimestamp: query.has('t') ? parseMediaTimestamp(query.get('t')!) : undefined,
|
mediaTimestamp: query.has('t') ? parseMediaTimestamp(query.get('t')!) : undefined,
|
||||||
single: query.has('single'),
|
single: query.has('single'),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunks[0] === 'c') {
|
if (chunks[0] === 'c') {
|
||||||
const channelId = Number(chunks[1])
|
const channelId = Number(chunks[1])
|
||||||
if (Number.isNaN(channelId)) return null
|
if (Number.isNaN(channelId)) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
channelId,
|
channelId,
|
||||||
threadId: chunks[3] ? Number(chunks[2]) : undefined,
|
threadId: chunks[3] ? Number(chunks[2]) : undefined,
|
||||||
...common,
|
...common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = chunks[0]
|
const username = chunks[0]
|
||||||
if (username[0] === '+') return null
|
if (username[0] === '+') return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
username,
|
username,
|
||||||
threadId: chunks[2] ? Number(chunks[1]) : undefined,
|
threadId: chunks[2] ? Number(chunks[1]) : undefined,
|
||||||
...common,
|
...common,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +6,7 @@ import { deeplinkBuilder } from './common.js'
|
||||||
*
|
*
|
||||||
* Used to share a prepared message and URL into a chosen chat's text field.
|
* Used to share a prepared message and URL into a chosen chat's text field.
|
||||||
*/
|
*/
|
||||||
export const share = deeplinkBuilder<{ url: string, text?: string }>({
|
export const share: Deeplink<{ url: string, text?: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ url, text }) => ['msg_url', { url, text }],
|
internalBuild: ({ url, text }) => ['msg_url', { url, text }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'msg_url') return null
|
if (path !== 'msg_url') return null
|
||||||
|
@ -35,7 +36,7 @@ export const share = deeplinkBuilder<{ url: string, text?: string }>({
|
||||||
*
|
*
|
||||||
* Used by users to boost channels, granting them the ability to post stories.
|
* Used by users to boost channels, granting them the ability to post stories.
|
||||||
*/
|
*/
|
||||||
export const boost = deeplinkBuilder<{ username: string } | { channelId: number }>({
|
export const boost: Deeplink<{ username: string } | { channelId: number }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: (params) => {
|
internalBuild: (params) => {
|
||||||
if ('username' in params) {
|
if ('username' in params) {
|
||||||
return ['boost', { domain: params.username }]
|
return ['boost', { domain: params.username }]
|
||||||
|
@ -86,7 +87,7 @@ export const boost = deeplinkBuilder<{ username: string } | { channelId: number
|
||||||
/**
|
/**
|
||||||
* Link to a shared folder (chat list)
|
* Link to a shared folder (chat list)
|
||||||
*/
|
*/
|
||||||
export const folder = deeplinkBuilder<{ slug: string }>({
|
export const folder: Deeplink<{ slug: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
// tg://addlist?slug=XXX
|
// tg://addlist?slug=XXX
|
||||||
internalBuild: ({ slug }) => ['addlist', { slug }],
|
internalBuild: ({ slug }) => ['addlist', { slug }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MTProxy links
|
* MTProxy links
|
||||||
*/
|
*/
|
||||||
export const mtproxy = deeplinkBuilder<{
|
export const mtproxy: Deeplink<{
|
||||||
server: string
|
server: string
|
||||||
port: number
|
port: number
|
||||||
secret: string
|
secret: string
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: params => ['proxy', params],
|
internalBuild: params => ['proxy', params],
|
||||||
externalBuild: params => ['proxy', params],
|
externalBuild: params => ['proxy', params],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
|
@ -37,12 +38,12 @@ export const mtproxy = deeplinkBuilder<{
|
||||||
/**
|
/**
|
||||||
* Socks5 proxy links
|
* Socks5 proxy links
|
||||||
*/
|
*/
|
||||||
export const socks5 = deeplinkBuilder<{
|
export const socks5: Deeplink<{
|
||||||
server: string
|
server: string
|
||||||
port: number
|
port: number
|
||||||
user?: string
|
user?: string
|
||||||
pass?: string
|
pass?: string
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: params => ['socks', params],
|
internalBuild: params => ['socks', params],
|
||||||
externalBuild: params => ['socks', params],
|
externalBuild: params => ['socks', params],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,10 +6,10 @@ import { deeplinkBuilder } from './common.js'
|
||||||
*
|
*
|
||||||
* Used to import stickersets or custom emoji stickersets
|
* Used to import stickersets or custom emoji stickersets
|
||||||
*/
|
*/
|
||||||
export const stickerset = deeplinkBuilder<{
|
export const stickerset: Deeplink<{
|
||||||
slug: string
|
slug: string
|
||||||
emoji?: boolean
|
emoji?: boolean
|
||||||
}>({
|
}> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ slug, emoji }) => [emoji ? 'addemoji' : 'addstickers', { set: slug }],
|
internalBuild: ({ slug, emoji }) => [emoji ? 'addemoji' : 'addstickers', { set: slug }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'addstickers' && path !== 'addemoji') return null
|
if (path !== 'addstickers' && path !== 'addemoji') return null
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Deeplink } from './common.js'
|
||||||
import { deeplinkBuilder } from './common.js'
|
import { deeplinkBuilder } from './common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +6,7 @@ import { deeplinkBuilder } from './common.js'
|
||||||
*
|
*
|
||||||
* Used to link to public users, groups and channels
|
* Used to link to public users, groups and channels
|
||||||
*/
|
*/
|
||||||
export const publicUsername = deeplinkBuilder<{ username: string }>({
|
export const publicUsername: Deeplink<{ username: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ username }) => ['resolve', { domain: username }],
|
internalBuild: ({ username }) => ['resolve', { domain: username }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'resolve') return null
|
if (path !== 'resolve') return null
|
||||||
|
@ -36,7 +37,7 @@ export const publicUsername = deeplinkBuilder<{ username: string }>({
|
||||||
* and they have an expiration date, specified by the expires field of the exportedContactToken
|
* and they have an expiration date, specified by the expires field of the exportedContactToken
|
||||||
* constructor returned by contacts.exportContactToken.
|
* constructor returned by contacts.exportContactToken.
|
||||||
*/
|
*/
|
||||||
export const temporaryProfile = deeplinkBuilder<{ token: string }>({
|
export const temporaryProfile: Deeplink<{ token: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ token }) => ['contact', { token }],
|
internalBuild: ({ token }) => ['contact', { token }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'contact') return null
|
if (path !== 'contact') return null
|
||||||
|
@ -60,7 +61,7 @@ export const temporaryProfile = deeplinkBuilder<{ token: string }>({
|
||||||
*
|
*
|
||||||
* Used to link to public and private users by their phone number.
|
* Used to link to public and private users by their phone number.
|
||||||
*/
|
*/
|
||||||
export const phoneNumber = deeplinkBuilder<{ phone: string }>({
|
export const phoneNumber: Deeplink<{ phone: string }> = /* #__PURE__ */ deeplinkBuilder({
|
||||||
internalBuild: ({ phone }) => ['resolve', { phone }],
|
internalBuild: ({ phone }) => ['resolve', { phone }],
|
||||||
internalParse: (path, query) => {
|
internalParse: (path, query) => {
|
||||||
if (path !== 'resolve') return null
|
if (path !== 'resolve') return null
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
|
||||||
|
import type { ICorePlatform } from '../platform.js'
|
||||||
import { getPlatform } from '../platform.js'
|
import { getPlatform } from '../platform.js'
|
||||||
|
|
||||||
import { isTlRpcError } from './type-assertions.js'
|
import { isTlRpcError } from './type-assertions.js'
|
||||||
|
@ -127,11 +128,11 @@ export class Logger {
|
||||||
this.mgr.handler(this.color, level, this.tag, this.getPrefix() + fmt, args)
|
this.mgr.handler(this.color, level, this.tag, this.getPrefix() + fmt, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly error = this.log.bind(this, LogManager.ERROR)
|
readonly error: (fmt: string, ...args: unknown[]) => void = this.log.bind(this, LogManager.ERROR)
|
||||||
readonly warn = this.log.bind(this, LogManager.WARN)
|
readonly warn: (fmt: string, ...args: unknown[]) => void = this.log.bind(this, LogManager.WARN)
|
||||||
readonly info = this.log.bind(this, LogManager.INFO)
|
readonly info: (fmt: string, ...args: unknown[]) => void = this.log.bind(this, LogManager.INFO)
|
||||||
readonly debug = this.log.bind(this, LogManager.DEBUG)
|
readonly debug: (fmt: string, ...args: unknown[]) => void = this.log.bind(this, LogManager.DEBUG)
|
||||||
readonly verbose = this.log.bind(this, LogManager.VERBOSE)
|
readonly verbose: (fmt: string, ...args: unknown[]) => void = this.log.bind(this, LogManager.VERBOSE)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link Logger} with the given tag
|
* Create a {@link Logger} with the given tag
|
||||||
|
@ -158,9 +159,9 @@ export class LogManager extends Logger {
|
||||||
static DEBUG = 4
|
static DEBUG = 4
|
||||||
static VERBOSE = 5
|
static VERBOSE = 5
|
||||||
|
|
||||||
readonly platform
|
readonly platform: ICorePlatform
|
||||||
level: number
|
level: number
|
||||||
handler
|
handler: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
|
||||||
|
|
||||||
constructor(tag = 'base') {
|
constructor(tag = 'base') {
|
||||||
// workaround because we cant pass this to super
|
// workaround because we cant pass this to super
|
||||||
|
|
|
@ -159,23 +159,23 @@ export class LongSet {
|
||||||
return this._set.size
|
return this._set.size
|
||||||
}
|
}
|
||||||
|
|
||||||
add(val: Long) {
|
add(val: Long): void {
|
||||||
this._set.add(longToFastString(val))
|
this._set.add(longToFastString(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(val: Long) {
|
delete(val: Long): void {
|
||||||
this._set.delete(longToFastString(val))
|
this._set.delete(longToFastString(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
has(val: Long) {
|
has(val: Long): boolean {
|
||||||
return this._set.has(longToFastString(val))
|
return this._set.has(longToFastString(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear(): void {
|
||||||
this._set.clear()
|
this._set.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
toArray() {
|
toArray(): Long[] {
|
||||||
const arr: Long[] = []
|
const arr: Long[] = []
|
||||||
|
|
||||||
for (const v of this._set) {
|
for (const v of this._set) {
|
||||||
|
|
|
@ -33,12 +33,12 @@ export class LruSet<T extends string | number | Long> {
|
||||||
this._set = forLong ? new LongSet() : new Set()
|
this._set = forLong ? new LongSet() : new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear(): void {
|
||||||
this._first = this._last = undefined
|
this._first = this._last = undefined
|
||||||
this._set.clear()
|
this._set.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
add(val: T) {
|
add(val: T): void {
|
||||||
if (this._set.has(val as any)) return
|
if (this._set.has(val as any)) return
|
||||||
|
|
||||||
if (!this._first) this._first = { v: val }
|
if (!this._first) this._first = { v: val }
|
||||||
|
@ -59,7 +59,7 @@ export class LruSet<T extends string | number | Long> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
has(val: T) {
|
has(val: T): boolean {
|
||||||
return this._set.has(val as any)
|
return this._set.has(val as any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export interface UserConfigPersisted {
|
||||||
apiHash: string
|
apiHash: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConfigFilePath() {
|
export function getConfigFilePath(): string {
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'linux':
|
case 'linux':
|
||||||
return path.join(homedir(), '.local', 'share', 'mtcute-create.json')
|
return path.join(homedir(), '.local', 'share', 'mtcute-create.json')
|
||||||
|
@ -35,7 +35,7 @@ export async function readConfig(): Promise<UserConfigPersisted | null> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function writeConfig(config: UserConfigPersisted) {
|
export async function writeConfig(config: UserConfigPersisted): Promise<void> {
|
||||||
const filePath = getConfigFilePath()
|
const filePath = getConfigFilePath()
|
||||||
|
|
||||||
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
await fs.mkdir(path.dirname(filePath), { recursive: true })
|
||||||
|
|
|
@ -11,7 +11,7 @@ export interface DependenciesList {
|
||||||
devDepdenencies: string[]
|
devDepdenencies: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildDependenciesList(config: UserConfig) {
|
export function buildDependenciesList(config: UserConfig): DependenciesList {
|
||||||
const dependencies = []
|
const dependencies = []
|
||||||
const devDepdenencies = []
|
const devDepdenencies = []
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ export function buildDependenciesList(config: UserConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installDependencies(cwd: string, config: UserConfig) {
|
export async function installDependencies(cwd: string, config: UserConfig): Promise<void> {
|
||||||
const { dependencies, devDepdenencies } = buildDependenciesList(config)
|
const { dependencies, devDepdenencies } = buildDependenciesList(config)
|
||||||
|
|
||||||
if (config.packageManager === PackageManager.Deno) {
|
if (config.packageManager === PackageManager.Deno) {
|
||||||
|
|
|
@ -85,7 +85,7 @@ export function getInstallCommand(params: { mgr: PackageManager, packages: strin
|
||||||
return exec
|
return exec
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getExecCommand(mgr: PackageManager, ...cmd: string[]) {
|
export function getExecCommand(mgr: PackageManager, ...cmd: string[]): string[] {
|
||||||
switch (mgr) {
|
switch (mgr) {
|
||||||
case PackageManager.Npm:
|
case PackageManager.Npm:
|
||||||
return ['npx', ...cmd]
|
return ['npx', ...cmd]
|
||||||
|
@ -100,7 +100,7 @@ export function getExecCommand(mgr: PackageManager, ...cmd: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function packageManagerToRuntime(mgr: PackageManager) {
|
export function packageManagerToRuntime(mgr: PackageManager): 'node' | 'bun' | 'deno' {
|
||||||
switch (mgr) {
|
switch (mgr) {
|
||||||
case PackageManager.Npm:
|
case PackageManager.Npm:
|
||||||
case PackageManager.Yarn:
|
case PackageManager.Yarn:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as colors from 'colorette'
|
import * as colors from 'colorette'
|
||||||
import { spawn } from 'cross-spawn'
|
import { spawn } from 'cross-spawn'
|
||||||
|
|
||||||
export function exec(cwd: string, ...cmd: string[]) {
|
export function exec(cwd: string, ...cmd: string[]): Promise<void> {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
console.log(`${colors.blue('$')} ${cmd.map(it => (it.includes(' ') ? JSON.stringify(it) : it)).join(' ')}`)
|
console.log(`${colors.blue('$')} ${cmd.map(it => (it.includes(' ') ? JSON.stringify(it) : it)).join(' ')}`)
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ class WrappedDatabase implements ISqliteDatabase {
|
||||||
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export { SqliteStorageDriver } from './driver.js'
|
||||||
export class SqliteStorage extends BaseSqliteStorage {
|
export class SqliteStorage extends BaseSqliteStorage {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super(new SqliteStorageDriver(filename, params))
|
super(new SqliteStorageDriver(filename, params))
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export class DenoCryptoProvider extends BaseCryptoProvider implements ICryptoPro
|
||||||
return toUint8Array(gunzipSync(data))
|
return toUint8Array(gunzipSync(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
randomFill(buf: Uint8Array) {
|
randomFill(buf: Uint8Array): void {
|
||||||
crypto.getRandomValues(buf)
|
crypto.getRandomValues(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ import { Readable as NodeReadable } from 'node:stream'
|
||||||
import type { UploadFileLike } from '@mtcute/core'
|
import type { UploadFileLike } from '@mtcute/core'
|
||||||
import { extractFileName } from '@mtcute/core/utils.js'
|
import { extractFileName } from '@mtcute/core/utils.js'
|
||||||
|
|
||||||
export async function normalizeFile(file: UploadFileLike) {
|
export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
|
file: UploadFileLike
|
||||||
|
fileName?: string | undefined
|
||||||
|
fileSize?: number
|
||||||
|
} | null> {
|
||||||
if (typeof file === 'string') {
|
if (typeof file === 'string') {
|
||||||
const fd = await Deno.open(file, { read: true })
|
const fd = await Deno.open(file, { read: true })
|
||||||
|
|
||||||
|
|
|
@ -134,5 +134,5 @@ export abstract class BaseTcpTransport extends EventEmitter implements ITelegram
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TcpTransport extends BaseTcpTransport {
|
export class TcpTransport extends BaseTcpTransport {
|
||||||
_packetCodec = new IntermediatePacketCodec()
|
_packetCodec: IntermediatePacketCodec = new IntermediatePacketCodec()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { OmitInputMessageId, ParametersSkip1 } from '@mtcute/core'
|
import type { Message, OmitInputMessageId, ParametersSkip1, Sticker } from '@mtcute/core'
|
||||||
import { BusinessMessage } from '@mtcute/core'
|
import { BusinessMessage } from '@mtcute/core'
|
||||||
import type { TelegramClient } from '@mtcute/core/client.js'
|
import type { TelegramClient } from '@mtcute/core/client.js'
|
||||||
import type {
|
import type {
|
||||||
|
@ -45,12 +45,12 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get all custom emojis contained in this message (message group), if any */
|
/** Get all custom emojis contained in this message (message group), if any */
|
||||||
getCustomEmojis() {
|
getCustomEmojis(): Promise<Sticker[]> {
|
||||||
return this.client.getCustomEmojisFromMessages(this.messages)
|
return this.client.getCustomEmojisFromMessages(this.messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
||||||
answerText(...params: ParametersSkip1<TelegramClient['answerText']>) {
|
answerText(...params: ParametersSkip1<TelegramClient['answerText']>): Promise<Message> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
||||||
answerMedia(...params: ParametersSkip1<TelegramClient['answerMedia']>) {
|
answerMedia(...params: ParametersSkip1<TelegramClient['answerMedia']>): Promise<Message> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
||||||
answerMediaGroup(...params: ParametersSkip1<TelegramClient['answerMediaGroup']>) {
|
answerMediaGroup(...params: ParametersSkip1<TelegramClient['answerMediaGroup']>): Promise<Message[]> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message in reply to this message */
|
/** Send a text message in reply to this message */
|
||||||
replyText(...params: ParametersSkip1<TelegramClient['replyText']>) {
|
replyText(...params: ParametersSkip1<TelegramClient['replyText']>): Promise<Message> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media in reply to this message */
|
/** Send a media in reply to this message */
|
||||||
replyMedia(...params: ParametersSkip1<TelegramClient['replyMedia']>) {
|
replyMedia(...params: ParametersSkip1<TelegramClient['replyMedia']>): Promise<Message> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group in reply to this message */
|
/** Send a media group in reply to this message */
|
||||||
replyMediaGroup(...params: ParametersSkip1<TelegramClient['replyMediaGroup']>) {
|
replyMediaGroup(...params: ParametersSkip1<TelegramClient['replyMediaGroup']>): Promise<Message[]> {
|
||||||
const [send, params_ = {}] = params
|
const [send, params_ = {}] = params
|
||||||
params_.businessConnectionId = this.update.connectionId
|
params_.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
|
@ -98,28 +98,28 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message in reply to this message */
|
/** Send a text message in reply to this message */
|
||||||
quoteWithText(params: Parameters<TelegramClient['quoteWithText']>[1]) {
|
quoteWithText(params: Parameters<TelegramClient['quoteWithText']>[1]): Promise<Message> {
|
||||||
params.businessConnectionId = this.update.connectionId
|
params.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
return this.client.quoteWithText(this, params)
|
return this.client.quoteWithText(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media in reply to this message */
|
/** Send a media in reply to this message */
|
||||||
quoteWithMedia(params: Parameters<TelegramClient['quoteWithMedia']>[1]) {
|
quoteWithMedia(params: Parameters<TelegramClient['quoteWithMedia']>[1]): Promise<Message> {
|
||||||
params.businessConnectionId = this.update.connectionId
|
params.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
return this.client.quoteWithMedia(this, params)
|
return this.client.quoteWithMedia(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group in reply to this message */
|
/** Send a media group in reply to this message */
|
||||||
quoteWithMediaGroup(params: Parameters<TelegramClient['quoteWithMediaGroup']>[1]) {
|
quoteWithMediaGroup(params: Parameters<TelegramClient['quoteWithMediaGroup']>[1]): Promise<Message[]> {
|
||||||
params.businessConnectionId = this.update.connectionId
|
params.businessConnectionId = this.update.connectionId
|
||||||
|
|
||||||
return this.client.quoteWithMediaGroup(this, params)
|
return this.client.quoteWithMediaGroup(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete this message (message group) */
|
/** Delete this message (message group) */
|
||||||
delete(params?: DeleteMessagesParams) {
|
delete(params?: DeleteMessagesParams): Promise<void> {
|
||||||
return this.client.deleteMessagesById(
|
return this.client.deleteMessagesById(
|
||||||
this.chat.inputPeer,
|
this.chat.inputPeer,
|
||||||
this.messages.map(it => it.id),
|
this.messages.map(it => it.id),
|
||||||
|
@ -128,7 +128,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pin this message */
|
/** Pin this message */
|
||||||
pin(params?: OmitInputMessageId<Parameters<TelegramClient['pinMessage']>[0]>) {
|
pin(params?: OmitInputMessageId<Parameters<TelegramClient['pinMessage']>[0]>): Promise<Message | null> {
|
||||||
return this.client.pinMessage({
|
return this.client.pinMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -137,7 +137,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unpin this message */
|
/** Unpin this message */
|
||||||
unpin() {
|
unpin(): Promise<void> {
|
||||||
return this.client.unpinMessage({
|
return this.client.unpinMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -145,7 +145,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Edit this message */
|
/** Edit this message */
|
||||||
edit(params: OmitInputMessageId<Parameters<TelegramClient['editMessage']>[0]>) {
|
edit(params: OmitInputMessageId<Parameters<TelegramClient['editMessage']>[0]>): Promise<Message> {
|
||||||
return this.client.editMessage({
|
return this.client.editMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -154,7 +154,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forward this message (message group) */
|
/** Forward this message (message group) */
|
||||||
forwardTo(params: ForwardMessageOptions) {
|
forwardTo(params: ForwardMessageOptions): Promise<Message[]> {
|
||||||
return this.client.forwardMessagesById({
|
return this.client.forwardMessagesById({
|
||||||
fromChatId: this.chat.inputPeer,
|
fromChatId: this.chat.inputPeer,
|
||||||
messages: this.messages.map(it => it.id),
|
messages: this.messages.map(it => it.id),
|
||||||
|
@ -163,7 +163,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a copy of this message (message group) */
|
/** Send a copy of this message (message group) */
|
||||||
copy(params: SendCopyParams & SendCopyGroupParams) {
|
copy(params: SendCopyParams & SendCopyGroupParams): Promise<Message | Message[]> {
|
||||||
if (this.isMessageGroup) {
|
if (this.isMessageGroup) {
|
||||||
return this.client.sendCopyGroup({
|
return this.client.sendCopyGroup({
|
||||||
messages: this.messages,
|
messages: this.messages,
|
||||||
|
@ -178,7 +178,7 @@ export class BusinessMessageContext extends BusinessMessage implements UpdateCon
|
||||||
}
|
}
|
||||||
|
|
||||||
/** React to this message */
|
/** React to this message */
|
||||||
react(params: OmitInputMessageId<Parameters<TelegramClient['sendReaction']>[0]>) {
|
react(params: OmitInputMessageId<Parameters<TelegramClient['sendReaction']>[0]>): Promise<Message | null> {
|
||||||
return this.client.sendReaction({
|
return this.client.sendReaction({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Answer to this callback query */
|
/** Answer to this callback query */
|
||||||
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]) {
|
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]): Promise<void> {
|
||||||
return this.client.answerCallbackQuery(this.id, params)
|
return this.client.answerCallbackQuery(this.id, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext
|
||||||
/**
|
/**
|
||||||
* Edit the message that contained the callback button that was clicked.
|
* Edit the message that contained the callback button that was clicked.
|
||||||
*/
|
*/
|
||||||
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>) {
|
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>): Promise<Message> {
|
||||||
return this.client.editMessage({
|
return this.client.editMessage({
|
||||||
chatId: this.raw.peer,
|
chatId: this.raw.peer,
|
||||||
message: this.raw.msgId,
|
message: this.raw.msgId,
|
||||||
|
@ -48,7 +48,9 @@ export class CallbackQueryContext extends CallbackQuery implements UpdateContext
|
||||||
/**
|
/**
|
||||||
* Shortcut for getting the message and editing it.
|
* Shortcut for getting the message and editing it.
|
||||||
*/
|
*/
|
||||||
async editMessageWith(handler: (msg: Message) => MaybePromise<Parameters<CallbackQueryContext['editMessage']>[0]>) {
|
async editMessageWith(
|
||||||
|
handler: (msg: Message) => MaybePromise<Parameters<CallbackQueryContext['editMessage']>[0]>,
|
||||||
|
): Promise<Message | undefined> {
|
||||||
const msg = await this.getMessage()
|
const msg = await this.getMessage()
|
||||||
if (!msg) return
|
if (!msg) return
|
||||||
|
|
||||||
|
@ -75,14 +77,14 @@ export class InlineCallbackQueryContext extends InlineCallbackQuery implements U
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Answer to this callback query */
|
/** Answer to this callback query */
|
||||||
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]) {
|
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]): Promise<void> {
|
||||||
return this.client.answerCallbackQuery(this.id, params)
|
return this.client.answerCallbackQuery(this.id, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit the message that contained the callback button that was clicked.
|
* Edit the message that contained the callback button that was clicked.
|
||||||
*/
|
*/
|
||||||
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>) {
|
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>): Promise<void> {
|
||||||
return this.client.editInlineMessage({
|
return this.client.editInlineMessage({
|
||||||
messageId: this.raw.msgId,
|
messageId: this.raw.msgId,
|
||||||
...params,
|
...params,
|
||||||
|
@ -108,14 +110,14 @@ export class BusinessCallbackQueryContext
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Answer to this callback query */
|
/** Answer to this callback query */
|
||||||
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]) {
|
answer(params: Parameters<TelegramClient['answerCallbackQuery']>[1]): Promise<void> {
|
||||||
return this.client.answerCallbackQuery(this.id, params)
|
return this.client.answerCallbackQuery(this.id, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit the message that contained the callback button that was clicked.
|
* Edit the message that contained the callback button that was clicked.
|
||||||
*/
|
*/
|
||||||
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>) {
|
async editMessage(params: Omit<Parameters<TelegramClient['editInlineMessage']>[0], 'messageId'>): Promise<Message> {
|
||||||
return this.client.editMessage({
|
return this.client.editMessage({
|
||||||
message: this.message,
|
message: this.message,
|
||||||
businessConnectionId: this.connectionId,
|
businessConnectionId: this.connectionId,
|
||||||
|
|
|
@ -20,7 +20,7 @@ export class InlineQueryContext extends InlineQuery implements UpdateContext<Inl
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Answer to this inline query */
|
/** Answer to this inline query */
|
||||||
answer(...params: ParametersSkip1<TelegramClient['answerInlineQuery']>) {
|
answer(...params: ParametersSkip1<TelegramClient['answerInlineQuery']>): Promise<void> {
|
||||||
return this.client.answerInlineQuery(this.id, ...params)
|
return this.client.answerInlineQuery(this.id, ...params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
import type { OmitInputMessageId, ParametersSkip1, Peer } from '@mtcute/core'
|
import type { OmitInputMessageId, ParametersSkip1, Peer, Sticker } from '@mtcute/core'
|
||||||
import { Message, MtPeerNotFoundError } from '@mtcute/core'
|
import { Message, MtPeerNotFoundError } from '@mtcute/core'
|
||||||
import type { TelegramClient } from '@mtcute/core/client.js'
|
import type { TelegramClient } from '@mtcute/core/client.js'
|
||||||
import type { DeleteMessagesParams, ForwardMessageOptions, SendCopyGroupParams, SendCopyParams } from '@mtcute/core/methods.js'
|
import type {
|
||||||
|
DeleteMessagesParams,
|
||||||
|
ForwardMessageOptions,
|
||||||
|
SendCopyGroupParams,
|
||||||
|
SendCopyParams,
|
||||||
|
} from '@mtcute/core/methods.js'
|
||||||
|
|
||||||
import type { UpdateContext } from './base.js'
|
import type { UpdateContext } from './base.js'
|
||||||
|
|
||||||
|
@ -63,82 +68,82 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a message that this message is a reply to */
|
/** Get a message that this message is a reply to */
|
||||||
getReplyTo() {
|
getReplyTo(): Promise<Message | null> {
|
||||||
return this.client.getReplyTo(this)
|
return this.client.getReplyTo(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If this is a channel post, get its automatic forward in the discussion group */
|
/** If this is a channel post, get its automatic forward in the discussion group */
|
||||||
getDiscussionMessage() {
|
getDiscussionMessage(): Promise<Message | null> {
|
||||||
return this.client.getDiscussionMessage({ chatId: this.chat.inputPeer, message: this.id })
|
return this.client.getDiscussionMessage({ chatId: this.chat.inputPeer, message: this.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get all custom emojis contained in this message (message group), if any */
|
/** Get all custom emojis contained in this message (message group), if any */
|
||||||
getCustomEmojis() {
|
getCustomEmojis(): Promise<Sticker[]> {
|
||||||
return this.client.getCustomEmojisFromMessages(this.messages)
|
return this.client.getCustomEmojisFromMessages(this.messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
||||||
answerText(...params: ParametersSkip1<TelegramClient['answerText']>) {
|
answerText(...params: ParametersSkip1<TelegramClient['answerText']>): Promise<Message> {
|
||||||
return this.client.answerText(this, ...params)
|
return this.client.answerText(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
||||||
answerMedia(...params: ParametersSkip1<TelegramClient['answerMedia']>) {
|
answerMedia(...params: ParametersSkip1<TelegramClient['answerMedia']>): Promise<Message> {
|
||||||
return this.client.answerMedia(this, ...params)
|
return this.client.answerMedia(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
||||||
answerMediaGroup(...params: ParametersSkip1<TelegramClient['answerMediaGroup']>) {
|
answerMediaGroup(...params: ParametersSkip1<TelegramClient['answerMediaGroup']>): Promise<Message[]> {
|
||||||
return this.client.answerMediaGroup(this, ...params)
|
return this.client.answerMediaGroup(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message in reply to this message */
|
/** Send a text message in reply to this message */
|
||||||
replyText(...params: ParametersSkip1<TelegramClient['replyText']>) {
|
replyText(...params: ParametersSkip1<TelegramClient['replyText']>): Promise<Message> {
|
||||||
return this.client.replyText(this, ...params)
|
return this.client.replyText(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media in reply to this message */
|
/** Send a media in reply to this message */
|
||||||
replyMedia(...params: ParametersSkip1<TelegramClient['replyMedia']>) {
|
replyMedia(...params: ParametersSkip1<TelegramClient['replyMedia']>): Promise<Message> {
|
||||||
return this.client.replyMedia(this, ...params)
|
return this.client.replyMedia(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group in reply to this message */
|
/** Send a media group in reply to this message */
|
||||||
replyMediaGroup(...params: ParametersSkip1<TelegramClient['replyMediaGroup']>) {
|
replyMediaGroup(...params: ParametersSkip1<TelegramClient['replyMediaGroup']>): Promise<Message[]> {
|
||||||
return this.client.replyMediaGroup(this, ...params)
|
return this.client.replyMediaGroup(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text message in reply to this message */
|
/** Send a text message in reply to this message */
|
||||||
quoteWithText(params: Parameters<TelegramClient['quoteWithText']>[1]) {
|
quoteWithText(params: Parameters<TelegramClient['quoteWithText']>[1]): Promise<Message> {
|
||||||
return this.client.quoteWithText(this, params)
|
return this.client.quoteWithText(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media in reply to this message */
|
/** Send a media in reply to this message */
|
||||||
quoteWithMedia(params: Parameters<TelegramClient['quoteWithMedia']>[1]) {
|
quoteWithMedia(params: Parameters<TelegramClient['quoteWithMedia']>[1]): Promise<Message> {
|
||||||
return this.client.quoteWithMedia(this, params)
|
return this.client.quoteWithMedia(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group in reply to this message */
|
/** Send a media group in reply to this message */
|
||||||
quoteWithMediaGroup(params: Parameters<TelegramClient['quoteWithMediaGroup']>[1]) {
|
quoteWithMediaGroup(params: Parameters<TelegramClient['quoteWithMediaGroup']>[1]): Promise<Message[]> {
|
||||||
return this.client.quoteWithMediaGroup(this, params)
|
return this.client.quoteWithMediaGroup(this, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a text as a comment to this message */
|
/** Send a text as a comment to this message */
|
||||||
commentText(...params: ParametersSkip1<TelegramClient['commentText']>) {
|
commentText(...params: ParametersSkip1<TelegramClient['commentText']>): Promise<Message> {
|
||||||
return this.client.commentText(this, ...params)
|
return this.client.commentText(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media as a comment to this message */
|
/** Send a media as a comment to this message */
|
||||||
commentMedia(...params: ParametersSkip1<TelegramClient['commentMedia']>) {
|
commentMedia(...params: ParametersSkip1<TelegramClient['commentMedia']>): Promise<Message> {
|
||||||
return this.client.commentMedia(this, ...params)
|
return this.client.commentMedia(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a media group as a comment to this message */
|
/** Send a media group as a comment to this message */
|
||||||
commentMediaGroup(...params: ParametersSkip1<TelegramClient['commentMediaGroup']>) {
|
commentMediaGroup(...params: ParametersSkip1<TelegramClient['commentMediaGroup']>): Promise<Message[]> {
|
||||||
return this.client.commentMediaGroup(this, ...params)
|
return this.client.commentMediaGroup(this, ...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete this message (message group) */
|
/** Delete this message (message group) */
|
||||||
delete(params?: DeleteMessagesParams) {
|
delete(params?: DeleteMessagesParams): Promise<void> {
|
||||||
return this.client.deleteMessagesById(
|
return this.client.deleteMessagesById(
|
||||||
this.chat.inputPeer,
|
this.chat.inputPeer,
|
||||||
this.messages.map(it => it.id),
|
this.messages.map(it => it.id),
|
||||||
|
@ -147,7 +152,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Pin this message */
|
/** Pin this message */
|
||||||
pin(params?: OmitInputMessageId<Parameters<TelegramClient['pinMessage']>[0]>) {
|
pin(params?: OmitInputMessageId<Parameters<TelegramClient['pinMessage']>[0]>): Promise<Message | null> {
|
||||||
return this.client.pinMessage({
|
return this.client.pinMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -156,7 +161,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Unpin this message */
|
/** Unpin this message */
|
||||||
unpin() {
|
unpin(): Promise<void> {
|
||||||
return this.client.unpinMessage({
|
return this.client.unpinMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -164,7 +169,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Edit this message */
|
/** Edit this message */
|
||||||
edit(params: OmitInputMessageId<Parameters<TelegramClient['editMessage']>[0]>) {
|
edit(params: OmitInputMessageId<Parameters<TelegramClient['editMessage']>[0]>): Promise<Message> {
|
||||||
return this.client.editMessage({
|
return this.client.editMessage({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
@ -173,7 +178,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forward this message (message group) */
|
/** Forward this message (message group) */
|
||||||
forwardTo(params: ForwardMessageOptions) {
|
forwardTo(params: ForwardMessageOptions): Promise<Message[]> {
|
||||||
return this.client.forwardMessagesById({
|
return this.client.forwardMessagesById({
|
||||||
fromChatId: this.chat.inputPeer,
|
fromChatId: this.chat.inputPeer,
|
||||||
messages: this.messages.map(it => it.id),
|
messages: this.messages.map(it => it.id),
|
||||||
|
@ -182,7 +187,7 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Send a copy of this message (message group) */
|
/** Send a copy of this message (message group) */
|
||||||
copy(params: SendCopyParams & SendCopyGroupParams) {
|
copy(params: SendCopyParams & SendCopyGroupParams): Promise<Message> | Promise<Message[]> {
|
||||||
if (this.isMessageGroup) {
|
if (this.isMessageGroup) {
|
||||||
return this.client.sendCopyGroup({
|
return this.client.sendCopyGroup({
|
||||||
messages: this.messages,
|
messages: this.messages,
|
||||||
|
@ -197,7 +202,9 @@ export class MessageContext extends Message implements UpdateContext<Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** React to this message */
|
/** React to this message */
|
||||||
react(params: OmitInputMessageId<Parameters<TelegramClient['sendReaction']>[0]>) {
|
react(
|
||||||
|
params: OmitInputMessageId<Parameters<TelegramClient['sendReaction']>[0]>,
|
||||||
|
): Promise<Message | null> {
|
||||||
return this.client.sendReaction({
|
return this.client.sendReaction({
|
||||||
chatId: this.chat.inputPeer,
|
chatId: this.chat.inputPeer,
|
||||||
message: this.id,
|
message: this.id,
|
||||||
|
|
|
@ -1,4 +1,21 @@
|
||||||
import type { ParsedUpdate } from '@mtcute/core'
|
import type {
|
||||||
|
BotReactionCountUpdate,
|
||||||
|
BotReactionUpdate,
|
||||||
|
BotStoppedUpdate,
|
||||||
|
BusinessConnection,
|
||||||
|
ChatJoinRequestUpdate,
|
||||||
|
ChatMemberUpdate,
|
||||||
|
DeleteBusinessMessageUpdate,
|
||||||
|
DeleteMessageUpdate,
|
||||||
|
DeleteStoryUpdate,
|
||||||
|
HistoryReadUpdate,
|
||||||
|
ParsedUpdate,
|
||||||
|
PollUpdate,
|
||||||
|
PollVoteUpdate,
|
||||||
|
StoryUpdate,
|
||||||
|
UserStatusUpdate,
|
||||||
|
UserTypingUpdate,
|
||||||
|
} from '@mtcute/core'
|
||||||
import type { TelegramClient } from '@mtcute/core/client.js'
|
import type { TelegramClient } from '@mtcute/core/client.js'
|
||||||
|
|
||||||
import type { UpdateContextDistributed } from './base.js'
|
import type { UpdateContextDistributed } from './base.js'
|
||||||
|
@ -10,8 +27,36 @@ import { InlineQueryContext } from './inline-query.js'
|
||||||
import { MessageContext } from './message.js'
|
import { MessageContext } from './message.js'
|
||||||
import { PreCheckoutQueryContext } from './pre-checkout-query.js'
|
import { PreCheckoutQueryContext } from './pre-checkout-query.js'
|
||||||
|
|
||||||
|
export type UpdateContextType =
|
||||||
|
| MessageContext
|
||||||
|
| InlineQueryContext
|
||||||
|
| ChosenInlineResultContext
|
||||||
|
| CallbackQueryContext
|
||||||
|
| InlineCallbackQueryContext
|
||||||
|
| BusinessCallbackQueryContext
|
||||||
|
| ChatJoinRequestUpdateContext
|
||||||
|
| PreCheckoutQueryContext
|
||||||
|
| BusinessMessageContext
|
||||||
|
| UpdateContextDistributed<
|
||||||
|
| DeleteMessageUpdate
|
||||||
|
| ChatMemberUpdate
|
||||||
|
| PollUpdate
|
||||||
|
| PollVoteUpdate
|
||||||
|
| UserStatusUpdate
|
||||||
|
| UserTypingUpdate
|
||||||
|
| HistoryReadUpdate
|
||||||
|
| BotStoppedUpdate
|
||||||
|
| ChatJoinRequestUpdate
|
||||||
|
| StoryUpdate
|
||||||
|
| DeleteStoryUpdate
|
||||||
|
| BotReactionUpdate
|
||||||
|
| BotReactionCountUpdate
|
||||||
|
| BusinessConnection
|
||||||
|
| DeleteBusinessMessageUpdate
|
||||||
|
>
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function _parsedUpdateToContext(client: TelegramClient, update: ParsedUpdate) {
|
export function _parsedUpdateToContext(client: TelegramClient, update: ParsedUpdate): UpdateContextType {
|
||||||
switch (update.name) {
|
switch (update.name) {
|
||||||
case 'new_message':
|
case 'new_message':
|
||||||
case 'edit_message':
|
case 'edit_message':
|
||||||
|
@ -43,5 +88,3 @@ export function _parsedUpdateToContext(client: TelegramClient, update: ParsedUpd
|
||||||
|
|
||||||
return _update
|
return _update
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateContextType = ReturnType<typeof _parsedUpdateToContext>
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import type { MaybeArray, MaybePromise, Message } from '@mtcute/core'
|
import type { Chat, MaybeArray, MaybePromise, Message } from '@mtcute/core'
|
||||||
|
|
||||||
import type { BusinessMessageContext } from '../context/business-message.js'
|
import type { BusinessMessageContext } from '../context/business-message.js'
|
||||||
import type { MessageContext } from '../context/message.js'
|
import type { MessageContext } from '../context/message.js'
|
||||||
|
|
||||||
import { chat } from './chat.js'
|
import { chat } from './chat.js'
|
||||||
import { and, or } from './logic.js'
|
import { and, or } from './logic.js'
|
||||||
import type { UpdateFilter } from './types.js'
|
import type { Modify, UpdateFilter } from './types.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter messages that call the given command(s)..
|
* Filter messages that call the given command(s)..
|
||||||
|
@ -97,13 +97,30 @@ export function command(commands: MaybeArray<string | RegExp>, {
|
||||||
* Shorthand filter that matches /start commands sent to bot's
|
* Shorthand filter that matches /start commands sent to bot's
|
||||||
* private messages.
|
* private messages.
|
||||||
*/
|
*/
|
||||||
export const start = and(chat('private'), command('start'))
|
export const start: UpdateFilter<
|
||||||
|
MessageContext | BusinessMessageContext,
|
||||||
|
{
|
||||||
|
chat: Modify<Chat, {
|
||||||
|
chatType: 'private'
|
||||||
|
}>
|
||||||
|
command: string[]
|
||||||
|
}
|
||||||
|
> = and(chat('private'), command('start'))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shorthand filter that matches /start commands
|
* Shorthand filter that matches /start commands
|
||||||
* sent in groups (i.e. using `?startgroup` parameter).
|
* sent in groups (i.e. using `?startgroup` parameter).
|
||||||
*/
|
*/
|
||||||
export const startGroup = and(or(chat('supergroup'), chat('group')), command('start'))
|
export const startGroup: UpdateFilter<
|
||||||
|
MessageContext | BusinessMessageContext,
|
||||||
|
{
|
||||||
|
chat: Modify<Chat, {
|
||||||
|
chatType: 'group' | 'supergroup'
|
||||||
|
}>
|
||||||
|
command: string[]
|
||||||
|
},
|
||||||
|
never
|
||||||
|
> = and(or(chat('supergroup'), chat('group')), command('start'))
|
||||||
|
|
||||||
function deeplinkBase(base: UpdateFilter<MessageContext | BusinessMessageContext, { command: string[] }>) {
|
function deeplinkBase(base: UpdateFilter<MessageContext | BusinessMessageContext, { command: string[] }>) {
|
||||||
return (
|
return (
|
||||||
|
@ -156,7 +173,10 @@ function deeplinkBase(base: UpdateFilter<MessageContext | BusinessMessageContext
|
||||||
* If the parameter is a regex, groups are added to `msg.command`,
|
* If the parameter is a regex, groups are added to `msg.command`,
|
||||||
* meaning that the first group is available in `msg.command[2]`.
|
* meaning that the first group is available in `msg.command[2]`.
|
||||||
*/
|
*/
|
||||||
export const deeplink = deeplinkBase(start)
|
export const deeplink: (params: MaybeArray<string | RegExp>) => UpdateFilter<
|
||||||
|
MessageContext | BusinessMessageContext,
|
||||||
|
{ command: string[] }
|
||||||
|
> = deeplinkBase(start)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for group deep links (i.e. `/start <deeplink_parameter>`).
|
* Filter for group deep links (i.e. `/start <deeplink_parameter>`).
|
||||||
|
@ -164,4 +184,7 @@ export const deeplink = deeplinkBase(start)
|
||||||
* If the parameter is a regex, groups are added to `msg.command`,
|
* If the parameter is a regex, groups are added to `msg.command`,
|
||||||
* meaning that the first group is available in `msg.command[2]`.
|
* meaning that the first group is available in `msg.command[2]`.
|
||||||
*/
|
*/
|
||||||
export const deeplinkGroup = deeplinkBase(startGroup)
|
export const deeplinkGroup: (params: MaybeArray<string | RegExp>) => UpdateFilter<
|
||||||
|
MessageContext | BusinessMessageContext,
|
||||||
|
{ command: string[] }
|
||||||
|
> = deeplinkBase(startGroup)
|
||||||
|
|
|
@ -1,16 +1,29 @@
|
||||||
import type {
|
import type {
|
||||||
|
Audio,
|
||||||
|
Contact,
|
||||||
|
Dice,
|
||||||
|
Document,
|
||||||
|
Game,
|
||||||
|
Invoice,
|
||||||
|
LiveLocation,
|
||||||
|
Location,
|
||||||
MaybeArray,
|
MaybeArray,
|
||||||
Message,
|
Message,
|
||||||
MessageAction,
|
MessageAction,
|
||||||
MessageMediaType,
|
MessageMediaType,
|
||||||
Peer,
|
Peer,
|
||||||
|
Photo,
|
||||||
|
Poll,
|
||||||
RepliedMessageInfo,
|
RepliedMessageInfo,
|
||||||
RepliedMessageOrigin,
|
RepliedMessageOrigin,
|
||||||
Sticker,
|
Sticker,
|
||||||
StickerSourceType,
|
StickerSourceType,
|
||||||
StickerType,
|
StickerType,
|
||||||
User,
|
User,
|
||||||
|
Venue,
|
||||||
Video,
|
Video,
|
||||||
|
Voice,
|
||||||
|
WebPage,
|
||||||
_RepliedMessageAssertionsByOrigin,
|
_RepliedMessageAssertionsByOrigin,
|
||||||
} from '@mtcute/core'
|
} from '@mtcute/core'
|
||||||
import {
|
import {
|
||||||
|
@ -68,41 +81,44 @@ export const media: UpdateFilter<Message, { media: Exclude<Message['media'], nul
|
||||||
/**
|
/**
|
||||||
* Filter messages containing media of given type
|
* Filter messages containing media of given type
|
||||||
*/
|
*/
|
||||||
export function mediaOf<T extends MessageMediaType>(type: T): UpdateFilter<Message, { media: Extract<Message['media'], { type: T }> }> {
|
export function mediaOf<T extends MessageMediaType>(type: T): UpdateFilter<
|
||||||
|
Message,
|
||||||
|
{ media: Extract<Message['media'], { type: T }> }
|
||||||
|
> {
|
||||||
return msg =>
|
return msg =>
|
||||||
msg.media?.type === type
|
msg.media?.type === type
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter messages containing a photo */
|
/** Filter messages containing a photo */
|
||||||
export const photo = mediaOf('photo')
|
export const photo: UpdateFilter<Message, { media: Photo }> = mediaOf('photo')
|
||||||
/** Filter messages containing a dice */
|
/** Filter messages containing a dice */
|
||||||
export const dice = mediaOf('dice')
|
export const dice: UpdateFilter<Message, { media: Dice }> = mediaOf('dice')
|
||||||
/** Filter messages containing a contact */
|
/** Filter messages containing a contact */
|
||||||
export const contact = mediaOf('contact')
|
export const contact: UpdateFilter<Message, { media: Contact }> = mediaOf('contact')
|
||||||
/** Filter messages containing an audio file */
|
/** Filter messages containing an audio file */
|
||||||
export const audio = mediaOf('audio')
|
export const audio: UpdateFilter<Message, { media: Audio }> = mediaOf('audio')
|
||||||
/** Filter messages containing a voice message (audio-only) */
|
/** Filter messages containing a voice message (audio-only) */
|
||||||
export const voice = mediaOf('voice')
|
export const voice: UpdateFilter<Message, { media: Voice }> = mediaOf('voice')
|
||||||
/** Filter messages containing a sticker */
|
/** Filter messages containing a sticker */
|
||||||
export const sticker = mediaOf('sticker')
|
export const sticker: UpdateFilter<Message, { media: Sticker }> = mediaOf('sticker')
|
||||||
/** Filter messages containing a document (a file) */
|
/** Filter messages containing a document (a file) */
|
||||||
export const document = mediaOf('document')
|
export const document: UpdateFilter<Message, { media: Document }> = mediaOf('document')
|
||||||
/** Filter messages containing any video (videos, round messages and animations) */
|
/** Filter messages containing any video (videos, round messages and animations) */
|
||||||
export const anyVideo = mediaOf('video')
|
export const anyVideo: UpdateFilter<Message, { media: Video }> = mediaOf('video')
|
||||||
/** Filter messages containing a static location */
|
/** Filter messages containing a static location */
|
||||||
export const location = mediaOf('location')
|
export const location: UpdateFilter<Message, { media: Location }> = mediaOf('location')
|
||||||
/** Filter messages containing a live location */
|
/** Filter messages containing a live location */
|
||||||
export const liveLocation = mediaOf('live_location')
|
export const liveLocation: UpdateFilter<Message, { media: LiveLocation }> = mediaOf('live_location')
|
||||||
/** Filter messages containing a game */
|
/** Filter messages containing a game */
|
||||||
export const game = mediaOf('game')
|
export const game: UpdateFilter<Message, { media: Game }> = mediaOf('game')
|
||||||
/** Filter messages containing a web page */
|
/** Filter messages containing a web page */
|
||||||
export const webpage = mediaOf('webpage')
|
export const webpage: UpdateFilter<Message, { media: WebPage }> = mediaOf('webpage')
|
||||||
/** Filter messages containing a venue */
|
/** Filter messages containing a venue */
|
||||||
export const venue = mediaOf('venue')
|
export const venue: UpdateFilter<Message, { media: Venue }> = mediaOf('venue')
|
||||||
/** Filter messages containing a poll */
|
/** Filter messages containing a poll */
|
||||||
export const poll = mediaOf('poll')
|
export const poll: UpdateFilter<Message, { media: Poll }> = mediaOf('poll')
|
||||||
/** Filter messages containing an invoice */
|
/** Filter messages containing an invoice */
|
||||||
export const invoice = mediaOf('invoice')
|
export const invoice: UpdateFilter<Message, { media: Invoice }> = mediaOf('invoice')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter messages containing any location (live or static).
|
* Filter messages containing any location (live or static).
|
||||||
|
@ -224,7 +240,10 @@ export function action<T extends Exclude<MessageAction, null>['type']>(type: May
|
||||||
return msg => msg.action?.type === type
|
return msg => msg.action?.type === type
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sender<T extends Message['sender']['type']>(type: T): UpdateFilter<Message, { sender: Extract<Message['sender'], { type: T }> }> {
|
export function sender<T extends Message['sender']['type']>(type: T): UpdateFilter<
|
||||||
|
Message,
|
||||||
|
{ sender: Extract<Message['sender'], { type: T }> }
|
||||||
|
> {
|
||||||
return msg =>
|
return msg =>
|
||||||
msg.sender.type === type
|
msg.sender.type === type
|
||||||
}
|
}
|
||||||
|
@ -235,7 +254,13 @@ export function sender<T extends Message['sender']['type']>(type: T): UpdateFilt
|
||||||
*
|
*
|
||||||
* Optionally, you can pass a filter that will be applied to the replied message.
|
* Optionally, you can pass a filter that will be applied to the replied message.
|
||||||
*/
|
*/
|
||||||
export function replyTo<Mod, State extends object>(filter?: UpdateFilter<Message, Mod, State>): UpdateFilter<MessageContext | BusinessMessageContext, { getReplyTo: () => Promise<Message & Mod> }, State> {
|
export function replyTo<Mod, State extends object>(
|
||||||
|
filter?: UpdateFilter<Message, Mod, State>,
|
||||||
|
): UpdateFilter<
|
||||||
|
MessageContext | BusinessMessageContext,
|
||||||
|
{ getReplyTo: () => Promise<Message & Mod> },
|
||||||
|
State
|
||||||
|
> {
|
||||||
return async (msg, state) => {
|
return async (msg, state) => {
|
||||||
if (!msg.replyToMessage?.id) return false
|
if (!msg.replyToMessage?.id) return false
|
||||||
|
|
||||||
|
@ -256,7 +281,9 @@ export function replyTo<Mod, State extends object>(filter?: UpdateFilter<Message
|
||||||
* Middleware-like filter that will fetch the sender of the message
|
* Middleware-like filter that will fetch the sender of the message
|
||||||
* and make it available to further filters, as well as the handler itself.
|
* and make it available to further filters, as well as the handler itself.
|
||||||
*/
|
*/
|
||||||
export function withCompleteSender<Mod, State extends object>(filter?: UpdateFilter<MessageContext, Mod, State>): UpdateFilter<MessageContext, Mod, State> {
|
export function withCompleteSender<Mod, State extends object>(
|
||||||
|
filter?: UpdateFilter<MessageContext, Mod, State>,
|
||||||
|
): UpdateFilter<MessageContext, Mod, State> {
|
||||||
return async (msg, state) => {
|
return async (msg, state) => {
|
||||||
try {
|
try {
|
||||||
await msg.getCompleteSender()
|
await msg.getCompleteSender()
|
||||||
|
|
|
@ -15,8 +15,8 @@ interface RateLimitDto {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MemoryStateRepository implements IStateRepository {
|
class MemoryStateRepository implements IStateRepository {
|
||||||
readonly state
|
readonly state: Map<string, StateDto>
|
||||||
readonly rl
|
readonly rl: Map<string, RateLimitDto>
|
||||||
constructor(readonly _driver: MemoryStorageDriver) {
|
constructor(readonly _driver: MemoryStorageDriver) {
|
||||||
this.state = this._driver.getState<Map<string, StateDto>>('dispatcher_fsm', () => new Map())
|
this.state = this._driver.getState<Map<string, StateDto>>('dispatcher_fsm', () => new Map())
|
||||||
this.rl = this._driver.getState<Map<string, RateLimitDto>>('rl', () => new Map())
|
this.rl = this._driver.getState<Map<string, RateLimitDto>>('rl', () => new Map())
|
||||||
|
@ -99,7 +99,7 @@ class MemoryStateRepository implements IStateRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MemoryStateStorage implements IStateStorageProvider {
|
export class MemoryStateStorage implements IStateStorageProvider {
|
||||||
readonly state
|
readonly state: MemoryStateRepository
|
||||||
|
|
||||||
constructor(readonly driver: MemoryStorageDriver = new MemoryStorageDriver()) {
|
constructor(readonly driver: MemoryStorageDriver = new MemoryStorageDriver()) {
|
||||||
this.state = new MemoryStateRepository(this.driver)
|
this.state = new MemoryStateRepository(this.driver)
|
||||||
|
|
|
@ -116,12 +116,12 @@ class SqliteStateRepository implements IStateRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SqliteStateStorage implements IStateStorageProvider {
|
export class SqliteStateStorage implements IStateStorageProvider {
|
||||||
readonly state
|
readonly state: SqliteStateRepository
|
||||||
constructor(readonly driver: BaseSqliteStorageDriver) {
|
constructor(readonly driver: BaseSqliteStorageDriver) {
|
||||||
this.state = new SqliteStateRepository(driver)
|
this.state = new SqliteStateRepository(driver)
|
||||||
}
|
}
|
||||||
|
|
||||||
static from(provider: BaseSqliteStorage) {
|
static from(provider: BaseSqliteStorage): SqliteStateStorage {
|
||||||
return new SqliteStateStorage(provider.driver)
|
return new SqliteStateStorage(provider.driver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { LruMap, asyncResettable } from '@mtcute/core/utils.js'
|
import { LruMap, asyncResettable } from '@mtcute/core/utils.js'
|
||||||
|
import type { MaybePromise } from '@mtcute/core'
|
||||||
|
|
||||||
import type { IStateStorageProvider } from './provider.js'
|
import type { IStateStorageProvider } from './provider.js'
|
||||||
|
|
||||||
|
@ -16,14 +17,14 @@ export class StateService {
|
||||||
this._loaded = true
|
this._loaded = true
|
||||||
})
|
})
|
||||||
|
|
||||||
async load() {
|
async load(): Promise<void> {
|
||||||
await this._load.run()
|
await this._load.run()
|
||||||
this._vacuumTimer = setInterval(() => {
|
this._vacuumTimer = setInterval(() => {
|
||||||
Promise.resolve(this.provider.state.vacuum(Date.now())).catch(() => {})
|
Promise.resolve(this.provider.state.vacuum(Date.now())).catch(() => {})
|
||||||
}, 300_000)
|
}, 300_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
async destroy() {
|
async destroy(): Promise<void> {
|
||||||
await this.provider.driver.save?.()
|
await this.provider.driver.save?.()
|
||||||
await this.provider.driver.destroy?.()
|
await this.provider.driver.destroy?.()
|
||||||
clearInterval(this._vacuumTimer)
|
clearInterval(this._vacuumTimer)
|
||||||
|
@ -68,11 +69,11 @@ export class StateService {
|
||||||
return this.deleteState(makeCurrentSceneKey(key))
|
return this.deleteState(makeCurrentSceneKey(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
getRateLimit(key: string, limit: number, window: number) {
|
getRateLimit(key: string, limit: number, window: number): MaybePromise<[number, number]> {
|
||||||
return this.provider.state.getRateLimit(key, Date.now(), limit, window)
|
return this.provider.state.getRateLimit(key, Date.now(), limit, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRateLimit(key: string) {
|
resetRateLimit(key: string): MaybePromise<void> {
|
||||||
return this.provider.state.resetRateLimit(key)
|
return this.provider.state.resetRateLimit(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { MaybePromise } from '@mtcute/core'
|
||||||
import type { MessageContext } from './context/message.js'
|
import type { MessageContext } from './context/message.js'
|
||||||
import type { DispatcherParams } from './dispatcher.js'
|
import type { DispatcherParams } from './dispatcher.js'
|
||||||
import { Dispatcher } from './dispatcher.js'
|
import { Dispatcher } from './dispatcher.js'
|
||||||
|
import type { UpdateFilter } from './filters/index.js'
|
||||||
import { filters } from './filters/index.js'
|
import { filters } from './filters/index.js'
|
||||||
import type { UpdateState } from './state/update-state.js'
|
import type { UpdateState } from './state/update-state.js'
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ export class WizardScene<State extends object> extends Dispatcher<State & Wizard
|
||||||
/**
|
/**
|
||||||
* Go to the Nth step
|
* Go to the Nth step
|
||||||
*/
|
*/
|
||||||
async goToStep(state: UpdateState<WizardInternalState>, step: number) {
|
async goToStep(state: UpdateState<WizardInternalState>, step: number): Promise<void> {
|
||||||
if (step >= this._steps) {
|
if (step >= this._steps) {
|
||||||
await state.exit()
|
await state.exit()
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,7 +73,7 @@ export class WizardScene<State extends object> extends Dispatcher<State & Wizard
|
||||||
/**
|
/**
|
||||||
* Skip N steps
|
* Skip N steps
|
||||||
*/
|
*/
|
||||||
async skip(state: UpdateState<WizardInternalState>, count = 1) {
|
async skip(state: UpdateState<WizardInternalState>, count = 1): Promise<void> {
|
||||||
const { $step } = (await state.get()) || {}
|
const { $step } = (await state.get()) || {}
|
||||||
if ($step === undefined) throw new Error('Wizard state is not initialized')
|
if ($step === undefined) throw new Error('Wizard state is not initialized')
|
||||||
|
|
||||||
|
@ -82,7 +83,8 @@ export class WizardScene<State extends object> extends Dispatcher<State & Wizard
|
||||||
/**
|
/**
|
||||||
* Filter that will only pass if the current step is `step`
|
* Filter that will only pass if the current step is `step`
|
||||||
*/
|
*/
|
||||||
static onNthStep(step: number) {
|
// eslint-disable-next-line ts/no-empty-object-type
|
||||||
|
static onNthStep(step: number): UpdateFilter<any, {}, WizardInternalState> {
|
||||||
const filter = filters.state<WizardInternalState>(it => it.$step === step)
|
const filter = filters.state<WizardInternalState>(it => it.$step === step)
|
||||||
|
|
||||||
if (step === 0) return filters.or(filters.stateEmpty, filter)
|
if (step === 0) return filters.or(filters.stateEmpty, filter)
|
||||||
|
@ -93,7 +95,8 @@ export class WizardScene<State extends object> extends Dispatcher<State & Wizard
|
||||||
/**
|
/**
|
||||||
* Filter that will only pass if the current step is the one after last one added
|
* Filter that will only pass if the current step is the one after last one added
|
||||||
*/
|
*/
|
||||||
onCurrentStep() {
|
// eslint-disable-next-line ts/no-empty-object-type
|
||||||
|
onCurrentStep(): UpdateFilter<any, {}, WizardInternalState> {
|
||||||
return WizardScene.onNthStep(this._steps)
|
return WizardScene.onNthStep(this._steps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ import type Long from 'long'
|
||||||
export const PERSISTENT_ID_VERSION_OLD = 2
|
export const PERSISTENT_ID_VERSION_OLD = 2
|
||||||
export const PERSISTENT_ID_VERSION = 4
|
export const PERSISTENT_ID_VERSION = 4
|
||||||
|
|
||||||
export const WEB_LOCATION_FLAG = 1 << 24
|
export const WEB_LOCATION_FLAG: number = 1 << 24
|
||||||
export const FILE_REFERENCE_FLAG = 1 << 25
|
export const FILE_REFERENCE_FLAG: number = 1 << 25
|
||||||
|
|
||||||
export const CURRENT_VERSION = 48
|
export const CURRENT_VERSION = 48
|
||||||
|
|
||||||
|
|
|
@ -160,5 +160,5 @@ export abstract class BaseHttpProxyTcpTransport extends BaseTcpTransport {
|
||||||
* (unless you want to use a custom codec).
|
* (unless you want to use a custom codec).
|
||||||
*/
|
*/
|
||||||
export class HttpProxyTcpTransport extends BaseHttpProxyTcpTransport {
|
export class HttpProxyTcpTransport extends BaseHttpProxyTcpTransport {
|
||||||
_packetCodec = new IntermediatePacketCodec()
|
_packetCodec: IntermediatePacketCodec = new IntermediatePacketCodec()
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ export async function generateFakeTlsHeader(domain: string, secret: Buffer, cryp
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class FakeTlsPacketCodec extends WrappedCodec implements IPacketCodec {
|
export class FakeTlsPacketCodec extends WrappedCodec implements IPacketCodec {
|
||||||
protected _stream = Buffer.alloc(0)
|
protected _stream: Buffer = Buffer.alloc(0)
|
||||||
|
|
||||||
private _header!: Buffer
|
private _header!: Buffer
|
||||||
private _isFirstTls = true
|
private _isFirstTls = true
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { Interface as RlInterface } from 'node:readline'
|
import type { Interface as RlInterface } from 'node:readline'
|
||||||
import { createInterface } from 'node:readline'
|
import { createInterface } from 'node:readline'
|
||||||
|
import type { Readable } from 'node:stream'
|
||||||
|
|
||||||
import type { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'
|
import type { FileDownloadLocation, FileDownloadParameters, ITelegramStorageProvider, PartialOnly, User } from '@mtcute/core'
|
||||||
import type {
|
import type {
|
||||||
|
@ -157,7 +158,7 @@ export class TelegramClient extends TelegramClientBase {
|
||||||
return downloadToFile(this, filename, location, params)
|
return downloadToFile(this, filename, location, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined) {
|
downloadAsNodeStream(location: FileDownloadLocation, params?: FileDownloadParameters | undefined): Readable {
|
||||||
return downloadAsNodeStream(this, location, params)
|
return downloadAsNodeStream(this, location, params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,13 @@ const LEVEL_NAMES = isTty
|
||||||
const TAG_COLORS = [6, 2, 3, 4, 5, 1].map(i => `\x1B[3${i};1m`)
|
const TAG_COLORS = [6, 2, 3, 4, 5, 1].map(i => `\x1B[3${i};1m`)
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const defaultLoggingHandler = isTty
|
export const defaultLoggingHandler: (
|
||||||
|
color: number,
|
||||||
|
level: number,
|
||||||
|
tag: string,
|
||||||
|
fmt: string,
|
||||||
|
args: unknown[]
|
||||||
|
) => void = isTty
|
||||||
? (color: number, level: number, tag: string, fmt: string, args: unknown[]): void => {
|
? (color: number, level: number, tag: string, fmt: string, args: unknown[]): void => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args)
|
console.log(BASE_FORMAT + fmt, new Date().toISOString(), LEVEL_NAMES[level], TAG_COLORS[color], tag, ...args)
|
||||||
|
|
|
@ -25,7 +25,7 @@ export interface SqliteStorageDriverOptions {
|
||||||
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
export class SqliteStorageDriver extends BaseSqliteStorageDriver {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export { SqliteStorageDriver } from './driver.js'
|
||||||
export class SqliteStorage extends BaseSqliteStorage {
|
export class SqliteStorage extends BaseSqliteStorage {
|
||||||
constructor(
|
constructor(
|
||||||
readonly filename = ':memory:',
|
readonly filename = ':memory:',
|
||||||
readonly params?: SqliteStorageDriverOptions,
|
readonly params?: SqliteStorageDriverOptions | undefined,
|
||||||
) {
|
) {
|
||||||
super(new SqliteStorageDriver(filename, params))
|
super(new SqliteStorageDriver(filename, params))
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ export abstract class BaseNodeCryptoProvider extends BaseCryptoProvider {
|
||||||
return gunzipSync(data)
|
return gunzipSync(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
randomFill(buf: Uint8Array) {
|
randomFill(buf: Uint8Array): void {
|
||||||
randomFillSync(buf)
|
randomFillSync(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,11 @@ import type { UploadFileLike } from '@mtcute/core'
|
||||||
|
|
||||||
import { nodeStreamToWeb } from './stream-utils.js'
|
import { nodeStreamToWeb } from './stream-utils.js'
|
||||||
|
|
||||||
export async function normalizeFile(file: UploadFileLike) {
|
export async function normalizeFile(file: UploadFileLike): Promise<{
|
||||||
|
file: UploadFileLike
|
||||||
|
fileName?: string | undefined
|
||||||
|
fileSize?: number
|
||||||
|
} | null> {
|
||||||
if (typeof file === 'string') {
|
if (typeof file === 'string') {
|
||||||
file = createReadStream(file)
|
file = createReadStream(file)
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,5 +136,5 @@ export abstract class BaseTcpTransport extends EventEmitter implements ITelegram
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TcpTransport extends BaseTcpTransport {
|
export class TcpTransport extends BaseTcpTransport {
|
||||||
_packetCodec = new IntermediatePacketCodec()
|
_packetCodec: IntermediatePacketCodec = new IntermediatePacketCodec()
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue