feat(core)!: timers!

breaking: setOffline renamed to sendOnline
This commit is contained in:
alina 🌸 2024-11-22 01:13:09 +03:00
parent 73a3f64fc3
commit 4682359769
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
28 changed files with 435 additions and 184 deletions

View file

@ -47,8 +47,8 @@
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "2.26.0", "@antfu/eslint-config": "2.26.0",
"@fuman/build": "https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc", "@fuman/build": "https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@types/deno": "npm:@teidesu/deno-types@1.46.3", "@types/deno": "npm:@teidesu/deno-types@1.46.3",
"@types/node": "20.10.0", "@types/node": "20.10.0",
"@types/ws": "8.5.4", "@types/ws": "8.5.4",

View file

@ -17,10 +17,10 @@
"@mtcute/html-parser": "workspace:^", "@mtcute/html-parser": "workspace:^",
"@mtcute/markdown-parser": "workspace:^", "@mtcute/markdown-parser": "workspace:^",
"@mtcute/wasm": "workspace:^", "@mtcute/wasm": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/bun": "https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc", "@fuman/bun": "https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc", "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4",
"@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc" "@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4"
}, },
"devDependencies": { "devDependencies": {
"@mtcute/test": "workspace:^" "@mtcute/test": "workspace:^"

View file

@ -10,8 +10,8 @@
"exports": "./src/index.ts", "exports": "./src/index.ts",
"dependencies": { "dependencies": {
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc" "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4"
}, },
"devDependencies": { "devDependencies": {
"@mtcute/test": "workspace:^" "@mtcute/test": "workspace:^"

View file

@ -19,9 +19,9 @@
"gen-updates": "node ./scripts/generate-updates.cjs" "gen-updates": "node ./scripts/generate-updates.cjs"
}, },
"dependencies": { "dependencies": {
"@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc", "@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc", "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@mtcute/file-id": "workspace:^", "@mtcute/file-id": "workspace:^",
"@mtcute/tl": "workspace:^", "@mtcute/tl": "workspace:^",
"@mtcute/tl-runtime": "workspace:^", "@mtcute/tl-runtime": "workspace:^",

View file

@ -1,7 +1,7 @@
import type { mtp } from '@mtcute/tl' import type { mtp } from '@mtcute/tl'
import { tl } from '@mtcute/tl' import { tl } from '@mtcute/tl'
import type Long from 'long' import type Long from 'long'
import { Emitter } from '@fuman/utils' import { Emitter, unknownToError } from '@fuman/utils'
import type { MtClientOptions } from '../network/client.js' import type { MtClientOptions } from '../network/client.js'
import { MtClient } from '../network/client.js' import { MtClient } from '../network/client.js'
@ -32,6 +32,7 @@ import type { TelegramStorageManagerExtraOptions } from './storage/storage.js'
import { TelegramStorageManager } from './storage/storage.js' import { TelegramStorageManager } from './storage/storage.js'
import { UpdatesManager } from './updates/manager.js' import { UpdatesManager } from './updates/manager.js'
import type { RawUpdateInfo, UpdatesManagerParams } from './updates/types.js' import type { RawUpdateInfo, UpdatesManagerParams } from './updates/types.js'
import { TimersManager } from './managers/timers.js'
export interface BaseTelegramClientOptions extends MtClientOptions { export interface BaseTelegramClientOptions extends MtClientOptions {
storage: ITelegramStorageProvider storage: ITelegramStorageProvider
@ -57,6 +58,7 @@ export class BaseTelegramClient implements ITelegramClient {
readonly crypto: ICryptoProvider readonly crypto: ICryptoProvider
readonly storage: TelegramStorageManager readonly storage: TelegramStorageManager
readonly platform: ICorePlatform readonly platform: ICorePlatform
readonly timers: TimersManager
readonly onServerUpdate: Emitter<tl.TypeUpdates> = new Emitter() readonly onServerUpdate: Emitter<tl.TypeUpdates> = new Emitter()
readonly onRawUpdate: Emitter<RawUpdateInfo> = new Emitter() readonly onRawUpdate: Emitter<RawUpdateInfo> = new Emitter()
@ -92,6 +94,8 @@ export class BaseTelegramClient implements ITelegramClient {
provider: this.params.storage, provider: this.params.storage,
...this.params.storageOptions, ...this.params.storageOptions,
}) })
this.timers = new TimersManager()
this.timers.onError(err => this.onError.emit(unknownToError(err)))
} }
readonly appConfig: AppConfigManager = new AppConfigManager(this) readonly appConfig: AppConfigManager = new AppConfigManager(this)
@ -142,6 +146,7 @@ export class BaseTelegramClient implements ITelegramClient {
} }
async close(): Promise<void> { async close(): Promise<void> {
this.timers.destroy()
this._connected = false this._connected = false
await this.mt.close() await this.mt.close()
this.updates?.stopLoop() this.updates?.stopLoop()

View file

@ -197,6 +197,7 @@ import { sendScheduled } from './methods/messages/send-scheduled.js'
import { sendText } from './methods/messages/send-text.js' import { sendText } from './methods/messages/send-text.js'
import { sendTyping } from './methods/messages/send-typing.js' import { sendTyping } from './methods/messages/send-typing.js'
import { sendVote } from './methods/messages/send-vote.js' import { sendVote } from './methods/messages/send-vote.js'
import { setTyping } from './methods/messages/set-typing.js'
import { translateMessage } from './methods/messages/translate-message.js' import { translateMessage } from './methods/messages/translate-message.js'
import { translateText } from './methods/messages/translate-text.js' import { translateText } from './methods/messages/translate-text.js'
import { unpinAllMessages } from './methods/messages/unpin-all-messages.js' import { unpinAllMessages } from './methods/messages/unpin-all-messages.js'
@ -279,7 +280,7 @@ import { setGlobalTtl } from './methods/users/set-global-ttl.js'
import { setMyBirthday } from './methods/users/set-my-birthday.js' import { setMyBirthday } from './methods/users/set-my-birthday.js'
import { setMyProfilePhoto } from './methods/users/set-my-profile-photo.js' import { setMyProfilePhoto } from './methods/users/set-my-profile-photo.js'
import { setMyUsername } from './methods/users/set-my-username.js' import { setMyUsername } from './methods/users/set-my-username.js'
import { setOffline } from './methods/users/set-offline.js' import { setOffline, setOnline } from './methods/users/set-online.js'
import { unblockUser } from './methods/users/unblock-user.js' import { unblockUser } from './methods/users/unblock-user.js'
import { updateProfile } from './methods/users/update-profile.js' import { updateProfile } from './methods/users/update-profile.js'
import { withParams } from './methods/misc/with-params.js' import { withParams } from './methods/misc/with-params.js'
@ -995,6 +996,13 @@ export interface TelegramClient extends ITelegramClient {
params: { params: {
userId: InputPeerLike userId: InputPeerLike
result: InputInlineResult result: InputInlineResult
/**
* Filters for the client to use when prompting the user for the
* chat to send the inline message to.
*
* Note that this is just a hint for the client, and the client is free to ignore it.
*/
filter?: tl.TypeInlineQueryPeerType[] | { filter?: tl.TypeInlineQueryPeerType[] | {
/** private chat with the bot itself */ /** private chat with the bot itself */
botSelf?: boolean botSelf?: boolean
@ -1002,8 +1010,11 @@ export interface TelegramClient extends ITelegramClient {
private?: boolean private?: boolean
/** private chats with other bots */ /** private chats with other bots */
bots?: boolean bots?: boolean
/** "basic" chats */
chats?: boolean chats?: boolean
/** supergroups */
supergroups?: boolean supergroups?: boolean
/** broadcast channels */
channels?: boolean channels?: boolean
} }
}): Promise<tl.messages.TypeBotPreparedInlineMessage> }): Promise<tl.messages.TypeBotPreparedInlineMessage>
@ -4200,6 +4211,7 @@ export interface TelegramClient extends ITelegramClient {
* @param chatId Chat ID * @param chatId Chat ID
* @param [status='typing'] Typing status * @param [status='typing'] Typing status
* @param params * @param params
* @deprecated - use {@link setTyping} instead
*/ */
sendTyping( sendTyping(
chatId: InputPeerLike, status?: Exclude<TypingStatus, 'interaction' | 'interaction_seen'> | tl.TypeSendMessageAction, chatId: InputPeerLike, status?: Exclude<TypingStatus, 'interaction' | 'interaction_seen'> | tl.TypeSendMessageAction,
@ -4234,6 +4246,40 @@ export interface TelegramClient extends ITelegramClient {
*/ */
options: null | MaybeArray<number | Uint8Array> options: null | MaybeArray<number | Uint8Array>
}): Promise<Poll> }): Promise<Poll>
/**
* Sets whether a user is typing in a specific chat
*
* This status is automatically renewed by mtcute until a further
* call with `sendMessageCancelAction` is made, or a message is sent to the chat.
* **Available**: both users and bots
*
*/
setTyping(
params: {
/** Chat ID where the user is currently typing */
peerId: InputPeerLike
/**
* Typing status to send (false is a shortcut for `{ _: 'sendMessageCancelAction' }`)
*
* @default `{ _: 'sendMessageTypingAction' }`
*/
status?: tl.TypeSendMessageAction | false
/**
* For `upload_*` and history import actions, progress of the upload
*/
progress?: number
/**
* Unique identifier of the business connection on behalf of which the action will be sent
*/
businessConnectionId?: string
/**
* For comment threads, ID of the thread (i.e. top message)
*/
threadId?: number
}): Promise<void>
/** /**
* Translate message text to a given language. * Translate message text to a given language.
* *
@ -5709,8 +5755,20 @@ export interface TelegramClient extends ITelegramClient {
* **Available**: 👤 users only * **Available**: 👤 users only
* *
* @param [offline=true] Whether the user is currently offline * @param [offline=true] Whether the user is currently offline
* @deprecated - use {@link setOnline} instead
*/ */
setOffline(offline?: boolean): Promise<void> setOffline(offline?: boolean): Promise<void>
/**
* Change user status to online or offline
*
* Once called with `true`, mtcute will keep notifying the server
* that the user is still online until a further call with `false` is made.
*
* **Available**: 👤 users only
*
* @param [online=true]
*/
setOnline(online?: boolean): Promise<void>
/** /**
* Unblock a user * Unblock a user
* *
@ -6452,6 +6510,9 @@ TelegramClient.prototype.sendTyping = function (...args) {
TelegramClient.prototype.sendVote = function (...args) { TelegramClient.prototype.sendVote = function (...args) {
return sendVote(this._client, ...args) return sendVote(this._client, ...args)
} }
TelegramClient.prototype.setTyping = function (...args) {
return setTyping(this._client, ...args)
}
TelegramClient.prototype.translateMessage = function (...args) { TelegramClient.prototype.translateMessage = function (...args) {
return translateMessage(this._client, ...args) return translateMessage(this._client, ...args)
} }
@ -6721,6 +6782,9 @@ TelegramClient.prototype.setMyUsername = function (...args) {
TelegramClient.prototype.setOffline = function (...args) { TelegramClient.prototype.setOffline = function (...args) {
return setOffline(this._client, ...args) return setOffline(this._client, ...args)
} }
TelegramClient.prototype.setOnline = function (...args) {
return setOnline(this._client, ...args)
}
TelegramClient.prototype.unblockUser = function (...args) { TelegramClient.prototype.unblockUser = function (...args) {
return unblockUser(this._client, ...args) return unblockUser(this._client, ...args)
} }

View file

@ -11,6 +11,7 @@ import type { AppConfigManager } from './managers/app-config-manager.js'
import type { TelegramStorageManager } from './storage/storage.js' import type { TelegramStorageManager } from './storage/storage.js'
import type { StringSessionData } from './utils/string-session.js' import type { StringSessionData } from './utils/string-session.js'
import type { RawUpdateInfo } from './updates/types.js' import type { RawUpdateInfo } from './updates/types.js'
import type { TimersManager } from './managers/timers.js'
/** /**
* Connection state of the client * Connection state of the client
@ -34,6 +35,7 @@ export interface ITelegramClient {
readonly log: Logger readonly log: Logger
readonly storage: PublicPart<TelegramStorageManager> readonly storage: PublicPart<TelegramStorageManager>
readonly appConfig: PublicPart<AppConfigManager> readonly appConfig: PublicPart<AppConfigManager>
readonly timers: Pick<TimersManager, 'create' | 'cancel' | 'exists'>
readonly stopSignal: AbortSignal readonly stopSignal: AbortSignal
readonly platform: ICorePlatform readonly platform: ICorePlatform

View file

@ -0,0 +1,50 @@
import { AsyncInterval } from '@fuman/utils'
export class TimersManager {
private _timers: Map<string, AsyncInterval> = new Map()
private _errorHandler?: (err: unknown) => void
constructor() {}
exists(key: string): boolean {
return this._timers.has(key)
}
create(
key: string,
handler: (abortSignal: AbortSignal) => Promise<void>,
interval: number,
startNow = false,
): void {
if (this._timers.has(key)) {
return
}
const timer = new AsyncInterval(handler, interval)
if (this._errorHandler) timer.onError(this._errorHandler)
this._timers.set(key, timer)
if (startNow) timer.startNow()
else timer.start()
}
cancel(key: string): void {
const timer = this._timers.get(key)
if (!timer) return
timer.stop()
this._timers.delete(key)
}
onError(handler: (err: unknown) => void): void {
this._errorHandler = handler
}
destroy(): void {
for (const timer of this._timers.values()) {
timer.stop()
}
this._timers.clear()
}
}

View file

@ -191,6 +191,7 @@ export { sendScheduled } from './methods/messages/send-scheduled.js'
export { sendText } from './methods/messages/send-text.js' export { sendText } from './methods/messages/send-text.js'
export { sendTyping } from './methods/messages/send-typing.js' export { sendTyping } from './methods/messages/send-typing.js'
export { sendVote } from './methods/messages/send-vote.js' export { sendVote } from './methods/messages/send-vote.js'
export { setTyping } from './methods/messages/set-typing.js'
export { translateMessage } from './methods/messages/translate-message.js' export { translateMessage } from './methods/messages/translate-message.js'
export { translateText } from './methods/messages/translate-text.js' export { translateText } from './methods/messages/translate-text.js'
export { unpinAllMessages } from './methods/messages/unpin-all-messages.js' export { unpinAllMessages } from './methods/messages/unpin-all-messages.js'
@ -281,6 +282,7 @@ export { setGlobalTtl } from './methods/users/set-global-ttl.js'
export { setMyBirthday } from './methods/users/set-my-birthday.js' export { setMyBirthday } from './methods/users/set-my-birthday.js'
export { setMyProfilePhoto } from './methods/users/set-my-profile-photo.js' export { setMyProfilePhoto } from './methods/users/set-my-profile-photo.js'
export { setMyUsername } from './methods/users/set-my-username.js' export { setMyUsername } from './methods/users/set-my-username.js'
export { setOffline } from './methods/users/set-offline.js' export { setOffline } from './methods/users/set-online.js'
export { setOnline } from './methods/users/set-online.js'
export { unblockUser } from './methods/users/unblock-user.js' export { unblockUser } from './methods/users/unblock-user.js'
export { updateProfile } from './methods/users/update-profile.js' export { updateProfile } from './methods/users/update-profile.js'

View file

@ -8,6 +8,7 @@ import { normalizeDate } from '../../utils/misc-utils.js'
import { resolvePeer } from '../users/resolve-peer.js' import { resolvePeer } from '../users/resolve-peer.js'
import { _normalizeQuickReplyShortcut } from './send-common.js' import { _normalizeQuickReplyShortcut } from './send-common.js'
import { _getTypingTimerId } from './set-typing.js'
// @exported // @exported
export interface ForwardMessageOptions { export interface ForwardMessageOptions {
@ -120,6 +121,8 @@ export async function forwardMessagesById(
const toPeer = await resolvePeer(client, toChatId) const toPeer = await resolvePeer(client, toChatId)
client.timers.cancel(_getTypingTimerId(toPeer))
const res = await client.call({ const res = await client.call({
_: 'messages.forwardMessages', _: 'messages.forwardMessages',
toPeer, toPeer,

View file

@ -13,6 +13,7 @@ import { resolvePeer } from '../users/resolve-peer.js'
import { _getDiscussionMessage } from './get-discussion-message.js' import { _getDiscussionMessage } from './get-discussion-message.js'
import { getMessages } from './get-messages.js' import { getMessages } from './get-messages.js'
import { _getTypingTimerId } from './set-typing.js'
// @exported // @exported
export interface CommonSendParams { export interface CommonSendParams {
@ -172,6 +173,8 @@ export async function _processCommonSendParameters(
}> { }> {
let peer = await resolvePeer(client, chatId) let peer = await resolvePeer(client, chatId)
client.timers.cancel(_getTypingTimerId(peer, params.businessConnectionId))
let replyTo = normalizeMessageId(params.replyTo) let replyTo = normalizeMessageId(params.replyTo)
const replyToPeer = typeof params.replyTo === 'number' ? undefined : params.replyTo?.chat.inputPeer const replyToPeer = typeof params.replyTo === 'number' ? undefined : params.replyTo?.chat.inputPeer

View file

@ -8,6 +8,45 @@ import { resolvePeer } from '../users/resolve-peer.js'
import { _maybeInvokeWithBusinessConnection } from './_business-connection.js' import { _maybeInvokeWithBusinessConnection } from './_business-connection.js'
export function _mapTypingStatus(status: Exclude<TypingStatus, 'interaction' | 'interaction_seen'>, progress: number = 0): tl.TypeSendMessageAction {
switch (status) {
case 'typing':
return { _: 'sendMessageTypingAction' }
case 'cancel':
return { _: 'sendMessageCancelAction' }
case 'record_video':
return { _: 'sendMessageRecordVideoAction' }
case 'upload_video':
return { _: 'sendMessageUploadVideoAction', progress }
case 'record_voice':
return { _: 'sendMessageRecordAudioAction' }
case 'upload_voice':
return { _: 'sendMessageUploadAudioAction', progress }
case 'upload_photo':
return { _: 'sendMessageUploadPhotoAction', progress }
case 'upload_document':
return { _: 'sendMessageUploadDocumentAction', progress }
case 'geo':
return { _: 'sendMessageGeoLocationAction' }
case 'contact':
return { _: 'sendMessageChooseContactAction' }
case 'game':
return { _: 'sendMessageGamePlayAction' }
case 'record_round':
return { _: 'sendMessageRecordRoundAction' }
case 'upload_round':
return { _: 'sendMessageUploadRoundAction', progress }
case 'speak_call':
return { _: 'speakingInGroupCallAction' }
case 'history_import':
return { _: 'sendMessageHistoryImportAction', progress }
case 'sticker':
return { _: 'sendMessageChooseStickerAction' }
default:
assertNever(status)
}
}
/** /**
* Sends a current user/bot typing event * Sends a current user/bot typing event
* to a conversation partner or group. * to a conversation partner or group.
@ -16,6 +55,8 @@ import { _maybeInvokeWithBusinessConnection } from './_business-connection.js'
* automatically cancelled if you send a * automatically cancelled if you send a
* message. * message.
* *
* If you need a continuous typing status, use {@link setTyping} instead.
*
* @param chatId Chat ID * @param chatId Chat ID
* @param status Typing status * @param status Typing status
* @param params * @param params
@ -42,60 +83,7 @@ export async function sendTyping(
}, },
): Promise<void> { ): Promise<void> {
if (typeof status === 'string') { if (typeof status === 'string') {
const progress = params?.progress ?? 0 status = _mapTypingStatus(status, params?.progress ?? 0)
switch (status) {
case 'typing':
status = { _: 'sendMessageTypingAction' }
break
case 'cancel':
status = { _: 'sendMessageCancelAction' }
break
case 'record_video':
status = { _: 'sendMessageRecordVideoAction' }
break
case 'upload_video':
status = { _: 'sendMessageUploadVideoAction', progress }
break
case 'record_voice':
status = { _: 'sendMessageRecordAudioAction' }
break
case 'upload_voice':
status = { _: 'sendMessageUploadAudioAction', progress }
break
case 'upload_photo':
status = { _: 'sendMessageUploadPhotoAction', progress }
break
case 'upload_document':
status = { _: 'sendMessageUploadDocumentAction', progress }
break
case 'geo':
status = { _: 'sendMessageGeoLocationAction' }
break
case 'contact':
status = { _: 'sendMessageChooseContactAction' }
break
case 'game':
status = { _: 'sendMessageGamePlayAction' }
break
case 'record_round':
status = { _: 'sendMessageRecordRoundAction' }
break
case 'upload_round':
status = { _: 'sendMessageUploadRoundAction', progress }
break
case 'speak_call':
status = { _: 'speakingInGroupCallAction' }
break
case 'history_import':
status = { _: 'sendMessageHistoryImportAction', progress }
break
case 'sticker':
status = { _: 'sendMessageChooseStickerAction' }
break
default:
assertNever(status)
}
} }
const r = await _maybeInvokeWithBusinessConnection(client, params?.businessConnectionId, { const r = await _maybeInvokeWithBusinessConnection(client, params?.businessConnectionId, {

View file

@ -0,0 +1,91 @@
import type { tl } from '@mtcute/tl'
import { getMarkedPeerId } from '../../../utils/peer-utils.js'
import type { ITelegramClient } from '../../client.types.js'
import type { InputPeerLike } from '../../types/peers/peer.js'
import { resolvePeer } from '../users/resolve-peer.js'
import type { TypingStatus } from '../../types/peers/typing-status.js'
import { _maybeInvokeWithBusinessConnection } from './_business-connection.js'
import { _mapTypingStatus } from './send-typing.js'
export function _getTypingTimerId(peer: tl.TypeInputPeer, businessId?: string): string {
let base = `typing:${getMarkedPeerId(peer)}`
if (businessId) base += `:b${businessId}`
return base
}
const TIMER_INTERVAL = 5_000 // 5 seconds
/**
* Sets whether a user is typing in a specific chat
*
* This status is automatically renewed by mtcute until a further
* call with `cancel` is made, or a message is sent to the chat.
*/
export async function setTyping(
client: ITelegramClient,
params: {
/** Chat ID where the user is currently typing */
peerId: InputPeerLike
/**
* Typing status to send
*
* @default `typing`
*/
status?: Exclude<TypingStatus, 'interaction' | 'interaction_seen'> | tl.TypeSendMessageAction
/**
* For `upload_*` and history import actions, progress of the upload
*/
progress?: number
/**
* Unique identifier of the business connection on behalf of which the action will be sent
*/
businessConnectionId?: string
/**
* For comment threads, ID of the thread (i.e. top message)
*/
threadId?: number
},
): Promise<void> {
const {
peerId,
businessConnectionId,
threadId,
} = params
let status = params.status ?? 'typing'
if (typeof status === 'string') status = _mapTypingStatus(status)
const peer = await resolvePeer(client, peerId)
const timerId = _getTypingTimerId(peer, businessConnectionId)
if (client.timers.exists(timerId)) {
client.timers.cancel(timerId)
}
if (status._ === 'sendMessageCancelAction') {
await client.call({
_: 'messages.setTyping',
peer,
action: status,
topMsgId: threadId,
})
return
}
client.timers.create(timerId, async (abortSignal) => {
await _maybeInvokeWithBusinessConnection(client, params?.businessConnectionId, {
_: 'messages.setTyping',
peer,
action: status,
topMsgId: threadId,
}, { abortSignal })
}, TIMER_INTERVAL, true)
}

View file

@ -1,13 +0,0 @@
import type { ITelegramClient } from '../../client.types.js'
/**
* Change user status to offline or online
*
* @param offline Whether the user is currently offline
*/
export async function setOffline(client: ITelegramClient, offline = true): Promise<void> {
await client.call({
_: 'account.updateStatus',
offline,
})
}

View file

@ -0,0 +1,45 @@
import type { ITelegramClient } from '../../client.types.js'
/**
* Change user status to offline or online once,
* which will expire after a while (currently ~5 minutes)
*
* For continuously sending online/offline status, use {@link setOnline}
*
* @param online Whether the user is currently online
*/
export async function sendOnline(client: ITelegramClient, online: boolean): Promise<void> {
await client.call({
_: 'account.updateStatus',
offline: !online,
})
}
const TIMER_ID = 'online'
const TIMER_INTERVAL = 240_000 // 4 minutes
/**
* Change user status to online or offline
*
* Once called with `true`, mtcute will keep notifying the server
* that the user is still online until a further call with `false` is made.
*
* @param online
*/
export async function setOnline(client: ITelegramClient, online = true): Promise<void> {
if (online) {
client.timers.create(TIMER_ID, async (abortSignal) => {
await client.call({
_: 'account.updateStatus',
offline: false,
}, { abortSignal })
}, TIMER_INTERVAL, true)
} else {
client.timers.cancel(TIMER_ID)
await client.call({
_: 'account.updateStatus',
offline: true,
})
}
}

View file

@ -1,5 +1,5 @@
import type { tl } from '@mtcute/tl' import type { tl } from '@mtcute/tl'
import { Emitter } from '@fuman/utils' import { Emitter, unknownToError } from '@fuman/utils'
import type { RpcCallOptions } from '../../network/network-manager.js' import type { RpcCallOptions } from '../../network/network-manager.js'
import type { MustEqual } from '../../types/utils.js' import type { MustEqual } from '../../types/utils.js'
@ -8,6 +8,7 @@ import type { ConnectionState, ITelegramClient } from '../client.types.js'
import { PeersIndex } from '../types/peers/peers-index.js' import { PeersIndex } from '../types/peers/peers-index.js'
import type { ICorePlatform } from '../../types/platform' import type { ICorePlatform } from '../../types/platform'
import type { RawUpdateInfo } from '../updates/types.js' import type { RawUpdateInfo } from '../updates/types.js'
import { TimersManager } from '../managers/timers.js'
import { AppConfigManagerProxy } from './app-config.js' import { AppConfigManagerProxy } from './app-config.js'
import { WorkerInvoker } from './invoker.js' import { WorkerInvoker } from './invoker.js'
@ -30,6 +31,10 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
readonly storage: TelegramStorageProxy readonly storage: TelegramStorageProxy
readonly appConfig: AppConfigManagerProxy readonly appConfig: AppConfigManagerProxy
// todo: ideally timers should be handled on the worker side,
// but i'm not sure yet of the best way to handle multiple clients (e.g. in SharedWorker-s)
// (with one worker client it's not that big of a deal)
readonly timers: TimersManager
// bound methods // bound methods
readonly prepare: ITelegramClient['prepare'] readonly prepare: ITelegramClient['prepare']
@ -87,6 +92,9 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
this.startUpdatesLoop = bind('startUpdatesLoop') this.startUpdatesLoop = bind('startUpdatesLoop')
this.stopUpdatesLoop = bind('stopUpdatesLoop') this.stopUpdatesLoop = bind('stopUpdatesLoop')
this.getMtprotoMessageId = bind('getMtprotoMessageId') this.getMtprotoMessageId = bind('getMtprotoMessageId')
this.timers = new TimersManager()
this.timers.onError(err => this.onError.emit(unknownToError(err)))
} }
call<T extends tl.RpcMethod>( call<T extends tl.RpcMethod>(
@ -143,6 +151,7 @@ export abstract class TelegramWorkerPort<Custom extends WorkerCustomMethods> imp
private _destroyed = false private _destroyed = false
destroy(terminate = false): void { destroy(terminate = false): void {
if (this._destroyed) return if (this._destroyed) return
this.timers.destroy()
this._connection[1]() this._connection[1]()
this._destroyed = true this._destroyed = true

View file

@ -177,6 +177,7 @@ export abstract class PersistentConnection {
} }
async destroy(): Promise<void> { async destroy(): Promise<void> {
this._destroyed = true
await this._fuman.close() await this._fuman.close()
} }

View file

@ -2026,6 +2026,7 @@ export class SessionConnection extends PersistentConnection {
const enc = this._session.encryptMessage(result) const enc = this._session.encryptMessage(result)
const promise = this.send(enc).catch((err: Error) => { const promise = this.send(enc).catch((err: Error) => {
if (this._destroyed) return
this.log.error('error while sending pending messages (root msg_id = %l): %e', rootMsgId, err) this.log.error('error while sending pending messages (root msg_id = %l): %e', rootMsgId, err)
// put acks in the front so they are the first to be sent // put acks in the front so they are the first to be sent

View file

@ -14,9 +14,9 @@
}, },
"dependencies": { "dependencies": {
"@db/sqlite": "npm:@jsr/db__sqlite@0.12.0", "@db/sqlite": "npm:@jsr/db__sqlite@0.12.0",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc", "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4",
"@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc", "@fuman/io": "https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4",
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@mtcute/html-parser": "workspace:^", "@mtcute/html-parser": "workspace:^",
"@mtcute/markdown-parser": "workspace:^", "@mtcute/markdown-parser": "workspace:^",

View file

@ -13,7 +13,7 @@
}, },
"dependencies": { "dependencies": {
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"events": "3.2.0" "events": "3.2.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -12,7 +12,7 @@
}, },
"dependencies": { "dependencies": {
"@mtcute/tl-runtime": "workspace:^", "@mtcute/tl-runtime": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"long": "5.2.3" "long": "5.2.3"
} }
} }

View file

@ -17,9 +17,9 @@
"@mtcute/html-parser": "workspace:^", "@mtcute/html-parser": "workspace:^",
"@mtcute/markdown-parser": "workspace:^", "@mtcute/markdown-parser": "workspace:^",
"@mtcute/wasm": "workspace:^", "@mtcute/wasm": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc", "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4",
"@fuman/node": "https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc", "@fuman/node": "https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4",
"better-sqlite3": "11.3.0" "better-sqlite3": "11.3.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -25,8 +25,8 @@
}, },
"dependencies": { "dependencies": {
"long": "5.2.3", "long": "5.2.3",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc" "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4"
}, },
"devDependencies": { "devDependencies": {
"@mtcute/tl-utils": "workspace:^" "@mtcute/tl-utils": "workspace:^"

View file

@ -12,6 +12,6 @@
}, },
"dependencies": { "dependencies": {
"long": "5.2.3", "long": "5.2.3",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc" "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4"
} }
} }

View file

@ -23,8 +23,8 @@
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@mtcute/node": "workspace:^", "@mtcute/node": "workspace:^",
"@mtcute/tl-utils": "workspace:^", "@mtcute/tl-utils": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc", "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4",
"@fuman/fetch": "https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc", "@fuman/fetch": "https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
"cheerio": "1.0.0-rc.12", "cheerio": "1.0.0-rc.12",
"csv-parse": "^5.5.0", "csv-parse": "^5.5.0",

View file

@ -18,6 +18,6 @@
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@mtcute/node": "workspace:^", "@mtcute/node": "workspace:^",
"@mtcute/web": "workspace:^", "@mtcute/web": "workspace:^",
"@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc" "@fuman/utils": "https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4"
} }
} }

View file

@ -15,7 +15,7 @@
"dependencies": { "dependencies": {
"@mtcute/core": "workspace:^", "@mtcute/core": "workspace:^",
"@mtcute/wasm": "workspace:^", "@mtcute/wasm": "workspace:^",
"@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc", "@fuman/net": "https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4",
"events": "3.2.0" "events": "3.2.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -15,11 +15,11 @@ importers:
specifier: 2.26.0 specifier: 2.26.0
version: 2.26.0(@typescript-eslint/utils@8.14.0(eslint@9.9.0)(typescript@5.5.4))(@vue/compiler-sfc@3.5.13)(eslint@9.9.0)(typescript@5.5.4)(vitest@2.0.5(@types/node@20.10.0)(@vitest/browser@2.0.5)(@vitest/ui@2.0.5)) version: 2.26.0(@typescript-eslint/utils@8.14.0(eslint@9.9.0)(typescript@5.5.4))(@vue/compiler-sfc@3.5.13)(eslint@9.9.0)(typescript@5.5.4)(vitest@2.0.5(@types/node@20.10.0)(@vitest/browser@2.0.5)(@vitest/ui@2.0.5))
'@fuman/build': '@fuman/build':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc(tough-cookie@4.1.4)(typescript@5.5.4)(vite@5.4.2(@types/node@20.10.0)) version: https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4(tough-cookie@4.1.4)(typescript@5.5.4)(vite@5.4.2(@types/node@20.10.0))
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@types/deno': '@types/deno':
specifier: npm:@teidesu/deno-types@1.46.3 specifier: npm:@teidesu/deno-types@1.46.3
version: '@teidesu/deno-types@1.46.3' version: '@teidesu/deno-types@1.46.3'
@ -105,17 +105,17 @@ importers:
packages/bun: packages/bun:
dependencies: dependencies:
'@fuman/bun': '@fuman/bun':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4
'@fuman/io': '@fuman/io':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -136,11 +136,11 @@ importers:
packages/convert: packages/convert:
dependencies: dependencies:
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -152,14 +152,14 @@ importers:
packages/core: packages/core:
dependencies: dependencies:
'@fuman/io': '@fuman/io':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/file-id': '@mtcute/file-id':
specifier: workspace:^ specifier: workspace:^
version: link:../file-id version: link:../file-id
@ -239,14 +239,14 @@ importers:
specifier: npm:@jsr/db__sqlite@0.12.0 specifier: npm:@jsr/db__sqlite@0.12.0
version: '@jsr/db__sqlite@0.12.0' version: '@jsr/db__sqlite@0.12.0'
'@fuman/io': '@fuman/io':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -270,8 +270,8 @@ importers:
packages/dispatcher: packages/dispatcher:
dependencies: dependencies:
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -286,8 +286,8 @@ importers:
packages/file-id: packages/file-id:
dependencies: dependencies:
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/tl-runtime': '@mtcute/tl-runtime':
specifier: workspace:^ specifier: workspace:^
version: link:../tl-runtime version: link:../tl-runtime
@ -328,14 +328,14 @@ importers:
packages/node: packages/node:
dependencies: dependencies:
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/node': '@fuman/node':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -362,11 +362,11 @@ importers:
packages/test: packages/test:
dependencies: dependencies:
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -397,11 +397,11 @@ importers:
version: 5.2.3 version: 5.2.3
devDependencies: devDependencies:
'@fuman/fetch': '@fuman/fetch':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc(tough-cookie@4.1.4)(zod@3.23.8) version: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4(tough-cookie@4.1.4)(zod@3.23.8)
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -427,8 +427,8 @@ importers:
packages/tl-runtime: packages/tl-runtime:
dependencies: dependencies:
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
long: long:
specifier: 5.2.3 specifier: 5.2.3
version: 5.2.3 version: 5.2.3
@ -445,8 +445,8 @@ importers:
packages/wasm: packages/wasm:
devDependencies: devDependencies:
'@fuman/utils': '@fuman/utils':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -460,8 +460,8 @@ importers:
packages/web: packages/web:
dependencies: dependencies:
'@fuman/net': '@fuman/net':
specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc specifier: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
version: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc version: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@mtcute/core': '@mtcute/core':
specifier: workspace:^ specifier: workspace:^
version: link:../core version: link:../core
@ -904,20 +904,20 @@ packages:
resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@fuman/build@https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc': '@fuman/build@https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4}
version: 0.0.1 version: 0.0.1
hasBin: true hasBin: true
peerDependencies: peerDependencies:
typescript: 5.5.4 typescript: 5.5.4
vite: ^5.4.0 vite: ^5.4.0
'@fuman/bun@https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc': '@fuman/bun@https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4}
version: 0.0.1 version: 0.0.1
'@fuman/fetch@https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc': '@fuman/fetch@https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4}
version: 0.0.1 version: 0.0.1
peerDependencies: peerDependencies:
tough-cookie: ^5.0.0 || ^4.0.0 tough-cookie: ^5.0.0 || ^4.0.0
@ -934,20 +934,20 @@ packages:
zod: zod:
optional: true optional: true
'@fuman/io@https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc': '@fuman/io@https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4}
version: 0.0.1 version: 0.0.1
'@fuman/net@https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc': '@fuman/net@https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4}
version: 0.0.1 version: 0.0.1
'@fuman/node@https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc': '@fuman/node@https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4}
version: 0.0.1 version: 0.0.1
'@fuman/utils@https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc': '@fuman/utils@https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4':
resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc} resolution: {tarball: https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4}
version: 0.0.1 version: 0.0.1
'@humanwhocodes/module-importer@1.0.1': '@humanwhocodes/module-importer@1.0.1':
@ -4253,13 +4253,13 @@ snapshots:
'@eslint/object-schema@2.1.4': {} '@eslint/object-schema@2.1.4': {}
'@fuman/build@https://pkg.pr.new/teidesu/fuman/@fuman/build@a3f52dc(tough-cookie@4.1.4)(typescript@5.5.4)(vite@5.4.2(@types/node@20.10.0))': '@fuman/build@https://pkg.pr.new/teidesu/fuman/@fuman/build@6017eb4(tough-cookie@4.1.4)(typescript@5.5.4)(vite@5.4.2(@types/node@20.10.0))':
dependencies: dependencies:
'@drizzle-team/brocli': 0.10.2 '@drizzle-team/brocli': 0.10.2
'@fuman/fetch': https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc(tough-cookie@4.1.4)(zod@3.23.8) '@fuman/fetch': https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4(tough-cookie@4.1.4)(zod@3.23.8)
'@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc '@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/node': https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc '@fuman/node': https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
cross-spawn: 7.0.3 cross-spawn: 7.0.3
detect-indent: 7.0.1 detect-indent: 7.0.1
js-yaml: 4.1.0 js-yaml: 4.1.0
@ -4274,35 +4274,35 @@ snapshots:
- valibot - valibot
- yup - yup
'@fuman/bun@https://pkg.pr.new/teidesu/fuman/@fuman/bun@a3f52dc': '@fuman/bun@https://pkg.pr.new/teidesu/fuman/@fuman/bun@6017eb4':
dependencies: dependencies:
'@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc '@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/net': https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc '@fuman/net': https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@fuman/fetch@https://pkg.pr.new/teidesu/fuman/@fuman/fetch@a3f52dc(tough-cookie@4.1.4)(zod@3.23.8)': '@fuman/fetch@https://pkg.pr.new/teidesu/fuman/@fuman/fetch@6017eb4(tough-cookie@4.1.4)(zod@3.23.8)':
dependencies: dependencies:
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
optionalDependencies: optionalDependencies:
tough-cookie: 4.1.4 tough-cookie: 4.1.4
zod: 3.23.8 zod: 3.23.8
'@fuman/io@https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc': '@fuman/io@https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4':
dependencies: dependencies:
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@fuman/net@https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc': '@fuman/net@https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4':
dependencies: dependencies:
'@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc '@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@fuman/node@https://pkg.pr.new/teidesu/fuman/@fuman/node@a3f52dc': '@fuman/node@https://pkg.pr.new/teidesu/fuman/@fuman/node@6017eb4':
dependencies: dependencies:
'@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@a3f52dc '@fuman/io': https://pkg.pr.new/teidesu/fuman/@fuman/io@6017eb4
'@fuman/net': https://pkg.pr.new/teidesu/fuman/@fuman/net@a3f52dc '@fuman/net': https://pkg.pr.new/teidesu/fuman/@fuman/net@6017eb4
'@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc '@fuman/utils': https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4
'@fuman/utils@https://pkg.pr.new/teidesu/fuman/@fuman/utils@a3f52dc': {} '@fuman/utils@https://pkg.pr.new/teidesu/fuman/@fuman/utils@6017eb4': {}
'@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/module-importer@1.0.1': {}