Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
6.3 KiB
239 lines
6.3 KiB
/* |
|
* https://github.com/morethanwords/tweb |
|
* Copyright (C) 2019-2021 Eduard Kuzmenko |
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE |
|
*/ |
|
|
|
import { MOUNT_CLASS_TO } from "../../config/debug"; |
|
import { copy } from "../../helpers/object"; |
|
import { InputMedia, MessageEntity } from "../../layer"; |
|
import { logger, LogTypes } from "../logger"; |
|
import apiManager from "../mtproto/mtprotoworker"; |
|
import { RichTextProcessor } from "../richtextprocessor"; |
|
import rootScope from "../rootScope"; |
|
import apiUpdatesManager from "./apiUpdatesManager"; |
|
import appMessagesManager from './appMessagesManager'; |
|
import appPeersManager from './appPeersManager'; |
|
import appUsersManager from "./appUsersManager"; |
|
|
|
export type PollAnswer = { |
|
_: 'pollAnswer', |
|
text: string, |
|
option: Uint8Array |
|
}; |
|
|
|
export type PollAnswerVoters = { |
|
_: 'pollAnswerVoters', |
|
flags: number, |
|
option: Uint8Array, |
|
voters: number, |
|
|
|
pFlags: Partial<{ |
|
chosen: true, |
|
correct: true |
|
}> |
|
}; |
|
|
|
export type PollResult = { |
|
_: 'pollAnswerVoters', |
|
flags: number, |
|
option: Uint8Array, |
|
voters: number, |
|
|
|
pFlags?: Partial<{chosen: true, correct: true}> |
|
}; |
|
|
|
export type PollResults = { |
|
_: 'pollResults', |
|
flags: number, |
|
results?: Array<PollResult>, |
|
total_voters?: number, |
|
recent_voters?: number[], |
|
solution?: string, |
|
solution_entities?: any[], |
|
|
|
pFlags: Partial<{ |
|
min: true |
|
}>, |
|
}; |
|
|
|
export type Poll = { |
|
_: 'poll', |
|
question: string, |
|
id: string, |
|
answers: Array<PollAnswer>, |
|
close_period?: number, |
|
close_date?: number |
|
|
|
pFlags?: Partial<{ |
|
closed: true, |
|
public_voters: true, |
|
multiple_choice: true, |
|
quiz: true |
|
}>, |
|
rQuestion?: string, |
|
rReply?: string, |
|
chosenIndexes?: number[] |
|
}; |
|
|
|
export class AppPollsManager { |
|
public polls: {[id: string]: Poll} = {}; |
|
public results: {[id: string]: PollResults} = {}; |
|
|
|
private log = logger('POLLS', LogTypes.Error); |
|
|
|
constructor() { |
|
rootScope.addMultipleEventsListeners({ |
|
updateMessagePoll: (update) => { |
|
this.log('updateMessagePoll:', update); |
|
|
|
let poll: Poll = update.poll || this.polls[update.poll_id]; |
|
if(!poll) { |
|
return; |
|
} |
|
|
|
poll = this.savePoll(poll, update.results as any); |
|
rootScope.dispatchEvent('poll_update', {poll, results: update.results as any}); |
|
} |
|
}); |
|
} |
|
|
|
public savePoll(poll: Poll, results: PollResults) { |
|
const id = poll.id; |
|
if(this.polls[id]) { |
|
poll = Object.assign(this.polls[id], poll); |
|
this.saveResults(poll, results); |
|
return poll; |
|
} |
|
|
|
this.polls[id] = poll; |
|
|
|
poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question); |
|
poll.rReply = RichTextProcessor.wrapEmojiText('📊') + ' ' + (poll.rQuestion || 'poll'); |
|
poll.chosenIndexes = []; |
|
this.saveResults(poll, results); |
|
return poll; |
|
} |
|
|
|
public saveResults(poll: Poll, results: PollResults) { |
|
if(this.results[poll.id]) { |
|
results = Object.assign(this.results[poll.id], results); |
|
} else { |
|
this.results[poll.id] = results; |
|
} |
|
|
|
if(!results.pFlags.min) { // ! https://core.telegram.org/constructor/pollResults - min |
|
poll.chosenIndexes.length = 0; |
|
if(results?.results?.length) { |
|
results.results.forEach((answer, idx) => { |
|
if(answer.pFlags?.chosen) { |
|
poll.chosenIndexes.push(idx); |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
public getPoll(pollId: string): {poll: Poll, results: PollResults} { |
|
return { |
|
poll: this.polls[pollId], |
|
results: this.results[pollId] |
|
}; |
|
} |
|
|
|
public getInputMediaPoll(poll: Poll, correctAnswers?: Uint8Array[], solution?: string, solutionEntities?: MessageEntity[]): InputMedia.inputMediaPoll { |
|
if(solution) { |
|
if(!solutionEntities) { |
|
solutionEntities = []; |
|
} |
|
|
|
solution = RichTextProcessor.parseMarkdown(solution, solutionEntities); |
|
} else { |
|
solution = undefined; // can be string here |
|
} |
|
|
|
return { |
|
_: 'inputMediaPoll', |
|
poll, |
|
correct_answers: correctAnswers, |
|
solution, |
|
solution_entities: solution ? solutionEntities : undefined |
|
}; |
|
} |
|
|
|
public sendVote(message: any, optionIds: number[]): Promise<void> { |
|
const poll: Poll = message.media.poll; |
|
|
|
const options: Uint8Array[] = optionIds.map(index => { |
|
return poll.answers[index].option; |
|
}); |
|
|
|
const messageId = message.mid; |
|
const peerId = message.peerId; |
|
const inputPeer = appPeersManager.getInputPeerById(peerId); |
|
|
|
if(message.pFlags.is_outgoing) { |
|
return appMessagesManager.invokeAfterMessageIsSent(messageId, 'sendVote', (message) => { |
|
this.log('invoke sendVote callback'); |
|
return this.sendVote(message, optionIds); |
|
}); |
|
} |
|
|
|
return apiManager.invokeApi('messages.sendVote', { |
|
peer: inputPeer, |
|
msg_id: appMessagesManager.getServerMessageId(message.mid), |
|
options |
|
}).then(updates => { |
|
this.log('sendVote updates:', updates); |
|
apiUpdatesManager.processUpdateMessage(updates); |
|
}); |
|
} |
|
|
|
public getResults(message: any) { |
|
const inputPeer = appPeersManager.getInputPeerById(message.peerId); |
|
|
|
return apiManager.invokeApi('messages.getPollResults', { |
|
peer: inputPeer, |
|
msg_id: appMessagesManager.getServerMessageId(message.mid) |
|
}).then(updates => { |
|
apiUpdatesManager.processUpdateMessage(updates); |
|
this.log('getResults updates:', updates); |
|
}); |
|
} |
|
|
|
public getVotes(message: any, option?: Uint8Array, offset?: string, limit = 20) { |
|
return apiManager.invokeApi('messages.getPollVotes', { |
|
peer: appPeersManager.getInputPeerById(message.peerId), |
|
id: appMessagesManager.getServerMessageId(message.mid), |
|
option, |
|
offset, |
|
limit |
|
}).then((votesList) => { |
|
this.log('getPollVotes messages:', votesList); |
|
|
|
appUsersManager.saveApiUsers(votesList.users); |
|
|
|
return votesList; |
|
}); |
|
} |
|
|
|
public stopPoll(message: any) { |
|
const poll: Poll = message.media.poll; |
|
|
|
if(poll.pFlags.closed) return Promise.resolve(); |
|
|
|
const newPoll = copy(poll); |
|
newPoll.pFlags.closed = true; |
|
return appMessagesManager.editMessage(message, undefined, { |
|
newMedia: this.getInputMediaPoll(newPoll) |
|
}).then(() => { |
|
//console.log('stopped poll'); |
|
}, err => { |
|
this.log.error('stopPoll error:', err); |
|
}); |
|
} |
|
} |
|
|
|
const appPollsManager = new AppPollsManager(); |
|
MOUNT_CLASS_TO.appPollsManager = appPollsManager; |
|
export default appPollsManager;
|
|
|