feat: user status and typing related methods and updates
This commit is contained in:
parent
8a0c9984b5
commit
002d949a13
11 changed files with 551 additions and 24 deletions
|
@ -73,6 +73,7 @@ import { _parseEntities } from './methods/messages/parse-entities'
|
||||||
import { pinMessage } from './methods/messages/pin-message'
|
import { pinMessage } from './methods/messages/pin-message'
|
||||||
import { searchGlobal } from './methods/messages/search-global'
|
import { searchGlobal } from './methods/messages/search-global'
|
||||||
import { searchMessages } from './methods/messages/search-messages'
|
import { searchMessages } from './methods/messages/search-messages'
|
||||||
|
import { sendTyping } from './methods/messages/send-chat-action'
|
||||||
import { sendMediaGroup } from './methods/messages/send-media-group'
|
import { sendMediaGroup } from './methods/messages/send-media-group'
|
||||||
import { sendMedia } from './methods/messages/send-media'
|
import { sendMedia } from './methods/messages/send-media'
|
||||||
import { sendText } from './methods/messages/send-text'
|
import { sendText } from './methods/messages/send-text'
|
||||||
|
@ -105,6 +106,7 @@ import { getCommonChats } from './methods/users/get-common-chats'
|
||||||
import { getMe } from './methods/users/get-me'
|
import { getMe } from './methods/users/get-me'
|
||||||
import { getUsers } from './methods/users/get-users'
|
import { getUsers } from './methods/users/get-users'
|
||||||
import { resolvePeer } from './methods/users/resolve-peer'
|
import { resolvePeer } from './methods/users/resolve-peer'
|
||||||
|
import { setOffline } from './methods/users/set-offline'
|
||||||
import { IMessageEntityParser } from './parser'
|
import { IMessageEntityParser } from './parser'
|
||||||
import { Readable } from 'stream'
|
import { Readable } from 'stream'
|
||||||
import {
|
import {
|
||||||
|
@ -1673,6 +1675,50 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
chunkSize?: number
|
chunkSize?: number
|
||||||
}
|
}
|
||||||
): AsyncIterableIterator<Message>
|
): AsyncIterableIterator<Message>
|
||||||
|
/**
|
||||||
|
* Sends a current user/bot typing event
|
||||||
|
* to a conversation partner or group.
|
||||||
|
*
|
||||||
|
* This status is set for 6 seconds, and is
|
||||||
|
* automatically cancelled if you send a
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID
|
||||||
|
* @param action
|
||||||
|
* (default: `'typing'`)
|
||||||
|
* Chat action:
|
||||||
|
* - `typing` - user is typing
|
||||||
|
* - `cancel` to cancel previously sent event
|
||||||
|
* - `record_video` - user is recording a video
|
||||||
|
* - `upload_video` - user is uploading a video
|
||||||
|
* - `record_voice` - user is recording a voice note
|
||||||
|
* - `upload_voice` - user is uploading a voice note
|
||||||
|
* - `upload_photo` - user is uploading a photo
|
||||||
|
* - `upload_document` - user is sending a document
|
||||||
|
*
|
||||||
|
* @param progress For `upload_*` actions, progress of the upload (optional)
|
||||||
|
*/
|
||||||
|
sendChatAction(
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
action?:
|
||||||
|
| 'typing'
|
||||||
|
| 'cancel'
|
||||||
|
| 'record_video'
|
||||||
|
| 'upload_video'
|
||||||
|
| 'record_voice'
|
||||||
|
| 'upload_voice'
|
||||||
|
| 'upload_photo'
|
||||||
|
| 'upload_document'
|
||||||
|
| 'geo'
|
||||||
|
| 'contact'
|
||||||
|
| 'game'
|
||||||
|
| 'record_round'
|
||||||
|
| 'upload_round'
|
||||||
|
| 'group_call'
|
||||||
|
| 'history_import'
|
||||||
|
| tl.TypeSendMessageAction,
|
||||||
|
progress?: number
|
||||||
|
): Promise<void>
|
||||||
/**
|
/**
|
||||||
* Send a group of media.
|
* Send a group of media.
|
||||||
*
|
*
|
||||||
|
@ -2169,6 +2215,12 @@ export interface TelegramClient extends BaseTelegramClient {
|
||||||
resolvePeer(
|
resolvePeer(
|
||||||
peerId: InputPeerLike
|
peerId: InputPeerLike
|
||||||
): Promise<tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel>
|
): Promise<tl.TypeInputPeer | tl.TypeInputUser | tl.TypeInputChannel>
|
||||||
|
/**
|
||||||
|
* Change user status to offline or online
|
||||||
|
*
|
||||||
|
* @param offline (default: `true`) Whether the user is currently offline
|
||||||
|
*/
|
||||||
|
setOffline(offline?: boolean): Promise<void>
|
||||||
}
|
}
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class TelegramClient extends BaseTelegramClient {
|
export class TelegramClient extends BaseTelegramClient {
|
||||||
|
@ -2273,6 +2325,7 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
pinMessage = pinMessage
|
pinMessage = pinMessage
|
||||||
searchGlobal = searchGlobal
|
searchGlobal = searchGlobal
|
||||||
searchMessages = searchMessages
|
searchMessages = searchMessages
|
||||||
|
sendChatAction = sendTyping
|
||||||
sendMediaGroup = sendMediaGroup
|
sendMediaGroup = sendMediaGroup
|
||||||
sendMedia = sendMedia
|
sendMedia = sendMedia
|
||||||
sendText = sendText
|
sendText = sendText
|
||||||
|
@ -2301,4 +2354,5 @@ export class TelegramClient extends BaseTelegramClient {
|
||||||
getMe = getMe
|
getMe = getMe
|
||||||
getUsers = getUsers
|
getUsers = getUsers
|
||||||
resolvePeer = resolvePeer
|
resolvePeer = resolvePeer
|
||||||
|
setOffline = setOffline
|
||||||
}
|
}
|
||||||
|
|
81
packages/client/src/methods/messages/send-typing.ts
Normal file
81
packages/client/src/methods/messages/send-typing.ts
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
import { InputPeerLike } from '../../types'
|
||||||
|
import { TypingStatus } from '../../types/peers/typing-status'
|
||||||
|
import { normalizeToInputPeer } from '../../utils/peer-utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a current user/bot typing event
|
||||||
|
* to a conversation partner or group.
|
||||||
|
*
|
||||||
|
* This status is set for 6 seconds, and is
|
||||||
|
* automatically cancelled if you send a
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* @param chatId Chat ID
|
||||||
|
* @param status Typing status
|
||||||
|
* @param progress For `upload_*` and actions, progress of the upload
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function sendTyping(
|
||||||
|
this: TelegramClient,
|
||||||
|
chatId: InputPeerLike,
|
||||||
|
status: TypingStatus | tl.TypeSendMessageAction = 'typing',
|
||||||
|
progress = 0
|
||||||
|
): Promise<void> {
|
||||||
|
if (typeof status === 'string') {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.call({
|
||||||
|
_: 'messages.setTyping',
|
||||||
|
peer: normalizeToInputPeer(await this.resolvePeer(chatId)),
|
||||||
|
action: status
|
||||||
|
})
|
||||||
|
}
|
17
packages/client/src/methods/users/set-offline.ts
Normal file
17
packages/client/src/methods/users/set-offline.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { TelegramClient } from '../../client'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change user status to offline or online
|
||||||
|
*
|
||||||
|
* @param offline Whether the user is currently offline
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function setOffline(
|
||||||
|
this: TelegramClient,
|
||||||
|
offline = true
|
||||||
|
): Promise<void> {
|
||||||
|
await this.call({
|
||||||
|
_: 'account.updateStatus',
|
||||||
|
offline
|
||||||
|
})
|
||||||
|
}
|
38
packages/client/src/types/peers/typing-status.ts
Normal file
38
packages/client/src/types/peers/typing-status.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* User typing status. Used to provide detailed
|
||||||
|
* information about chat partners' actions:
|
||||||
|
* typing messages, recording/uploading attachments, etc.
|
||||||
|
*
|
||||||
|
* Can be:
|
||||||
|
* - `typing`: User is typing
|
||||||
|
* - `cancel`: User is not doing anything (used to cancel previously sent status)
|
||||||
|
* - `record_video`: User is recording a video
|
||||||
|
* - `upload_video`: User is uploading a video
|
||||||
|
* - `record_voice`: User is recording a voice message
|
||||||
|
* - `upload_voice`: User is uploading a voice message
|
||||||
|
* - `upload_photo`: User is uploading a photo
|
||||||
|
* - `upload_document`: User is uploading a document
|
||||||
|
* - `geo`: User is choosing a geolocation to share
|
||||||
|
* - `contact`: User is choosing a contact to share
|
||||||
|
* - `game`: User is playing a game
|
||||||
|
* - `record_round`: User is recording a round video message
|
||||||
|
* - `upload_round`: User is uploading a round video message
|
||||||
|
* - `speak_call`: *undocumented* User is speaking in a group call
|
||||||
|
* - `history_import`: *undocumented* User is importing history
|
||||||
|
*/
|
||||||
|
export type TypingStatus =
|
||||||
|
| 'typing'
|
||||||
|
| 'cancel'
|
||||||
|
| 'record_video'
|
||||||
|
| 'upload_video'
|
||||||
|
| 'record_voice'
|
||||||
|
| 'upload_voice'
|
||||||
|
| 'upload_photo'
|
||||||
|
| 'upload_document'
|
||||||
|
| 'geo'
|
||||||
|
| 'contact'
|
||||||
|
| 'game'
|
||||||
|
| 'record_round'
|
||||||
|
| 'upload_round'
|
||||||
|
| 'speak_call'
|
||||||
|
| 'history_import'
|
|
@ -25,12 +25,12 @@ export namespace User {
|
||||||
| 'within_month'
|
| 'within_month'
|
||||||
| 'long_time_ago'
|
| 'long_time_ago'
|
||||||
| 'bot'
|
| 'bot'
|
||||||
}
|
|
||||||
|
|
||||||
interface ParsedStatus {
|
export interface ParsedStatus {
|
||||||
status: User.Status
|
status: User.Status
|
||||||
lastOnline: Date | null
|
lastOnline: Date | null
|
||||||
nextOffline: Date | null
|
nextOffline: Date | null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
|
@ -117,40 +117,43 @@ export class User {
|
||||||
return this._user.lastName ?? null
|
return this._user.lastName ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parsedStatus?: ParsedStatus
|
static parseStatus(status: tl.TypeUserStatus, bot = false): User.ParsedStatus {
|
||||||
|
let ret: User.Status
|
||||||
private _parseStatus() {
|
|
||||||
let status: User.Status
|
|
||||||
let date: Date
|
let date: Date
|
||||||
|
|
||||||
const us = this._user.status
|
const us = status
|
||||||
if (!us) {
|
if (!us) {
|
||||||
status = 'long_time_ago'
|
ret = 'long_time_ago'
|
||||||
} else if (this._user.bot) {
|
} else if (bot) {
|
||||||
status = 'bot'
|
ret = 'bot'
|
||||||
} else if (us._ === 'userStatusOnline') {
|
} else if (us._ === 'userStatusOnline') {
|
||||||
status = 'online'
|
ret = 'online'
|
||||||
date = new Date(us.expires * 1000)
|
date = new Date(us.expires * 1000)
|
||||||
} else if (us._ === 'userStatusOffline') {
|
} else if (us._ === 'userStatusOffline') {
|
||||||
status = 'offline'
|
ret = 'offline'
|
||||||
date = new Date(us.wasOnline * 1000)
|
date = new Date(us.wasOnline * 1000)
|
||||||
} else if (us._ === 'userStatusRecently') {
|
} else if (us._ === 'userStatusRecently') {
|
||||||
status = 'recently'
|
ret = 'recently'
|
||||||
} else if (us._ === 'userStatusLastWeek') {
|
} else if (us._ === 'userStatusLastWeek') {
|
||||||
status = 'within_week'
|
ret = 'within_week'
|
||||||
} else if (us._ === 'userStatusLastMonth') {
|
} else if (us._ === 'userStatusLastMonth') {
|
||||||
status = 'within_month'
|
ret = 'within_month'
|
||||||
} else {
|
} else {
|
||||||
status = 'long_time_ago'
|
ret = 'long_time_ago'
|
||||||
}
|
}
|
||||||
|
|
||||||
this._parsedStatus = {
|
return {
|
||||||
status,
|
status: ret,
|
||||||
lastOnline: status === 'offline' ? date! : null,
|
lastOnline: ret === 'offline' ? date! : null,
|
||||||
nextOffline: status === 'online' ? date! : null,
|
nextOffline: ret === 'online' ? date! : null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _parsedStatus?: User.ParsedStatus
|
||||||
|
private _parseStatus() {
|
||||||
|
this._parsedStatus = User.parseStatus(this._user.status!, this._user.bot)
|
||||||
|
}
|
||||||
|
|
||||||
/** User's Last Seen & Online status */
|
/** User's Last Seen & Online status */
|
||||||
get status(): User.Status {
|
get status(): User.Status {
|
||||||
if (!this._parsedStatus) this._parseStatus()
|
if (!this._parsedStatus) this._parseStatus()
|
||||||
|
|
|
@ -9,3 +9,5 @@ chosen_inline_result = ChosenInlineResult
|
||||||
callback_query = CallbackQuery
|
callback_query = CallbackQuery
|
||||||
poll: PollUpdate = PollUpdate
|
poll: PollUpdate = PollUpdate
|
||||||
poll_vote = PollVoteUpdate
|
poll_vote = PollVoteUpdate
|
||||||
|
user_status: UserStatusUpdate = UserStatusUpdate
|
||||||
|
user_typing = UserTypingUpdate
|
||||||
|
|
|
@ -10,6 +10,8 @@ import {
|
||||||
CallbackQueryHandler,
|
CallbackQueryHandler,
|
||||||
PollUpdateHandler,
|
PollUpdateHandler,
|
||||||
PollVoteHandler,
|
PollVoteHandler,
|
||||||
|
UserStatusUpdateHandler,
|
||||||
|
UserTypingHandler,
|
||||||
} from './handler'
|
} from './handler'
|
||||||
// end-codegen-imports
|
// end-codegen-imports
|
||||||
import { filters, UpdateFilter } from './filters'
|
import { filters, UpdateFilter } from './filters'
|
||||||
|
@ -18,6 +20,8 @@ import { ChatMemberUpdate } from './updates'
|
||||||
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
||||||
import { PollUpdate } from './updates/poll-update'
|
import { PollUpdate } from './updates/poll-update'
|
||||||
import { PollVoteUpdate } from './updates/poll-vote'
|
import { PollVoteUpdate } from './updates/poll-vote'
|
||||||
|
import { UserStatusUpdate } from './updates/user-status-update'
|
||||||
|
import { UserTypingUpdate } from './updates/user-typing-update'
|
||||||
|
|
||||||
function _create<T extends UpdateHandler>(
|
function _create<T extends UpdateHandler>(
|
||||||
type: T['type'],
|
type: T['type'],
|
||||||
|
@ -291,5 +295,62 @@ export namespace handlers {
|
||||||
return _create('poll_vote', filter, handler)
|
return _create('poll_vote', filter, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an user status update handler
|
||||||
|
*
|
||||||
|
* @param handler User status update handler
|
||||||
|
*/
|
||||||
|
export function userStatusUpdate(
|
||||||
|
handler: UserStatusUpdateHandler['callback']
|
||||||
|
): UserStatusUpdateHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an user status update handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler User status update handler
|
||||||
|
*/
|
||||||
|
export function userStatusUpdate<Mod>(
|
||||||
|
filter: UpdateFilter<UserStatusUpdate, Mod>,
|
||||||
|
handler: UserStatusUpdateHandler<
|
||||||
|
filters.Modify<UserStatusUpdate, Mod>
|
||||||
|
>['callback']
|
||||||
|
): UserStatusUpdateHandler
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function userStatusUpdate(
|
||||||
|
filter: any,
|
||||||
|
handler?: any
|
||||||
|
): UserStatusUpdateHandler {
|
||||||
|
return _create('user_status', filter, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an user typing handler
|
||||||
|
*
|
||||||
|
* @param handler User typing handler
|
||||||
|
*/
|
||||||
|
export function userTyping(
|
||||||
|
handler: UserTypingHandler['callback']
|
||||||
|
): UserTypingHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an user typing handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler User typing handler
|
||||||
|
*/
|
||||||
|
export function userTyping<Mod>(
|
||||||
|
filter: UpdateFilter<UserTypingUpdate, Mod>,
|
||||||
|
handler: UserTypingHandler<
|
||||||
|
filters.Modify<UserTypingUpdate, Mod>
|
||||||
|
>['callback']
|
||||||
|
): UserTypingHandler
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
export function userTyping(filter: any, handler?: any): UserTypingHandler {
|
||||||
|
return _create('user_typing', filter, handler)
|
||||||
|
}
|
||||||
|
|
||||||
// end-codegen
|
// end-codegen
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import {
|
||||||
CallbackQueryHandler,
|
CallbackQueryHandler,
|
||||||
PollUpdateHandler,
|
PollUpdateHandler,
|
||||||
PollVoteHandler,
|
PollVoteHandler,
|
||||||
|
UserStatusUpdateHandler,
|
||||||
|
UserTypingHandler,
|
||||||
} from './handler'
|
} from './handler'
|
||||||
// end-codegen-imports
|
// end-codegen-imports
|
||||||
import { filters, UpdateFilter } from './filters'
|
import { filters, UpdateFilter } from './filters'
|
||||||
|
@ -32,6 +34,8 @@ import { ChatMemberUpdate } from './updates'
|
||||||
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
||||||
import { PollUpdate } from './updates/poll-update'
|
import { PollUpdate } from './updates/poll-update'
|
||||||
import { PollVoteUpdate } from './updates/poll-vote'
|
import { PollVoteUpdate } from './updates/poll-vote'
|
||||||
|
import { UserStatusUpdate } from './updates/user-status-update'
|
||||||
|
import { UserTypingUpdate } from './updates/user-typing-update'
|
||||||
|
|
||||||
const noop = () => {}
|
const noop = () => {}
|
||||||
|
|
||||||
|
@ -67,6 +71,10 @@ const callbackQueryParser: UpdateParser = [
|
||||||
'callback_query',
|
'callback_query',
|
||||||
(client, upd, users) => new CallbackQuery(client, upd as any, users),
|
(client, upd, users) => new CallbackQuery(client, upd as any, users),
|
||||||
]
|
]
|
||||||
|
const userTypingParser: UpdateParser = [
|
||||||
|
'user_typing',
|
||||||
|
(client, upd) => new UserTypingUpdate(client, upd as any)
|
||||||
|
]
|
||||||
|
|
||||||
const PARSERS: Partial<
|
const PARSERS: Partial<
|
||||||
Record<(tl.TypeUpdate | tl.TypeMessage)['_'], UpdateParser>
|
Record<(tl.TypeUpdate | tl.TypeMessage)['_'], UpdateParser>
|
||||||
|
@ -100,10 +108,17 @@ const PARSERS: Partial<
|
||||||
'poll_vote',
|
'poll_vote',
|
||||||
(client, upd, users) => new PollVoteUpdate(client, upd as any, users),
|
(client, upd, users) => new PollVoteUpdate(client, upd as any, users),
|
||||||
],
|
],
|
||||||
|
updateUserStatus: [
|
||||||
|
'user_status',
|
||||||
|
(client, upd) => new UserStatusUpdate(client, upd as any),
|
||||||
|
],
|
||||||
|
updateChannelUserTyping: userTypingParser,
|
||||||
|
updateChatUserTyping: userTypingParser,
|
||||||
|
updateUserTyping: userTypingParser,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dispatcher
|
* Updates dispatcher
|
||||||
*/
|
*/
|
||||||
export class Dispatcher {
|
export class Dispatcher {
|
||||||
private _groups: Record<number, UpdateHandler[]> = {}
|
private _groups: Record<number, UpdateHandler[]> = {}
|
||||||
|
@ -667,5 +682,66 @@ export class Dispatcher {
|
||||||
this._addKnownHandler('pollVote', filter, handler, group)
|
this._addKnownHandler('pollVote', filter, handler, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an user status update handler without any filters
|
||||||
|
*
|
||||||
|
* @param handler User status update handler
|
||||||
|
* @param group Handler group index
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
onUserStatusUpdate(
|
||||||
|
handler: UserStatusUpdateHandler['callback'],
|
||||||
|
group?: number
|
||||||
|
): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an user status update handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler User status update handler
|
||||||
|
* @param group Handler group index
|
||||||
|
*/
|
||||||
|
onUserStatusUpdate<Mod>(
|
||||||
|
filter: UpdateFilter<UserStatusUpdate, Mod>,
|
||||||
|
handler: UserStatusUpdateHandler<
|
||||||
|
filters.Modify<UserStatusUpdate, Mod>
|
||||||
|
>['callback'],
|
||||||
|
group?: number
|
||||||
|
): void
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
onUserStatusUpdate(filter: any, handler?: any, group?: number): void {
|
||||||
|
this._addKnownHandler('userStatusUpdate', filter, handler, group)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an user typing handler without any filters
|
||||||
|
*
|
||||||
|
* @param handler User typing handler
|
||||||
|
* @param group Handler group index
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
onUserTyping(handler: UserTypingHandler['callback'], group?: number): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an user typing handler with a filter
|
||||||
|
*
|
||||||
|
* @param filter Update filter
|
||||||
|
* @param handler User typing handler
|
||||||
|
* @param group Handler group index
|
||||||
|
*/
|
||||||
|
onUserTyping<Mod>(
|
||||||
|
filter: UpdateFilter<UserTypingUpdate, Mod>,
|
||||||
|
handler: UserTypingHandler<
|
||||||
|
filters.Modify<UserTypingUpdate, Mod>
|
||||||
|
>['callback'],
|
||||||
|
group?: number
|
||||||
|
): void
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
onUserTyping(filter: any, handler?: any, group?: number): void {
|
||||||
|
this._addKnownHandler('userTyping', filter, handler, group)
|
||||||
|
}
|
||||||
|
|
||||||
// end-codegen
|
// end-codegen
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import { ChatMemberUpdate } from './updates'
|
||||||
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
import { ChosenInlineResult } from './updates/chosen-inline-result'
|
||||||
import { PollUpdate } from './updates/poll-update'
|
import { PollUpdate } from './updates/poll-update'
|
||||||
import { PollVoteUpdate } from './updates/poll-vote'
|
import { PollVoteUpdate } from './updates/poll-vote'
|
||||||
|
import { UserStatusUpdate } from './updates/user-status-update'
|
||||||
|
import { UserTypingUpdate } from './updates/user-typing-update'
|
||||||
|
|
||||||
interface BaseUpdateHandler<Type, Handler, Checker> {
|
interface BaseUpdateHandler<Type, Handler, Checker> {
|
||||||
type: Type
|
type: Type
|
||||||
|
@ -73,6 +75,14 @@ export type PollVoteHandler<T = PollVoteUpdate> = ParsedUpdateHandler<
|
||||||
'poll_vote',
|
'poll_vote',
|
||||||
T
|
T
|
||||||
>
|
>
|
||||||
|
export type UserStatusUpdateHandler<T = UserStatusUpdate> = ParsedUpdateHandler<
|
||||||
|
'user_status',
|
||||||
|
T
|
||||||
|
>
|
||||||
|
export type UserTypingHandler<T = UserTypingUpdate> = ParsedUpdateHandler<
|
||||||
|
'user_typing',
|
||||||
|
T
|
||||||
|
>
|
||||||
|
|
||||||
export type UpdateHandler =
|
export type UpdateHandler =
|
||||||
| RawUpdateHandler
|
| RawUpdateHandler
|
||||||
|
@ -84,5 +94,7 @@ export type UpdateHandler =
|
||||||
| CallbackQueryHandler
|
| CallbackQueryHandler
|
||||||
| PollUpdateHandler
|
| PollUpdateHandler
|
||||||
| PollVoteHandler
|
| PollVoteHandler
|
||||||
|
| UserStatusUpdateHandler
|
||||||
|
| UserTypingHandler
|
||||||
|
|
||||||
// end-codegen
|
// end-codegen
|
||||||
|
|
66
packages/dispatcher/src/updates/user-status-update.ts
Normal file
66
packages/dispatcher/src/updates/user-status-update.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { TelegramClient, User } from '@mtcute/client'
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
import { makeInspectable } from '@mtcute/client/src/types/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User status has changed
|
||||||
|
*/
|
||||||
|
export class UserStatusUpdate {
|
||||||
|
readonly client: TelegramClient
|
||||||
|
readonly raw: tl.RawUpdateUserStatus
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
client: TelegramClient,
|
||||||
|
raw: tl.RawUpdateUserStatus
|
||||||
|
) {
|
||||||
|
this.client = client
|
||||||
|
this.raw = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the user whose status has updated
|
||||||
|
*/
|
||||||
|
get userId(): number {
|
||||||
|
return this.raw.userId
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parsedStatus?: User.ParsedStatus
|
||||||
|
private _parseStatus() {
|
||||||
|
this._parsedStatus = User.parseStatus(this.raw.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's new Last Seen & Online status
|
||||||
|
*/
|
||||||
|
get status(): User.Status {
|
||||||
|
if (!this._parsedStatus) this._parseStatus()
|
||||||
|
return this._parsedStatus!.status
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last time this user was seen online.
|
||||||
|
* Only available if {@link status} is `offline`
|
||||||
|
*/
|
||||||
|
get lastOnline(): Date | null {
|
||||||
|
if (!this._parsedStatus) this._parseStatus()
|
||||||
|
return this._parsedStatus!.lastOnline
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time when this user will automatically go offline.
|
||||||
|
* Only available if {@link status} is `online`
|
||||||
|
*/
|
||||||
|
get nextOffline(): Date | null {
|
||||||
|
if (!this._parsedStatus) this._parseStatus()
|
||||||
|
return this._parsedStatus!.nextOffline
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch information about the user
|
||||||
|
*/
|
||||||
|
getUser(): Promise<User> {
|
||||||
|
return this.client.getUsers(this.raw.userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeInspectable(UserStatusUpdate)
|
117
packages/dispatcher/src/updates/user-typing-update.ts
Normal file
117
packages/dispatcher/src/updates/user-typing-update.ts
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
import { BasicPeerType, Chat, MtCuteUnsupportedError, TelegramClient, User } from '@mtcute/client'
|
||||||
|
import { tl } from '@mtcute/tl'
|
||||||
|
import { getBarePeerId, MAX_CHANNEL_ID } from '@mtcute/core'
|
||||||
|
import { TypingStatus } from '@mtcute/client/src/types/peers/typing-status'
|
||||||
|
import { makeInspectable } from '@mtcute/client/src/types/utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's typing status has changed.
|
||||||
|
*
|
||||||
|
* This update is valid for 6 seconds.
|
||||||
|
*/
|
||||||
|
export class UserTypingUpdate {
|
||||||
|
readonly client: TelegramClient
|
||||||
|
readonly raw:
|
||||||
|
| tl.RawUpdateUserTyping
|
||||||
|
| tl.RawUpdateChatUserTyping
|
||||||
|
| tl.RawUpdateChannelUserTyping
|
||||||
|
|
||||||
|
constructor(client: TelegramClient, raw: UserTypingUpdate['raw']) {
|
||||||
|
this.client = client
|
||||||
|
this.raw = raw
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the user whose typing status changed
|
||||||
|
*/
|
||||||
|
get userId(): number {
|
||||||
|
return this.raw._ === 'updateUserTyping'
|
||||||
|
? this.raw.userId
|
||||||
|
: getBarePeerId(this.raw.fromId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marked ID of the chat where the user is typing,
|
||||||
|
*
|
||||||
|
* If the user is typing in PMs, this will
|
||||||
|
* equal to {@link userId}
|
||||||
|
*/
|
||||||
|
get chatId(): number {
|
||||||
|
switch (this.raw._) {
|
||||||
|
case 'updateUserTyping':
|
||||||
|
return this.raw.userId
|
||||||
|
case 'updateChatUserTyping':
|
||||||
|
return -this.raw.chatId
|
||||||
|
case 'updateChannelUserTyping':
|
||||||
|
return MAX_CHANNEL_ID - this.raw.channelId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the chat where this event has occurred
|
||||||
|
*/
|
||||||
|
get chatType(): BasicPeerType {
|
||||||
|
switch (this.raw._) {
|
||||||
|
case 'updateUserTyping':
|
||||||
|
return 'user'
|
||||||
|
case 'updateChatUserTyping':
|
||||||
|
return 'chat'
|
||||||
|
case 'updateChannelUserTyping':
|
||||||
|
return 'channel'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current typing status
|
||||||
|
*/
|
||||||
|
get status(): TypingStatus {
|
||||||
|
switch (this.raw.action._) {
|
||||||
|
case 'sendMessageTypingAction':
|
||||||
|
return 'typing'
|
||||||
|
case 'sendMessageCancelAction':
|
||||||
|
return 'cancel'
|
||||||
|
case 'sendMessageRecordVideoAction':
|
||||||
|
return 'record_video'
|
||||||
|
case 'sendMessageUploadVideoAction':
|
||||||
|
return 'upload_video'
|
||||||
|
case 'sendMessageRecordAudioAction':
|
||||||
|
return 'record_voice'
|
||||||
|
case 'sendMessageUploadAudioAction':
|
||||||
|
return 'upload_voice'
|
||||||
|
case 'sendMessageUploadPhotoAction':
|
||||||
|
return 'upload_photo'
|
||||||
|
case 'sendMessageUploadDocumentAction':
|
||||||
|
return 'upload_document'
|
||||||
|
case 'sendMessageGeoLocationAction':
|
||||||
|
return 'geo'
|
||||||
|
case 'sendMessageChooseContactAction':
|
||||||
|
return 'contact'
|
||||||
|
case 'sendMessageRecordRoundAction':
|
||||||
|
return 'record_round'
|
||||||
|
case 'sendMessageUploadRoundAction':
|
||||||
|
return 'upload_round'
|
||||||
|
case 'speakingInGroupCallAction':
|
||||||
|
return 'speak_call'
|
||||||
|
case 'sendMessageHistoryImportAction':
|
||||||
|
return 'history_import'
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new MtCuteUnsupportedError()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the user whose typing status has changed
|
||||||
|
*/
|
||||||
|
getUser(): Promise<User> {
|
||||||
|
return this.client.getUsers(this.userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the chat where the update has happenned
|
||||||
|
*/
|
||||||
|
getChat(): Promise<Chat> {
|
||||||
|
return this.client.getChat(this.chatId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeInspectable(UserTypingUpdate)
|
Loading…
Reference in a new issue