refactor: prefer switches to else-if chains

This commit is contained in:
teidesu 2021-05-12 17:58:45 +03:00
parent 6879ae945a
commit 9be7b0d6c9
10 changed files with 704 additions and 611 deletions

View file

@ -102,24 +102,31 @@ export async function getChatMembers(
const type = params.type ?? 'recent' const type = params.type ?? 'recent'
let filter: tl.TypeChannelParticipantsFilter let filter: tl.TypeChannelParticipantsFilter
if (type === 'all') { switch (type) {
filter = { _: 'channelParticipantsSearch', q } case 'all':
} else if (type === 'banned') { filter = { _: 'channelParticipantsSearch', q }
filter = { _: 'channelParticipantsKicked', q } break
} else if (type === 'restricted') { case 'banned':
filter = { _: 'channelParticipantsBanned', q } filter = { _: 'channelParticipantsKicked', q }
} else if (type === 'mention') { break
filter = { _: 'channelParticipantsMentions', q } case 'restricted':
} else if (type === 'bots') { filter = { _: 'channelParticipantsBanned', q }
filter = { _: 'channelParticipantsBots' } break
} else if (type === 'recent') { case 'mention':
filter = { _: 'channelParticipantsRecent' } filter = { _: 'channelParticipantsMentions', q }
} else if (type === 'admins') { break
filter = { _: 'channelParticipantsAdmins' } case 'bots':
} else if (type === 'contacts') { filter = { _: 'channelParticipantsBots' }
filter = { _: 'channelParticipantsContacts', q } break
} else { case 'recent':
return type as never filter = { _: 'channelParticipantsRecent' }
break
case 'admins':
filter = { _: 'channelParticipantsAdmins' }
break
case 'contacts':
filter = { _: 'channelParticipantsContacts', q }
break
} }
const res = await this.call({ const res = await this.call({

View file

@ -208,8 +208,7 @@ async function _loadDifference(
qts: 0, qts: 0,
}) })
if (diff._ === 'updates.differenceEmpty') if (diff._ === 'updates.differenceEmpty') return
return
if (diff._ === 'updates.differenceTooLong') { if (diff._ === 'updates.differenceTooLong') {
this._pts = diff.pts this._pts = diff.pts
@ -340,7 +339,8 @@ async function _loadChannelDifference(
} }
diff.messages.forEach((message) => { diff.messages.forEach((message) => {
if (noDispatch && noDispatch.msg[channelId]?.[message.id]) return if (noDispatch && noDispatch.msg[channelId]?.[message.id])
return
this.dispatchUpdate(message, users, chats) this.dispatchUpdate(message, users, chats)
}) })
@ -409,351 +409,359 @@ export function _handleUpdate(
// i tried my best to follow the documentation, but i still may have missed something. // i tried my best to follow the documentation, but i still may have missed something.
// feel free to contribute! // feel free to contribute!
// reference: https://core.telegram.org/api/updates // reference: https://core.telegram.org/api/updates
if (update._ === 'updatesTooLong') { switch (update._) {
// "there are too many events pending to be pushed to the client", we need to fetch them manually case 'updatesTooLong': // "there are too many events pending to be pushed to the client", we need to fetch them manually
await _loadDifference.call(this, noDispatchIndex) await _loadDifference.call(this, noDispatchIndex)
} else if ( break
update._ === 'updates' || case 'updates':
update._ === 'updatesCombined' case 'updatesCombined': {
) { // const seqStart =
// const seqStart = // update._ === 'updatesCombined'
// update._ === 'updatesCombined' // ? update.seqStart
// ? update.seqStart // : update.seq
// : update.seq // const nextLocalSeq = this._seq + 1
// const nextLocalSeq = this._seq + 1 //
// // debug('received %s (seq_start=%d, seq_end=%d)', update._, seqStart, update.seq)
// debug('received %s (seq_start=%d, seq_end=%d)', update._, seqStart, update.seq) //
// // if (nextLocalSeq > seqStart)
// if (nextLocalSeq > seqStart) // // "the updates were already applied, and must be ignored"
// // "the updates were already applied, and must be ignored" // return
// return // if (nextLocalSeq < seqStart)
// if (nextLocalSeq < seqStart) // // "there's an updates gap that must be filled"
// // "there's an updates gap that must be filled" // // loading difference will also load any updates contained
// // loading difference will also load any updates contained // // in this update, so we discard it
// // in this update, so we discard it // return await _loadDifference.call(this)
// return await _loadDifference.call(this)
await this._cachePeersFrom(update) await this._cachePeersFrom(update)
const { users, chats } = createUsersChatsIndex(update) const { users, chats } = createUsersChatsIndex(update)
for (const upd of update.updates) { for (const upd of update.updates) {
if (upd._ === 'updateChannelTooLong') { if (upd._ === 'updateChannelTooLong') {
if (upd.pts) { if (upd.pts) {
this._cpts[upd.channelId] = upd.pts this._cpts[upd.channelId] = upd.pts
}
await _loadChannelDifference.call(
this,
upd.channelId,
noDispatchIndex
)
continue
}
const channelId = extractChannelIdFromUpdate(upd)
const pts = 'pts' in upd ? upd.pts : undefined
const ptsCount =
'ptsCount' in upd ? upd.ptsCount : undefined
if (pts !== undefined && ptsCount !== undefined) {
let nextLocalPts
if (channelId === undefined)
nextLocalPts = this._pts + ptsCount
else if (channelId in this._cpts)
nextLocalPts = this._cpts[channelId] + ptsCount
else {
const saved = await this.storage.getChannelPts(
channelId
)
if (saved) {
this._cpts[channelId] = saved
nextLocalPts = saved + ptsCount
} else {
nextLocalPts = null
} }
await _loadChannelDifference.call(
this,
upd.channelId,
noDispatchIndex
)
continue
} }
if (nextLocalPts) { const channelId = extractChannelIdFromUpdate(upd)
if (nextLocalPts > pts) const pts = 'pts' in upd ? upd.pts : undefined
// "the update was already applied, and must be ignored" const ptsCount =
return 'ptsCount' in upd ? upd.ptsCount : undefined
if (nextLocalPts < pts)
if (channelId) { if (pts !== undefined && ptsCount !== undefined) {
// "there's an update gap that must be filled" let nextLocalPts
await _loadChannelDifference.call( if (channelId === undefined)
this, nextLocalPts = this._pts + ptsCount
channelId, else if (channelId in this._cpts)
noDispatchIndex nextLocalPts = this._cpts[channelId] + ptsCount
) else {
continue const saved = await this.storage.getChannelPts(
channelId
)
if (saved) {
this._cpts[channelId] = saved
nextLocalPts = saved + ptsCount
} else { } else {
return await _loadDifference.call(this) nextLocalPts = null
} }
} }
if (!isDummyUpdate(upd) && !noDispatch) { if (nextLocalPts) {
if (nextLocalPts > pts)
// "the update was already applied, and must be ignored"
return
if (nextLocalPts < pts)
if (channelId) {
// "there's an update gap that must be filled"
await _loadChannelDifference.call(
this,
channelId,
noDispatchIndex
)
continue
} else {
return await _loadDifference.call(this)
}
}
if (!isDummyUpdate(upd) && !noDispatch) {
this.dispatchUpdate(upd, users, chats)
}
if (channelId) {
this._cpts[channelId] = pts
} else {
this._pts = pts
}
} else if (!noDispatch) {
this.dispatchUpdate(upd, users, chats) this.dispatchUpdate(upd, users, chats)
} }
}
if (channelId) { if (!isDummyUpdates(update)) {
this._cpts[channelId] = pts // this._seq = update.seq
} else { this._date = update.date
this._pts = pts }
} break
}
case 'updateShort': {
const upd = update.update
if (upd._ === 'updateDcOptions' && this._config) {
;(this._config as tl.Mutable<tl.TypeConfig>).dcOptions =
upd.dcOptions
} else if (upd._ === 'updateConfig') {
this._config = await this.call({ _: 'help.getConfig' })
} else if (!noDispatch) { } else if (!noDispatch) {
this.dispatchUpdate(upd, users, chats) this.dispatchUpdate(upd, {}, {})
} }
}
if (!isDummyUpdates(update)) {
// this._seq = update.seq
this._date = update.date this._date = update.date
break
} }
} else if (update._ === 'updateShort') { case 'updateShortMessage': {
const upd = update.update if (noDispatch) return
if (upd._ === 'updateDcOptions' && this._config) {
;(this._config as tl.Mutable<tl.TypeConfig>).dcOptions =
upd.dcOptions
} else if (upd._ === 'updateConfig') {
this._config = await this.call({ _: 'help.getConfig' })
} else if (!noDispatch) {
this.dispatchUpdate(upd, {}, {})
}
this._date = update.date const message: tl.RawMessage = {
} else if (update._ === 'updateShortMessage') { _: 'message',
if (noDispatch) return out: update.out,
mentioned: update.mentioned,
const message: tl.RawMessage = { mediaUnread: update.mediaUnread,
_: 'message', silent: update.silent,
out: update.out, id: update.id,
mentioned: update.mentioned, fromId: {
mediaUnread: update.mediaUnread, _: 'peerUser',
silent: update.silent, userId: update.out ? this._userId! : update.userId,
id: update.id, },
fromId: { peerId: {
_: 'peerUser', _: 'peerUser',
userId: update.out ? this._userId! : update.userId,
},
peerId: {
_: 'peerUser',
userId: update.userId,
},
fwdFrom: update.fwdFrom,
viaBotId: update.viaBotId,
replyTo: update.replyTo,
date: update.date,
message: update.message,
entities: update.entities,
ttlPeriod: update.ttlPeriod,
}
// now we need to fetch info about users involved.
// since this update is only used for PM, we can just
// fetch the current user and the other user.
// additionally, we need to handle "forwarded from"
// field, as it may contain a user OR a channel
const fwdFrom = update.fwdFrom?.fromId
? peerToInputPeer(update.fwdFrom.fromId)
: undefined
let rawUsers: tl.TypeUser[]
{
const id: tl.TypeInputUser[] = [
{ _: 'inputUserSelf' },
{
_: 'inputUser',
userId: update.userId, userId: update.userId,
accessHash: bigInt.zero,
}, },
] fwdFrom: update.fwdFrom,
viaBotId: update.viaBotId,
if (fwdFrom) { replyTo: update.replyTo,
const inputUser = normalizeToInputUser(fwdFrom) date: update.date,
if (inputUser) id.push(inputUser) message: update.message,
entities: update.entities,
ttlPeriod: update.ttlPeriod,
} }
rawUsers = await this.call({ // now we need to fetch info about users involved.
_: 'users.getUsers', // since this update is only used for PM, we can just
id, // fetch the current user and the other user.
}) // additionally, we need to handle "forwarded from"
// field, as it may contain a user OR a channel
const fwdFrom = update.fwdFrom?.fromId
? peerToInputPeer(update.fwdFrom.fromId)
: undefined
if (rawUsers.length !== id.length) { let rawUsers: tl.TypeUser[]
// other user failed to load. {
// first try checking for input peer in storage const id: tl.TypeInputUser[] = [
const saved = await this.storage.getPeerById( { _: 'inputUserSelf' },
update.userId {
) _: 'inputUser',
if (saved) { userId: update.userId,
id[1] = normalizeToInputUser(saved)! accessHash: bigInt.zero,
},
]
rawUsers = await this.call({ if (fwdFrom) {
_: 'users.getUsers', const inputUser = normalizeToInputUser(fwdFrom)
id, if (inputUser) id.push(inputUser)
})
} }
}
if (rawUsers.length !== id.length) { rawUsers = await this.call({
// not saved (or invalid hash), not found by id _: 'users.getUsers',
// find that user in dialogs (since the update id,
// is about an incoming message, dialog with that
// user should be one of the first)
const dialogs = await this.call({
_: 'messages.getDialogs',
offsetDate: 0,
offsetId: 0,
offsetPeer: { _: 'inputPeerEmpty' },
limit: 20,
hash: 0,
}) })
if (dialogs._ === 'messages.dialogsNotModified') return
const user = dialogs.users.find( if (rawUsers.length !== id.length) {
(it) => it.id === update.userId // other user failed to load.
) // first try checking for input peer in storage
if (!user) { const saved = await this.storage.getPeerById(
debug( update.userId
"received updateShortMessage, but wasn't able to find User"
) )
return if (saved) {
id[1] = normalizeToInputUser(saved)!
rawUsers = await this.call({
_: 'users.getUsers',
id,
})
}
}
if (rawUsers.length !== id.length) {
// not saved (or invalid hash), not found by id
// find that user in dialogs (since the update
// is about an incoming message, dialog with that
// user should be one of the first)
const dialogs = await this.call({
_: 'messages.getDialogs',
offsetDate: 0,
offsetId: 0,
offsetPeer: { _: 'inputPeerEmpty' },
limit: 20,
hash: 0,
})
if (dialogs._ === 'messages.dialogsNotModified')
return
const user = dialogs.users.find(
(it) => it.id === update.userId
)
if (!user) {
debug(
"received updateShortMessage, but wasn't able to find User"
)
return
}
rawUsers.push(user)
} }
rawUsers.push(user)
} }
let rawChats: tl.TypeChat[] = []
if (fwdFrom) {
const inputChannel = normalizeToInputChannel(fwdFrom)
if (inputChannel)
rawChats = await this.call({
_: 'channels.getChannels',
id: [inputChannel],
}).then((res) => res.chats)
}
this._date = update.date
this._pts = update.pts
const { users, chats } = createUsersChatsIndex({
users: rawUsers,
chats: rawChats,
})
this.dispatchUpdate(message, users, chats)
break
} }
let rawChats: tl.TypeChat[] = [] case 'updateShortChatMessage': {
if (fwdFrom) { if (noDispatch) return
const inputChannel = normalizeToInputChannel(fwdFrom)
if (inputChannel)
rawChats = await this.call({
_: 'channels.getChannels',
id: [inputChannel],
}).then((res) => res.chats)
}
this._date = update.date const message: tl.RawMessage = {
this._pts = update.pts _: 'message',
out: update.out,
const { users, chats } = createUsersChatsIndex({ mentioned: update.mentioned,
users: rawUsers, mediaUnread: update.mediaUnread,
chats: rawChats, silent: update.silent,
}) id: update.id,
this.dispatchUpdate(message, users, chats) fromId: {
} else if (update._ === 'updateShortChatMessage') { _: 'peerUser',
if (noDispatch) return
const message: tl.RawMessage = {
_: 'message',
out: update.out,
mentioned: update.mentioned,
mediaUnread: update.mediaUnread,
silent: update.silent,
id: update.id,
fromId: {
_: 'peerUser',
userId: update.fromId,
},
peerId: {
_: 'peerChat',
chatId: update.chatId,
},
fwdFrom: update.fwdFrom,
viaBotId: update.viaBotId,
replyTo: update.replyTo,
date: update.date,
message: update.message,
entities: update.entities,
ttlPeriod: update.ttlPeriod,
}
// similarly to updateShortMessage, we need to fetch the sender
// user and the chat, and also handle "forwarded from" info.
const fwdFrom = update.fwdFrom?.fromId
? peerToInputPeer(update.fwdFrom.fromId)
: undefined
let rawUsers: tl.TypeUser[]
{
const id: tl.TypeInputUser[] = [
{
_: 'inputUser',
userId: update.fromId, userId: update.fromId,
accessHash: bigInt.zero,
}, },
] peerId: {
_: 'peerChat',
chatId: update.chatId,
},
fwdFrom: update.fwdFrom,
viaBotId: update.viaBotId,
replyTo: update.replyTo,
date: update.date,
message: update.message,
entities: update.entities,
ttlPeriod: update.ttlPeriod,
}
// similarly to updateShortMessage, we need to fetch the sender
// user and the chat, and also handle "forwarded from" info.
const fwdFrom = update.fwdFrom?.fromId
? peerToInputPeer(update.fwdFrom.fromId)
: undefined
let rawUsers: tl.TypeUser[]
{
const id: tl.TypeInputUser[] = [
{
_: 'inputUser',
userId: update.fromId,
accessHash: bigInt.zero,
},
]
if (fwdFrom) {
const inputUser = normalizeToInputUser(fwdFrom)
if (inputUser) id.push(inputUser)
}
rawUsers = await this.call({
_: 'users.getUsers',
id,
})
if (rawUsers.length !== id.length) {
// user failed to load.
// first try checking for input peer in storage
const saved = await this.storage.getPeerById(
update.fromId
)
if (saved) {
id[0] = normalizeToInputUser(saved)!
rawUsers = await this.call({
_: 'users.getUsers',
id,
})
}
}
if (rawUsers.length !== id.length) {
// not saved (or invalid hash), not found by id
// find that user in chat participants list
const res = await this.call({
_: 'messages.getFullChat',
chatId: update.chatId,
})
const user = res.users.find(
(it) => it.id === update.fromId
)
if (!user) {
debug(
"received updateShortChatMessage, but wasn't able to find User"
)
return
}
rawUsers.push(user)
}
}
const rawChats = await this.call({
_: 'messages.getChats',
id: [update.chatId],
}).then((res) => res.chats)
if (fwdFrom) { if (fwdFrom) {
const inputUser = normalizeToInputUser(fwdFrom) const inputChannel = normalizeToInputChannel(fwdFrom)
if (inputUser) id.push(inputUser) if (inputChannel) {
} const res = await this.call({
_: 'channels.getChannels',
rawUsers = await this.call({ id: [inputChannel],
_: 'users.getUsers',
id,
})
if (rawUsers.length !== id.length) {
// user failed to load.
// first try checking for input peer in storage
const saved = await this.storage.getPeerById(
update.fromId
)
if (saved) {
id[0] = normalizeToInputUser(saved)!
rawUsers = await this.call({
_: 'users.getUsers',
id,
}) })
rawChats.push(...res.chats)
} }
} }
if (rawUsers.length !== id.length) {
// not saved (or invalid hash), not found by id
// find that user in chat participants list
const res = await this.call({
_: 'messages.getFullChat',
chatId: update.chatId,
})
const user = res.users.find( this._date = update.date
(it) => it.id === update.fromId this._pts = update.pts
)
if (!user) { const { users, chats } = createUsersChatsIndex({
debug( users: rawUsers,
"received updateShortChatMessage, but wasn't able to find User" chats: rawChats,
) })
return this.dispatchUpdate(message, users, chats)
} break
rawUsers.push(user)
}
} }
const rawChats = await this.call({ case 'updateShortSentMessage': // only store the new pts and date values
_: 'messages.getChats', // we never need to dispatch this
id: [update.chatId], this._date = update.date
}).then((res) => res.chats) this._pts = update.pts
break
if (fwdFrom) {
const inputChannel = normalizeToInputChannel(fwdFrom)
if (inputChannel) {
const res = await this.call({
_: 'channels.getChannels',
id: [inputChannel],
})
rawChats.push(...res.chats)
}
}
this._date = update.date
this._pts = update.pts
const { users, chats } = createUsersChatsIndex({
users: rawUsers,
chats: rawChats,
})
this.dispatchUpdate(message, users, chats)
} else if (update._ === 'updateShortSentMessage') {
// only store the new pts and date values
// we never need to dispatch this
this._date = update.date
this._pts = update.pts
} }
}) })
.catch((err) => this._emitError(err)) .catch((err) => this._emitError(err))

View file

@ -56,33 +56,37 @@ export async function resolvePeer(
const peerType = getBasicPeerType(peerId) const peerType = getBasicPeerType(peerId)
if (peerType === 'user') { switch (peerType) {
await this.call({ case 'user':
_: 'users.getUsers', await this.call({
id: [ _: 'users.getUsers',
{ id: [
_: 'inputUser', {
userId: peerId, _: 'inputUser',
accessHash: bigInt.zero, userId: peerId,
}, accessHash: bigInt.zero,
], },
}) ],
} else if (peerType === 'chat') { })
await this.call({ break
_: 'messages.getChats', case 'chat':
id: [-peerId], await this.call({
}) _: 'messages.getChats',
} else if (peerType === 'channel') { id: [-peerId],
await this.call({ })
_: 'channels.getChannels', break
id: [ case 'channel':
{ await this.call({
_: 'inputChannel', _: 'channels.getChannels',
channelId: MAX_CHANNEL_ID - peerId, id: [
accessHash: bigInt.zero, {
}, _: 'inputChannel',
], channelId: MAX_CHANNEL_ID - peerId,
}) accessHash: bigInt.zero,
},
],
})
break
} }
const fromStorage = await this.storage.getPeerById(peerId) const fromStorage = await this.storage.getPeerById(peerId)

View file

@ -75,29 +75,32 @@ export class Thumbnail extends FileLocation {
| (() => tl.TypeInputFileLocation | Buffer) | (() => tl.TypeInputFileLocation | Buffer)
let size, width, height: number let size, width, height: number
if (sz._ === 'photoStrippedSize') { switch (sz._) {
location = strippedPhotoToJpg(sz.bytes) case 'photoStrippedSize':
width = height = NaN location = strippedPhotoToJpg(sz.bytes)
size = location.length width = height = NaN
} else if (sz._ === 'photoPathSize') { size = location.length
// lazily break
location = () => svgPathToFile(this._path!) case 'photoPathSize': // lazily
width = height = NaN location = () => svgPathToFile(this._path!)
size = Infinity // this doesn't really matter width = height = NaN
} else { size = Infinity // this doesn't really matter
location = { break
_: default:
media._ === 'photo' location = {
? 'inputPhotoFileLocation' _:
: 'inputDocumentFileLocation', media._ === 'photo'
id: media.id, ? 'inputPhotoFileLocation'
fileReference: media.fileReference, : 'inputDocumentFileLocation',
accessHash: media.accessHash, id: media.id,
thumbSize: sz.type, fileReference: media.fileReference,
} accessHash: media.accessHash,
width = sz.w thumbSize: sz.type,
height = sz.h }
size = sz._ === 'photoSize' ? sz.size : Math.max(...sz.sizes) width = sz.w
height = sz.h
size = sz._ === 'photoSize' ? sz.size : Math.max(...sz.sizes)
break
} }
super(client, location, size, media.dcId) super(client, location, size, media.dcId)

View file

@ -272,22 +272,26 @@ export class Message {
if (fwd.fromName) { if (fwd.fromName) {
sender = fwd.fromName sender = fwd.fromName
} else if (fwd.fromId) { } else if (fwd.fromId) {
if (fwd.fromId._ === 'peerChannel') { switch (fwd.fromId._) {
sender = new Chat( case 'peerChannel':
this.client, sender = new Chat(
this._chats[fwd.fromId.channelId] this.client,
) this._chats[fwd.fromId.channelId]
} else if (fwd.fromId._ === 'peerUser') { )
sender = new User( break
this.client, case 'peerUser':
this._users[fwd.fromId.userId] sender = new User(
) this.client,
} else this._users[fwd.fromId.userId]
throw new MtCuteTypeAssertionError( )
'Message#forward (@ raw.fwdFrom.fromId)', break
'peerUser | peerChannel', default:
fwd.fromId._ throw new MtCuteTypeAssertionError(
) 'Message#forward (@ raw.fwdFrom.fromId)',
'peerUser | peerChannel',
fwd.fromId._
)
}
} else { } else {
this._forward = null this._forward = null
return this._forward return this._forward
@ -431,31 +435,39 @@ export class Message {
} else { } else {
const rm = this.raw.replyMarkup const rm = this.raw.replyMarkup
let markup: ReplyMarkup | null let markup: ReplyMarkup | null
if (rm._ === 'replyKeyboardHide') { switch (rm._) {
markup = { case 'replyKeyboardHide':
type: 'reply_hide', markup = {
selective: rm.selective, type: 'reply_hide',
} selective: rm.selective,
} else if (rm._ === 'replyKeyboardForceReply') { }
markup = { break
type: 'force_reply', case 'replyKeyboardForceReply':
singleUse: rm.singleUse, markup = {
selective: rm.selective, type: 'force_reply',
} singleUse: rm.singleUse,
} else if (rm._ === 'replyKeyboardMarkup') { selective: rm.selective,
markup = { }
type: 'reply', break
resize: rm.resize, case 'replyKeyboardMarkup':
singleUse: rm.singleUse, markup = {
selective: rm.selective, type: 'reply',
buttons: BotKeyboard._rowsTo2d(rm.rows), resize: rm.resize,
} singleUse: rm.singleUse,
} else if (rm._ === 'replyInlineMarkup') { selective: rm.selective,
markup = { buttons: BotKeyboard._rowsTo2d(rm.rows),
type: 'inline', }
buttons: BotKeyboard._rowsTo2d(rm.rows), break
} case 'replyInlineMarkup':
} else markup = null markup = {
type: 'inline',
buttons: BotKeyboard._rowsTo2d(rm.rows),
}
break
default:
markup = null
break
}
this._markup = markup this._markup = markup
} }

View file

@ -126,21 +126,29 @@ export class User {
ret = 'long_time_ago' ret = 'long_time_ago'
} else if (bot) { } else if (bot) {
ret = 'bot' ret = 'bot'
} else if (us._ === 'userStatusOnline') { } else
ret = 'online' switch (us._) {
date = new Date(us.expires * 1000) case 'userStatusOnline':
} else if (us._ === 'userStatusOffline') { ret = 'online'
ret = 'offline' date = new Date(us.expires * 1000)
date = new Date(us.wasOnline * 1000) break
} else if (us._ === 'userStatusRecently') { case 'userStatusOffline':
ret = 'recently' ret = 'offline'
} else if (us._ === 'userStatusLastWeek') { date = new Date(us.wasOnline * 1000)
ret = 'within_week' break
} else if (us._ === 'userStatusLastMonth') { case 'userStatusRecently':
ret = 'within_month' ret = 'recently'
} else { break
ret = 'long_time_ago' case 'userStatusLastWeek':
} ret = 'within_week'
break
case 'userStatusLastMonth':
ret = 'within_month'
break
default:
ret = 'long_time_ago'
break
}
return { return {
status: ret, status: ret,

View file

@ -652,39 +652,45 @@ export class BaseTelegramClient {
continue continue
} }
if (peer._ === 'user') { switch (peer._) {
parsedPeers.push({ case 'user':
id: peer.id, parsedPeers.push({
accessHash: peer.accessHash!, id: peer.id,
username: peer.username?.toLowerCase() ?? null, accessHash: peer.accessHash!,
phone: peer.phone ?? null, username: peer.username?.toLowerCase() ?? null,
type: peer.bot ? 'bot' : 'user', phone: peer.phone ?? null,
updated: 0, type: peer.bot ? 'bot' : 'user',
fromMessage: peer.fromMessage, updated: 0,
}) fromMessage: peer.fromMessage,
} else if (peer._ === 'chat' || peer._ === 'chatForbidden') { })
parsedPeers.push({ break
id: -peer.id, case 'chat':
accessHash: bigInt.zero, case 'chatForbidden':
username: null, parsedPeers.push({
phone: null, id: -peer.id,
type: 'group', accessHash: bigInt.zero,
updated: 0, username: null,
fromMessage: peer.fromMessage, phone: null,
}) type: 'group',
} else if (peer._ === 'channel' || peer._ === 'channelForbidden') { updated: 0,
parsedPeers.push({ fromMessage: peer.fromMessage,
id: MAX_CHANNEL_ID - peer.id, })
accessHash: peer.accessHash!, break
username: case 'channel':
peer._ === 'channel' case 'channelForbidden':
? peer.username?.toLowerCase() ?? null parsedPeers.push({
: null, id: MAX_CHANNEL_ID - peer.id,
phone: null, accessHash: peer.accessHash!,
type: peer.broadcast ? 'channel' : 'supergroup', username:
updated: 0, peer._ === 'channel'
fromMessage: peer.fromMessage, ? peer.username?.toLowerCase() ?? null
}) : null,
phone: null,
type: peer.broadcast ? 'channel' : 'supergroup',
updated: 0,
fromMessage: peer.fromMessage,
})
break
} }
} }

View file

@ -74,9 +74,14 @@ export function getBasicPeerType(peer: tl.TypePeer | number): BasicPeerType {
export function markedPeerIdToBare(peerId: number): number { export function markedPeerIdToBare(peerId: number): number {
const type = getBasicPeerType(peerId) const type = getBasicPeerType(peerId)
if (type === 'user') return peerId switch (type) {
else if (type === 'chat') return -peerId case 'user':
else if (type === 'channel') return MAX_CHANNEL_ID - peerId return peerId
case 'chat':
return -peerId
case 'channel':
return MAX_CHANNEL_ID - peerId
}
throw new Error('Invalid marked peer id') throw new Error('Invalid marked peer id')
} }

View file

@ -60,88 +60,99 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
name = name.toLowerCase() name = name.toLowerCase()
let entity: tl.TypeMessageEntity let entity: tl.TypeMessageEntity
if (name === 'b' || name === 'strong') { switch (name) {
entity = { case 'b':
_: 'messageEntityBold', case 'strong':
offset: plainText.length, entity = {
length: 0, _: 'messageEntityBold',
} offset: plainText.length,
} else if (name === 'i' || name === 'em') { length: 0,
entity = { }
_: 'messageEntityItalic', break
offset: plainText.length, case 'i':
length: 0, case 'em':
} entity = {
} else if (name === 'u') { _: 'messageEntityItalic',
entity = { offset: plainText.length,
_: 'messageEntityUnderline', length: 0,
offset: plainText.length, }
length: 0, break
} case 'u':
} else if ( entity = {
name === 's' || _: 'messageEntityUnderline',
name === 'del' || offset: plainText.length,
name === 'strike' length: 0,
) { }
entity = { break
_: 'messageEntityStrike', case 's':
offset: plainText.length, case 'del':
length: 0, case 'strike':
} entity = {
} else if (name === 'blockquote') { _: 'messageEntityStrike',
entity = { offset: plainText.length,
_: 'messageEntityBlockquote', length: 0,
offset: plainText.length, }
length: 0, break
} case 'blockquote':
} else if (name === 'code') { entity = {
entity = { _: 'messageEntityBlockquote',
_: 'messageEntityCode', offset: plainText.length,
offset: plainText.length, length: 0,
length: 0, }
} break
} else if (name === 'pre') { case 'code':
entity = { entity = {
_: 'messageEntityPre', _: 'messageEntityCode',
offset: plainText.length, offset: plainText.length,
length: 0, length: 0,
language: attribs.language ?? '', }
} break
} else if (name === 'a') { case 'pre':
const url = attribs.href entity = {
if (!url) return _: 'messageEntityPre',
offset: plainText.length,
length: 0,
language: attribs.language ?? '',
}
break
case 'a':
const url = attribs.href
if (!url) return
const mention = MENTION_REGEX.exec(url) const mention = MENTION_REGEX.exec(url)
if (mention) { if (mention) {
const accessHash = mention[2] const accessHash = mention[2]
if (accessHash) { if (accessHash) {
entity = { entity = {
_: 'inputMessageEntityMentionName', _: 'inputMessageEntityMentionName',
offset: plainText.length, offset: plainText.length,
length: 0, length: 0,
userId: { userId: {
_: 'inputUser', _: 'inputUser',
userId: parseInt(mention[1]),
accessHash: bigInt(accessHash, 16),
},
}
} else {
entity = {
_: 'messageEntityMentionName',
offset: plainText.length,
length: 0,
userId: parseInt(mention[1]), userId: parseInt(mention[1]),
accessHash: bigInt(accessHash, 16), }
},
} }
} else { } else {
entity = { entity = {
_: 'messageEntityMentionName', _: 'messageEntityTextUrl',
offset: plainText.length, offset: plainText.length,
length: 0, length: 0,
userId: parseInt(mention[1]), url,
} }
} }
} else { break
entity = { default:
_: 'messageEntityTextUrl', return
offset: plainText.length, }
length: 0,
url,
}
}
} else return
if (!(name in stacks)) { if (!(name in stacks)) {
stacks[name] = [] stacks[name] = []
@ -232,48 +243,56 @@ export class HtmlMessageEntityParser implements IMessageEntityParser {
) )
const type = entity.type const type = entity.type
if ( switch (type) {
type === 'bold' || case 'bold':
type === 'italic' || case 'italic':
type === 'underline' || case 'underline':
type === 'strikethrough' case 'strikethrough':
) { html.push(`<${type[0]}>${entityText}</${type[0]}>`)
html.push(`<${type[0]}>${entityText}</${type[0]}>`) break
} else if ( case 'code':
type === 'code' || case 'pre':
type === 'pre' || case 'blockquote':
type === 'blockquote' html.push(
) { `<${type}${
html.push( type === 'pre' && entity.language
`<${type}${ ? ` language="${entity.language}"`
type === 'pre' && entity.language : ''
? ` language="${entity.language}"` }>${
: '' this._syntaxHighlighter
}>${ ? this._syntaxHighlighter(
this._syntaxHighlighter entityText,
? this._syntaxHighlighter( entity.language!
entityText, )
entity.language! : entityText
) }</${type}>`
: entityText )
}</${type}>` break
) case 'email':
} else if (type === 'email') { html.push(
html.push(`<a href="mailto:${entityText}">${entityText}</a>`) `<a href="mailto:${entityText}">${entityText}</a>`
} else if (type === 'url') { )
html.push(`<a href="${entityText}">${entityText}</a>`) break
} else if (type === 'text_link') { case 'url':
html.push( html.push(`<a href="${entityText}">${entityText}</a>`)
`<a href="${HtmlMessageEntityParser.escape( break
entity.url!, case 'text_link':
true html.push(
)}">${entityText}</a>` `<a href="${HtmlMessageEntityParser.escape(
) entity.url!,
} else if (type == 'text_mention') { true
html.push( )}">${entityText}</a>`
`<a href="tg://user?id=${entity.userId!}">${entityText}</a>` )
) break
} else skip = true case 'text_mention':
html.push(
`<a href="tg://user?id=${entity.userId!}">${entityText}</a>`
)
break
default:
skip = true
break
}
lastOffset = relativeOffset + (skip ? 0 : length) lastOffset = relativeOffset + (skip ? 0 : length)
} }

View file

@ -209,10 +209,20 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
| 'Underline' | 'Underline'
| 'Strike' | 'Strike'
| null = null | null = null
if (c === '_') type = 'Italic' switch (c) {
else if (c === '*') type = 'Bold' case '_':
else if (c === '-') type = 'Underline' type = 'Italic'
else if (c === '~') type = 'Strike' break
case '*':
type = 'Bold'
break
case '-':
type = 'Underline'
break
case '~':
type = 'Strike'
break
}
if (type) { if (type) {
if (!(type in stacks)) stacks[type] = [] if (!(type in stacks)) stacks[type] = []
@ -288,32 +298,43 @@ export class MarkdownMessageEntityParser implements IMessageEntityParser {
} }
let startTag, endTag: string let startTag, endTag: string
if (type === 'bold') { switch (type) {
startTag = endTag = TAG_BOLD case 'bold':
} else if (type === 'italic') { startTag = endTag = TAG_BOLD
startTag = endTag = TAG_ITALIC break
} else if (type === 'underline') { case 'italic':
startTag = endTag = TAG_UNDERLINE startTag = endTag = TAG_ITALIC
} else if (type === 'strikethrough') { break
startTag = endTag = TAG_STRIKE case 'underline':
} else if (type === 'code') { startTag = endTag = TAG_UNDERLINE
startTag = endTag = TAG_CODE break
} else if (type === 'pre') { case 'strikethrough':
startTag = TAG_PRE startTag = endTag = TAG_STRIKE
break
case 'code':
startTag = endTag = TAG_CODE
break
case 'pre':
startTag = TAG_PRE
if (entity.language) { if (entity.language) {
startTag += entity.language startTag += entity.language
} }
startTag += '\n' startTag += '\n'
endTag = '\n' + TAG_PRE endTag = '\n' + TAG_PRE
} else if (type === 'text_link') { break
startTag = '[' case 'text_link':
endTag = `](${entity.url!})` startTag = '['
} else if (type === 'text_mention') { endTag = `](${entity.url!})`
startTag = '[' break
endTag = `](tg://user?id=${entity.userId!})` case 'text_mention':
} else continue startTag = '['
endTag = `](tg://user?id=${entity.userId!})`
break
default:
continue
}
insert.push([start, startTag]) insert.push([start, startTag])
insert.push([end, endTag]) insert.push([end, endTag])