Merge
This commit is contained in:
commit
d2010cc259
273
README.md
273
README.md
@ -175,279 +175,6 @@ Tip: if you receive an error (or nothing), [send us](https://t.me/pwrtelegramgro
|
|||||||
* [Upload or download files up to 1.5 GB](https://docs.madelineproto.xyz/docs/FILES.html)
|
* [Upload or download files up to 1.5 GB](https://docs.madelineproto.xyz/docs/FILES.html)
|
||||||
* [Make a phone call and play a song](https://docs.madelineproto.xyz/docs/CALLS.html)
|
* [Make a phone call and play a song](https://docs.madelineproto.xyz/docs/CALLS.html)
|
||||||
* [Create a secret chat bot](https://docs.madelineproto.xyz/docs/SECRET_CHATS.html)
|
* [Create a secret chat bot](https://docs.madelineproto.xyz/docs/SECRET_CHATS.html)
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_acceptUrlAuth.html" name="messages_acceptUrlAuth">Accept URL authorization: messages.acceptUrlAuth</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_acceptContact.html" name="contacts_acceptContact">Accept contact: contacts.acceptContact</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_acceptAuthorization.html" name="account_acceptAuthorization">Accept telegram passport authorization: account.acceptAuthorization</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_acceptTermsOfService.html" name="help_acceptTermsOfService">Accept telegram's TOS: help.acceptTermsOfService</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_installStickerSet.html" name="messages_installStickerSet">Add a sticker set: messages.installStickerSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_faveSticker.html" name="messages_faveSticker">Add a sticker to favorites: messages.faveSticker</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_saveRecentSticker.html" name="messages_saveRecentSticker">Add a sticker to recent stickers: messages.saveRecentSticker</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_addChatUser.html" name="messages_addChatUser">Add a user to a normal chat (use channels->inviteToChannel for supergroups): messages.addChatUser</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_addContact.html" name="contacts_addContact">Add contact: contacts.addContact</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_importContacts.html" name="contacts_importContacts">Add phone number as contact: contacts.importContacts</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/stickers_addStickerToSet.html" name="stickers_addStickerToSet">Add sticker to stickerset: stickers.addStickerToSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_inviteToChannel.html" name="channels_inviteToChannel">Add users to channel/supergroup: channels.inviteToChannel</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_block.html" name="contacts_block">Block a user: contacts.block</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getAuthorizationForm.html" name="account_getAuthorizationForm">Bots only: get telegram passport authorization form: account.getAuthorizationForm</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_sendPaymentForm.html" name="payments_sendPaymentForm">Bots only: send payment form: payments.sendPaymentForm</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setBotPrecheckoutResults.html" name="messages_setBotPrecheckoutResults">Bots only: set precheckout results: messages.setBotPrecheckoutResults</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setBotShippingResults.html" name="messages_setBotShippingResults">Bots only: set shipping results: messages.setBotShippingResults</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setBotCallbackAnswer.html" name="messages_setBotCallbackAnswer">Bots only: set the callback answer (after a button was clicked): messages.setBotCallbackAnswer</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setInlineBotResults.html" name="messages_setInlineBotResults">Bots only: set the results of an inline query: messages.setInlineBotResults</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getInlineBotResults.html" name="messages_getInlineBotResults">Call inline bot: messages.getInlineBotResults</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_cancelPasswordEmail.html" name="account_cancelPasswordEmail">Cancel password recovery email: account.cancelPasswordEmail</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_updateNotifySettings.html" name="account_updateNotifySettings">Change notification settings: account.updateNotifySettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/stickers_changeStickerPosition.html" name="stickers_changeStickerPosition">Change sticker position in photo: stickers.changeStickerPosition</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_changePhone.html" name="account_changePhone">Change the phone number associated to this account: account.changePhone</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_sendChangePhoneCode.html" name="account_sendChangePhoneCode">Change the phone number: account.sendChangePhoneCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/photos_updateProfilePhoto.html" name="photos_updateProfilePhoto">Change the profile photo: photos.updateProfilePhoto</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setTyping.html" name="messages_setTyping">Change typing status: messages.setTyping</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getMessageEditData.html" name="messages_getMessageEditData">Check if about to edit a message or a media caption: messages.getMessageEditData</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_checkChatInvite.html" name="messages_checkChatInvite">Check if an invitation link is valid: messages.checkChatInvite</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_checkUsername.html" name="account_checkUsername">Check if this username is available: account.checkUsername</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_checkUsername.html" name="channels_checkUsername">Check if this username is free and can be assigned to a channel/supergroup: channels.checkUsername</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_clearAllDrafts.html" name="messages_clearAllDrafts">Clear all drafts: messages.clearAllDrafts</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_clearRecentStickers.html" name="messages_clearRecentStickers">Clear all recent stickers: messages.clearRecentStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_clearSavedInfo.html" name="payments_clearSavedInfo">Clear saved payments info: payments.clearSavedInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_confirmPasswordEmail.html" name="account_confirmPasswordEmail">Confirm password recovery using email: account.confirmPasswordEmail</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_confirmPhone.html" name="account_confirmPhone">Confirm this phone number is associated to this account, obtain phone_code_hash from sendConfirmPhoneCode: account.confirmPhone</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getContactSignUpNotification.html" name="account_getContactSignUpNotification">Contact signup notification setting value: account.getContactSignUpNotification</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_migrateChat.html" name="messages_migrateChat">Convert chat to supergroup: messages.migrateChat</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_createChat.html" name="messages_createChat">Create a chat (not supergroup): messages.createChat</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_createChannel.html" name="channels_createChannel">Create channel/supergroup: channels.createChannel</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/stickers_createStickerSet.html" name="stickers_createStickerSet">Create stickerset: stickers.createStickerSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resetAuthorization.html" name="account_resetAuthorization">Delete a certain session: account.resetAuthorization</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resetWebAuthorization.html" name="account_resetWebAuthorization">Delete a certain telegram web login authorization: account.resetWebAuthorization</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_deleteChannel.html" name="channels_deleteChannel">Delete a channel/supergroup: channels.deleteChannel</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_deleteChatUser.html" name="messages_deleteChatUser">Delete a user from a chat (not supergroup): messages.deleteChatUser</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_resetAuthorizations.html" name="auth_resetAuthorizations">Delete all logged-in sessions.: auth.resetAuthorizations</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_deleteUserHistory.html" name="channels_deleteUserHistory">Delete all messages of a user in a channel/supergroup: channels.deleteUserHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_dropTempAuthKeys.html" name="auth_dropTempAuthKeys">Delete all temporary authorization keys except the ones provided: auth.dropTempAuthKeys</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_deleteMessages.html" name="channels_deleteMessages">Delete channel/supergroup messages: channels.deleteMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_deleteHistory.html" name="messages_deleteHistory">Delete chat history: messages.deleteHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_deleteByPhones.html" name="contacts_deleteByPhones">Delete contacts by phones: contacts.deleteByPhones</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/folders_deleteFolder.html" name="folders_deleteFolder">Delete folder: folders.deleteFolder</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_deleteMessages.html" name="messages_deleteMessages">Delete messages: messages.deleteMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_deleteContacts.html" name="contacts_deleteContacts">Delete multiple contacts: contacts.deleteContacts</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/photos_deletePhotos.html" name="photos_deletePhotos">Delete profile photos: photos.deletePhotos</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_deleteSecureValue.html" name="account_deleteSecureValue">Delete secure telegram passport value: account.deleteSecureValue</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_deleteHistory.html" name="channels_deleteHistory">Delete the history of a supergroup/channel: channels.deleteHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_deleteAccount.html" name="account_deleteAccount">Delete this account: account.deleteAccount</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_updateDeviceLocked.html" name="account_updateDeviceLocked">Disable all notifications for a certain period: account.updateDeviceLocked</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/upload_getWebFile.html" name="upload_getWebFile">Download a file through telegram: upload.getWebFile</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editMessage.html" name="messages_editMessage">Edit a message: messages.editMessage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editInlineBotMessage.html" name="messages_editInlineBotMessage">Edit a sent inline message: messages.editInlineBotMessage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editAdmin.html" name="channels_editAdmin">Edit admin permissions of a user in a channel/supergroup: channels.editAdmin</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editChatAdmin.html" name="messages_editChatAdmin">Edit admin permissions: messages.editChatAdmin</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editChatAbout.html" name="messages_editChatAbout">Edit chat info: messages.editChatAbout</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editCreator.html" name="channels_editCreator">Edit creator of channel: channels.editCreator</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editChatDefaultBannedRights.html" name="messages_editChatDefaultBannedRights">Edit default rights of chat: messages.editChatDefaultBannedRights</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/folders_editPeerFolders.html" name="folders_editPeerFolders">Edit folder: folders.editPeerFolders</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editLocation.html" name="channels_editLocation">Edit location (geochats): channels.editLocation</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editChatPhoto.html" name="messages_editChatPhoto">Edit the photo of a normal chat (not supergroup): messages.editChatPhoto</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editPhoto.html" name="channels_editPhoto">Edit the photo of a supergroup/channel: channels.editPhoto</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_editChatTitle.html" name="messages_editChatTitle">Edit the title of a normal chat (not supergroup): messages.editChatTitle</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editTitle.html" name="channels_editTitle">Edit the title of a supergroup/channel: channels.editTitle</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_editUserInfo.html" name="help_editUserInfo">Edit user info: help.editUserInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_togglePreHistoryHidden.html" name="channels_togglePreHistoryHidden">Enable or disable hidden history for new channel/supergroup users: channels.togglePreHistoryHidden</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_exportChatInvite.html" name="messages_exportChatInvite">Export chat invite : messages.exportChatInvite</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_searchStickerSets.html" name="messages_searchStickerSets">Find a sticker set: messages.searchStickerSets</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_finishTakeoutSession.html" name="account_finishTakeoutSession">Finish account exporting session: account.finishTakeoutSession</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_forwardMessages.html" name="messages_forwardMessages">Forward messages: messages.forwardMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getCdnConfig.html" name="help_getCdnConfig">Get CDN configuration: help.getCdnConfig</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getStickerSet.html" name="messages_getStickerSet">Get a stickerset: messages.getStickerSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getAccountTTL.html" name="account_getAccountTTL">Get account TTL: account.getAccountTTL</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getAdminLog.html" name="channels_getAdminLog">Get admin log of a channel/supergroup: channels.getAdminLog</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getArchivedStickers.html" name="messages_getArchivedStickers">Get all archived stickers: messages.getArchivedStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getLeftChannels.html" name="channels_getLeftChannels">Get all channels you left: channels.getLeftChannels</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getAllChats.html" name="messages_getAllChats">Get all chats (not supergroups or channels): messages.getAllChats</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getContacts.html" name="contacts_getContacts">Get all contacts: contacts.getContacts</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getAuthorizations.html" name="account_getAuthorizations">Get all logged-in authorizations: account.getAuthorizations</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getAllDrafts.html" name="messages_getAllDrafts">Get all message drafts: messages.getAllDrafts</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getAllSecureValues.html" name="account_getAllSecureValues">Get all secure telegram passport values: account.getAllSecureValues</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getAllStickers.html" name="messages_getAllStickers">Get all stickerpacks: messages.getAllStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getAdminedPublicChannels.html" name="channels_getAdminedPublicChannels">Get all supergroups/channels where you're admin: channels.getAdminedPublicChannels</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getMessagesViews.html" name="messages_getMessagesViews">Get and increase message views: messages.getMessagesViews</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getAppConfig.html" name="help_getAppConfig">Get app config: help.getAppConfig</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getAutoDownloadSettings.html" name="account_getAutoDownloadSettings">Get autodownload settings: account.getAutoDownloadSettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/langpack_getLanguages.html" name="langpack_getLanguages">Get available languages: langpack.getLanguages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getBlocked.html" name="contacts_getBlocked">Get blocked users: contacts.getBlocked</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/phone_getCallConfig.html" name="phone_getCallConfig">Get call configuration: phone.getCallConfig</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getMessages.html" name="channels_getMessages">Get channel/supergroup messages: channels.getMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getParticipants.html" name="channels_getParticipants">Get channel/supergroup participants (you should use `$MadelineProto->get_pwr_chat($id)` instead): channels.getParticipants</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getCommonChats.html" name="messages_getCommonChats">Get chats in common with a user: messages.getCommonChats</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getContactIDs.html" name="contacts_getContactIDs">Get contacts by IDs: contacts.getContactIDs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getDeepLinkInfo.html" name="help_getDeepLinkInfo">Get deep link info: help.getDeepLinkInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getPeerDialogs.html" name="messages_getPeerDialogs">Get dialog info of peers: messages.getPeerDialogs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getDialogUnreadMarks.html" name="messages_getDialogUnreadMarks">Get dialogs marked as unread manually: messages.getDialogUnreadMarks</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getDocumentByHash.html" name="messages_getDocumentByHash">Get document by SHA256 hash: messages.getDocumentByHash</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getEmojiURL.html" name="messages_getEmojiURL">Get emoji URL: messages.getEmojiURL</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getEmojiKeywordsDifference.html" name="messages_getEmojiKeywordsDifference">Get emoji keyword difference: messages.getEmojiKeywordsDifference</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getEmojiKeywordsLanguages.html" name="messages_getEmojiKeywordsLanguages">Get emoji keyword languages: messages.getEmojiKeywordsLanguages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getEmojiKeywords.html" name="messages_getEmojiKeywords">Get emoji keywords: messages.getEmojiKeywords</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getFavedStickers.html" name="messages_getFavedStickers">Get favorite stickers: messages.getFavedStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getFeaturedStickers.html" name="messages_getFeaturedStickers">Get featured stickers: messages.getFeaturedStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getGroupsForDiscussion.html" name="channels_getGroupsForDiscussion">Get groups for discussion: channels.getGroupsForDiscussion</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getInlineGameHighScores.html" name="messages_getInlineGameHighScores">Get high scores of a game sent in an inline message: messages.getInlineGameHighScores</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getGameHighScores.html" name="messages_getGameHighScores">Get high scores of a game: messages.getGameHighScores</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getParticipant.html" name="channels_getParticipant">Get info about a certain channel/supergroup participant: channels.getParticipant</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getAppUpdate.html" name="help_getAppUpdate">Get info about app updates: help.getAppUpdate</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getChats.html" name="messages_getChats">Get info about chats: messages.getChats</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_getChannels.html" name="channels_getChannels">Get info about multiple channels/supergroups: channels.getChannels</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/users_getUsers.html" name="users_getUsers">Get info about users: users.getUsers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getSupport.html" name="help_getSupport">Get info of support user: help.getSupport</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getProxyData.html" name="help_getProxyData">Get information about the current proxy: help.getProxyData</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getInviteText.html" name="help_getInviteText">Get invitation text: help.getInviteText</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/langpack_getStrings.html" name="langpack_getStrings">Get language pack strings: langpack.getStrings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/langpack_getDifference.html" name="langpack_getDifference">Get language pack updates: langpack.getDifference</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/langpack_getLangPack.html" name="langpack_getLangPack">Get language pack: langpack.getLangPack</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/langpack_getLanguage.html" name="langpack_getLanguage">Get language: langpack.getLanguage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getMaskStickers.html" name="messages_getMaskStickers">Get masks: messages.getMaskStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getSplitRanges.html" name="messages_getSplitRanges">Get message ranges to fetch: messages.getSplitRanges</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getMessages.html" name="messages_getMessages">Get messages: messages.getMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getTopPeers.html" name="contacts_getTopPeers">Get most used chats: contacts.getTopPeers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getNearestDc.html" name="help_getNearestDc">Get nearest datacenter: help.getNearestDc</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getNotifyExceptions.html" name="account_getNotifyExceptions">Get notification exceptions: account.getNotifyExceptions</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getNotifySettings.html" name="account_getNotifySettings">Get notification settings: account.getNotifySettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getStatuses.html" name="contacts_getStatuses">Get online status of all users: contacts.getStatuses</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getOnlines.html" name="messages_getOnlines">Get online users: messages.getOnlines</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getPassportConfig.html" name="help_getPassportConfig">Get passport config: help.getPassportConfig</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_getPaymentForm.html" name="payments_getPaymentForm">Get payment form: payments.getPaymentForm</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_getPaymentReceipt.html" name="payments_getPaymentReceipt">Get payment receipt: payments.getPaymentReceipt</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getLocated.html" name="contacts_getLocated">Get people nearby (geochats): contacts.getLocated</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getPinnedDialogs.html" name="messages_getPinnedDialogs">Get pinned dialogs: messages.getPinnedDialogs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getPollResults.html" name="messages_getPollResults">Get poll results: messages.getPollResults</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getHistory.html" name="messages_getHistory">Get previous messages of a group: messages.getHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getPrivacy.html" name="account_getPrivacy">Get privacy settings: account.getPrivacy</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getRecentLocations.html" name="messages_getRecentLocations">Get recent locations: messages.getRecentLocations</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getRecentStickers.html" name="messages_getRecentStickers">Get recent stickers: messages.getRecentStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getRecentMeUrls.html" name="help_getRecentMeUrls">Get recent t.me URLs: help.getRecentMeUrls</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_getSaved.html" name="contacts_getSaved">Get saved contacts: contacts.getSaved</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getSavedGifs.html" name="messages_getSavedGifs">Get saved gifs: messages.getSavedGifs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_getSavedInfo.html" name="payments_getSavedInfo">Get saved payments info: payments.getSavedInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getSearchCounters.html" name="messages_getSearchCounters">Get search counter: messages.getSearchCounters</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getSecureValue.html" name="account_getSecureValue">Get secure value for telegram passport: account.getSecureValue</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getConfig.html" name="help_getConfig">Get server configuration: help.getConfig</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getStatsURL.html" name="messages_getStatsURL">Get stats URL: messages.getStatsURL</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getAttachedStickers.html" name="messages_getAttachedStickers">Get stickers attachable to images: messages.getAttachedStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getStickers.html" name="messages_getStickers">Get stickers: messages.getStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getSupportName.html" name="help_getSupportName">Get support name: help.getSupportName</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getWebAuthorizations.html" name="account_getWebAuthorizations">Get telegram web login authorizations: account.getWebAuthorizations</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getTmpPassword.html" name="account_getTmpPassword">Get temporary password for buying products through bots: account.getTmpPassword</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getBotCallbackAnswer.html" name="messages_getBotCallbackAnswer">Get the callback answer of a bot (after clicking a button): messages.getBotCallbackAnswer</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getAppChangelog.html" name="help_getAppChangelog">Get the changelog of this app: help.getAppChangelog</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getPassword.html" name="account_getPassword">Get the current password: account.getPassword</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_exportMessageLink.html" name="channels_exportMessageLink">Get the link of a message in a channel: channels.exportMessageLink</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/photos_getUserPhotos.html" name="photos_getUserPhotos">Get the profile photos of a user: photos.getUserPhotos</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getPeerSettings.html" name="messages_getPeerSettings">Get the settings of apeer: messages.getPeerSettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getUnreadMentions.html" name="messages_getUnreadMentions">Get unread mentions: messages.getUnreadMentions</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getTermsOfServiceUpdate.html" name="help_getTermsOfServiceUpdate">Get updated TOS: help.getTermsOfServiceUpdate</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_getUserInfo.html" name="help_getUserInfo">Get user info: help.getUserInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getWallPaper.html" name="account_getWallPaper">Get wallpaper info: account.getWallPaper</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getWebPage.html" name="messages_getWebPage">Get webpage preview: messages.getWebPage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getWebPagePreview.html" name="messages_getWebPagePreview">Get webpage preview: messages.getWebPagePreview</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_getDialogs.html" name="messages_getDialogs">Gets list of chats: you should use $MadelineProto->get_dialogs() instead: https://docs.madelineproto.xyz/docs/DIALOGS.html: messages.getDialogs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_searchGlobal.html" name="messages_searchGlobal">Global message search: messages.searchGlobal</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_hidePeerSettingsBar.html" name="messages_hidePeerSettingsBar">Hide peer settings bar: messages.hidePeerSettingsBar</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_importChatInvite.html" name="messages_importChatInvite">Import chat invite: messages.importChatInvite</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/initConnection.html" name="initConnection">Initializes connection and save information on the user's device and application.: initConnection</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_installWallPaper.html" name="account_installWallPaper">Install wallpaper: account.installWallPaper</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_cancelCode.html" name="auth_cancelCode">Invalidate sent phone code: auth.cancelCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeWithTakeout.html" name="invokeWithTakeout">Invoke method from takeout session: invokeWithTakeout</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeWithLayer.html" name="invokeWithLayer">Invoke this method with layer X: invokeWithLayer</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeWithMessagesRange.html" name="invokeWithMessagesRange">Invoke with messages range: invokeWithMessagesRange</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeWithoutUpdates.html" name="invokeWithoutUpdates">Invoke with method without returning updates in the socket: invokeWithoutUpdates</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeAfterMsg.html" name="invokeAfterMsg">Invokes a query after successfull completion of one of the previous queries.: invokeAfterMsg</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_joinChannel.html" name="channels_joinChannel">Join a channel/supergroup: channels.joinChannel</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_editBanned.html" name="channels_editBanned">Kick or ban a user from a channel/supergroup: channels.editBanned</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_leaveChannel.html" name="channels_leaveChannel">Leave a channel/supergroup: channels.leaveChannel</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_saveAppLog.html" name="help_saveAppLog">Log data for developer of this app: help.saveAppLog</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_readHistory.html" name="channels_readHistory">Mark channel/supergroup history as read: channels.readHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_readMessageContents.html" name="channels_readMessageContents">Mark channel/supergroup messages as read: channels.readMessageContents</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_markDialogUnread.html" name="messages_markDialogUnread">Mark dialog as unread : messages.markDialogUnread</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_readMentions.html" name="messages_readMentions">Mark mentions as read: messages.readMentions</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_readMessageContents.html" name="messages_readMessageContents">Mark message as read: messages.readMessageContents</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_readEncryptedHistory.html" name="messages_readEncryptedHistory">Mark messages as read in secret chats: messages.readEncryptedHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_readHistory.html" name="messages_readHistory">Mark messages as read: messages.readHistory</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_receivedMessages.html" name="messages_receivedMessages">Mark messages as read: messages.receivedMessages</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_readFeaturedStickers.html" name="messages_readFeaturedStickers">Mark new featured stickers as read: messages.readFeaturedStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/phone_receivedCall.html" name="phone_receivedCall">Notify server that you received a call (server will refuse all incoming calls until the current call is over): phone.receivedCall</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_toggleDialogPin.html" name="messages_toggleDialogPin">Pin or unpin dialog: messages.toggleDialogPin</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_registerDevice.html" name="account_registerDevice">Register device for push notifications: account.registerDevice</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_uninstallStickerSet.html" name="messages_uninstallStickerSet">Remove a sticker set: messages.uninstallStickerSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/stickers_removeStickerFromSet.html" name="stickers_removeStickerFromSet">Remove sticker from stickerset: stickers.removeStickerFromSet</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_reorderPinnedDialogs.html" name="messages_reorderPinnedDialogs">Reorder pinned dialogs: messages.reorderPinnedDialogs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_reorderStickerSets.html" name="messages_reorderStickerSets">Reorder sticker sets: messages.reorderStickerSets</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_reportSpam.html" name="channels_reportSpam">Report a message in a supergroup/channel for spam: channels.reportSpam</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_report.html" name="messages_report">Report a message: messages.report</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_reportSpam.html" name="messages_reportSpam">Report a peer for spam: messages.reportSpam</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_reportEncryptedSpam.html" name="messages_reportEncryptedSpam">Report for spam a secret chat: messages.reportEncryptedSpam</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_reportPeer.html" name="account_reportPeer">Report for spam: account.reportPeer</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_requestUrlAuth.html" name="messages_requestUrlAuth">Request URL authorization: messages.requestUrlAuth</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resendPasswordEmail.html" name="account_resendPasswordEmail">Resend password recovery email: account.resendPasswordEmail</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_resendCode.html" name="auth_resendCode">Resend the SMS verification code: auth.resendCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resetNotifySettings.html" name="account_resetNotifySettings">Reset all notification settings: account.resetNotifySettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resetWebAuthorizations.html" name="account_resetWebAuthorizations">Reset all telegram web login authorizations: account.resetWebAuthorizations</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_resetSaved.html" name="contacts_resetSaved">Reset saved contacts: contacts.resetSaved</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_resetTopPeerRating.html" name="contacts_resetTopPeerRating">Reset top peer rating for a certain category/peer: contacts.resetTopPeerRating</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_resetWallPapers.html" name="account_resetWallPapers">Reset wallpapers: account.resetWallPapers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/invokeAfterMsgs.html" name="invokeAfterMsgs">Result type returned by a current query.: invokeAfterMsgs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_getWallPapers.html" name="account_getWallPapers">Returns a list of available wallpapers.: account.getWallPapers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_saveGif.html" name="messages_saveGif">Save a GIF: messages.saveGif</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_saveDraft.html" name="messages_saveDraft">Save a message draft: messages.saveDraft</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_saveAutoDownloadSettings.html" name="account_saveAutoDownloadSettings">Save autodownload settings: account.saveAutoDownloadSettings</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/phone_saveCallDebug.html" name="phone_saveCallDebug">Save call debugging info: phone.saveCallDebug</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_saveSecureValue.html" name="account_saveSecureValue">Save telegram passport secure value: account.saveSecureValue</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_saveWallPaper.html" name="account_saveWallPaper">Save wallpaper: account.saveWallPaper</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_search.html" name="contacts_search">Search contacts: contacts.search</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_searchGifs.html" name="messages_searchGifs">Search gifs: messages.searchGifs</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_search.html" name="messages_search">Search peers or messages: messages.search</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/bots_sendCustomRequest.html" name="bots_sendCustomRequest">Send a custom request to the bot API: bots.sendCustomRequest</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendEncryptedFile.html" name="messages_sendEncryptedFile">Send a file to a secret chat: messages.sendEncryptedFile</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendMedia.html" name="messages_sendMedia">Send a media: messages.sendMedia</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendMessage.html" name="messages_sendMessage">Send a message: messages.sendMessage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendEncryptedService.html" name="messages_sendEncryptedService">Send a service message to a secret chat: messages.sendEncryptedService</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendMultiMedia.html" name="messages_sendMultiMedia">Send an album: messages.sendMultiMedia</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_requestPasswordRecovery.html" name="auth_requestPasswordRecovery">Send an email to recover the 2FA password: auth.requestPasswordRecovery</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_sendConfirmPhoneCode.html" name="account_sendConfirmPhoneCode">Send confirmation phone code: account.sendConfirmPhoneCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_sendVerifyEmailCode.html" name="account_sendVerifyEmailCode">Send email verification code: account.sendVerifyEmailCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendInlineBotResult.html" name="messages_sendInlineBotResult">Send inline bot result obtained with messages.getInlineBotResults to the chat: messages.sendInlineBotResult</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendEncrypted.html" name="messages_sendEncrypted">Send message to secret chat: messages.sendEncrypted</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_sendVerifyPhoneCode.html" name="account_sendVerifyPhoneCode">Send phone verification code: account.sendVerifyPhoneCode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendScreenshotNotification.html" name="messages_sendScreenshotNotification">Send screenshot notification: messages.sendScreenshotNotification</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setEncryptedTyping.html" name="messages_setEncryptedTyping">Send typing notification to secret chat: messages.setEncryptedTyping</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_sendVote.html" name="messages_sendVote">Send vote: messages.sendVote</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/bots_answerWebhookJSONQuery.html" name="bots_answerWebhookJSONQuery">Send webhook request via bot API: bots.answerWebhookJSONQuery</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_setAccountTTL.html" name="account_setAccountTTL">Set account TTL: account.setAccountTTL</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_setContactSignUpNotification.html" name="account_setContactSignUpNotification">Set contact sign up notification: account.setContactSignUpNotification</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_setDiscussionGroup.html" name="channels_setDiscussionGroup">Set discussion group of channel: channels.setDiscussionGroup</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/phone_setCallRating.html" name="phone_setCallRating">Set phone call rating: phone.setCallRating</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_setPrivacy.html" name="account_setPrivacy">Set privacy settings: account.setPrivacy</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/users_setSecureValueErrors.html" name="users_setSecureValueErrors">Set secure value error for telegram passport: users.setSecureValueErrors</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setInlineGameScore.html" name="messages_setInlineGameScore">Set the game score of an inline message: messages.setInlineGameScore</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_setGameScore.html" name="messages_setGameScore">Set the game score: messages.setGameScore</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_setStickers.html" name="channels_setStickers">Set the supergroup/channel stickerpack: channels.setStickers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/help_setBotUpdatesStatus.html" name="help_setBotUpdatesStatus">Set the update status of webhook: help.setBotUpdatesStatus</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_startBot.html" name="messages_startBot">Start a bot: messages.startBot</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_initTakeoutSession.html" name="account_initTakeoutSession">Start account exporting session: account.initTakeoutSession</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_unregisterDevice.html" name="account_unregisterDevice">Stop sending PUSH notifications to app: account.unregisterDevice</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_toggleSignatures.html" name="channels_toggleSignatures">Toggle channel signatures: channels.toggleSignatures</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_toggleSlowMode.html" name="channels_toggleSlowMode">Toggle slow mode: channels.toggleSlowMode</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_toggleTopPeers.html" name="contacts_toggleTopPeers">Toggle top peers: contacts.toggleTopPeers</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/contacts_unblock.html" name="contacts_unblock">Unblock a user: contacts.unblock</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_updateStatus.html" name="account_updateStatus">Update online status: account.updateStatus</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_updatePinnedMessage.html" name="messages_updatePinnedMessage">Update pinned message: messages.updatePinnedMessage</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_updateProfile.html" name="account_updateProfile">Update profile info: account.updateProfile</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/channels_updateUsername.html" name="channels_updateUsername">Update the username of a supergroup/channel: channels.updateUsername</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_updateUsername.html" name="account_updateUsername">Update this user's username: account.updateUsername</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_uploadMedia.html" name="messages_uploadMedia">Upload a file without sending it to anyone: messages.uploadMedia</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/messages_uploadEncryptedFile.html" name="messages_uploadEncryptedFile">Upload a secret chat file without sending it to anyone: messages.uploadEncryptedFile</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/photos_uploadProfilePhoto.html" name="photos_uploadProfilePhoto">Upload profile photo: photos.uploadProfilePhoto</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_uploadWallPaper.html" name="account_uploadWallPaper">Upload wallpaper: account.uploadWallPaper</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/auth_recoverPassword.html" name="auth_recoverPassword">Use the code that was emailed to you after running $MadelineProto->auth->requestPasswordRecovery to login to your account: auth.recoverPassword</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/payments_validateRequestedInfo.html" name="payments_validateRequestedInfo">Validate requested payment info: payments.validateRequestedInfo</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_verifyEmail.html" name="account_verifyEmail">Verify email address: account.verifyEmail</a>
|
|
||||||
* <a href="https://docs.madelineproto.xyz/API_docs/methods/account_verifyPhone.html" name="account_verifyPhone">Verify phone number: account.verifyPhone</a>
|
|
||||||
* [Peers](https://docs.madelineproto.xyz/docs/USING_METHODS.html#peers)
|
* [Peers](https://docs.madelineproto.xyz/docs/USING_METHODS.html#peers)
|
||||||
* [Files](https://docs.madelineproto.xyz/docs/FILES.html)
|
* [Files](https://docs.madelineproto.xyz/docs/FILES.html)
|
||||||
* [Secret chats](https://docs.madelineproto.xyz/docs/USING_METHODS.html#secret-chats)
|
* [Secret chats](https://docs.madelineproto.xyz/docs/USING_METHODS.html#secret-chats)
|
||||||
|
2
docs
2
docs
@ -1 +1 @@
|
|||||||
Subproject commit 1c91f7808bc95c909b6ba8502b68b5d4fe9c9a41
|
Subproject commit 11f1a298b7d50a1ee6a71bd5bbd61f40fc288100
|
4
phar.php
4
phar.php
@ -61,10 +61,10 @@ function ___install_madeline()
|
|||||||
$release_branch = '';
|
$release_branch = '';
|
||||||
}
|
}
|
||||||
$release_fallback_branch = '';
|
$release_fallback_branch = '';
|
||||||
if (isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], '000webhost.io') && $custom_branch === null) {
|
/*if (isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], '000webhost.io') && $custom_branch === null) {
|
||||||
$release_branch = '-deprecated';
|
$release_branch = '-deprecated';
|
||||||
$release_fallback_branch = '-deprecated';
|
$release_fallback_branch = '-deprecated';
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if (PHP_MAJOR_VERSION <= 5) {
|
if (PHP_MAJOR_VERSION <= 5) {
|
||||||
$release_branch = '5'.$release_branch;
|
$release_branch = '5'.$release_branch;
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
namespace danog\MadelineProto;
|
namespace danog\MadelineProto;
|
||||||
|
|
||||||
use Amp\Deferred;
|
use Amp\Deferred;
|
||||||
|
use function Amp\File\put;
|
||||||
|
use function Amp\File\rename;
|
||||||
|
use function Amp\File\get;
|
||||||
|
use function Amp\File\exists;
|
||||||
|
|
||||||
class API extends APIFactory
|
class API extends APIFactory
|
||||||
{
|
{
|
||||||
@ -62,21 +66,15 @@ class API extends APIFactory
|
|||||||
$realpaths = Serialization::realpaths($params);
|
$realpaths = Serialization::realpaths($params);
|
||||||
$this->session = $realpaths['file'];
|
$this->session = $realpaths['file'];
|
||||||
|
|
||||||
if (file_exists($realpaths['file'])) {
|
if (yield exists($realpaths['file'])) {
|
||||||
if (!file_exists($realpaths['lockfile'])) {
|
Logger::log('Waiting for shared lock of serialization lockfile...');
|
||||||
touch($realpaths['lockfile']);
|
$unlock = yield Tools::flock($realpaths['lockfile'], LOCK_SH);
|
||||||
clearstatcache();
|
Logger::log('Shared lock acquired, deserializing...');
|
||||||
}
|
|
||||||
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
|
|
||||||
\danog\MadelineProto\Logger::log('Waiting for shared lock of serialization lockfile...');
|
|
||||||
flock($realpaths['lockfile'], LOCK_SH);
|
|
||||||
\danog\MadelineProto\Logger::log('Shared lock acquired, deserializing...');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$tounserialize = file_get_contents($realpaths['file']);
|
$tounserialize = yield get($realpaths['file']);
|
||||||
} finally {
|
} finally {
|
||||||
flock($realpaths['lockfile'], LOCK_UN);
|
$unlock();
|
||||||
fclose($realpaths['lockfile']);
|
|
||||||
}
|
}
|
||||||
\danog\MadelineProto\Magic::class_exists();
|
\danog\MadelineProto\Magic::class_exists();
|
||||||
|
|
||||||
@ -142,7 +140,6 @@ class API extends APIFactory
|
|||||||
//$pong = yield $this->ping(['ping_id' => 3], ['async' => true]);
|
//$pong = yield $this->ping(['ping_id' => 3], ['async' => true]);
|
||||||
//\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE);
|
//\danog\MadelineProto\Logger::log('Pong: '.$pong['ping_id'], Logger::ULTRA_VERBOSE);
|
||||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +155,7 @@ class API extends APIFactory
|
|||||||
$this->API = new MTProto($params);
|
$this->API = new MTProto($params);
|
||||||
$this->APIFactory();
|
$this->APIFactory();
|
||||||
$deferred->resolve();
|
$deferred->resolve();
|
||||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['apifactory_start'], Logger::VERBOSE);
|
Logger::log(\danog\MadelineProto\Lang::$current_lang['apifactory_start'], Logger::VERBOSE);
|
||||||
yield $this->API->initAsync();
|
yield $this->API->initAsync();
|
||||||
$this->APIFactory();
|
$this->APIFactory();
|
||||||
$this->asyncInitPromise = null;
|
$this->asyncInitPromise = null;
|
||||||
@ -312,14 +309,11 @@ class API extends APIFactory
|
|||||||
}
|
}
|
||||||
$this->serialized = time();
|
$this->serialized = time();
|
||||||
$realpaths = Serialization::realpaths($filename);
|
$realpaths = Serialization::realpaths($filename);
|
||||||
if (!file_exists($realpaths['lockfile'])) {
|
Logger::log('Waiting for exclusive lock of serialization lockfile...');
|
||||||
touch($realpaths['lockfile']);
|
|
||||||
clearstatcache();
|
$unlock = yield Tools::flock($realpaths['lockfile'], LOCK_EX);
|
||||||
}
|
|
||||||
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w');
|
Logger::log('Lock acquired, serializing');
|
||||||
\danog\MadelineProto\Logger::log('Waiting for exclusive lock of serialization lockfile...');
|
|
||||||
flock($realpaths['lockfile'], LOCK_EX);
|
|
||||||
\danog\MadelineProto\Logger::log('Lock acquired, serializing');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!$this->getting_api_id) {
|
if (!$this->getting_api_id) {
|
||||||
@ -332,17 +326,16 @@ class API extends APIFactory
|
|||||||
$this->API->settings['logger']['logger_param'] = [$this->API, 'noop'];
|
$this->API->settings['logger']['logger_param'] = [$this->API, 'noop'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$wrote = file_put_contents($realpaths['tempfile'], serialize($this));
|
$wrote = yield put($realpaths['tempfile'], serialize($this));
|
||||||
rename($realpaths['tempfile'], $realpaths['file']);
|
yield rename($realpaths['tempfile'], $realpaths['file']);
|
||||||
} finally {
|
} finally {
|
||||||
if (!$this->getting_api_id) {
|
if (!$this->getting_api_id) {
|
||||||
$this->API->settings['updates']['callback'] = $update_closure;
|
$this->API->settings['updates']['callback'] = $update_closure;
|
||||||
$this->API->settings['logger']['logger_param'] = $logger_closure;
|
$this->API->settings['logger']['logger_param'] = $logger_closure;
|
||||||
}
|
}
|
||||||
flock($realpaths['lockfile'], LOCK_UN);
|
$unlock();
|
||||||
fclose($realpaths['lockfile']);
|
|
||||||
}
|
}
|
||||||
\danog\MadelineProto\Logger::log('Done serializing');
|
Logger::log('Done serializing');
|
||||||
|
|
||||||
return $wrote;
|
return $wrote;
|
||||||
})());
|
})());
|
||||||
|
@ -20,7 +20,6 @@ namespace danog\MadelineProto;
|
|||||||
|
|
||||||
use Amp\ByteStream\ClosedException;
|
use Amp\ByteStream\ClosedException;
|
||||||
use Amp\Deferred;
|
use Amp\Deferred;
|
||||||
use Amp\Promise;
|
|
||||||
use danog\MadelineProto\Loop\Connection\CheckLoop;
|
use danog\MadelineProto\Loop\Connection\CheckLoop;
|
||||||
use danog\MadelineProto\Loop\Connection\HttpWaitLoop;
|
use danog\MadelineProto\Loop\Connection\HttpWaitLoop;
|
||||||
use danog\MadelineProto\Loop\Connection\ReadLoop;
|
use danog\MadelineProto\Loop\Connection\ReadLoop;
|
||||||
@ -42,6 +41,7 @@ class Connection
|
|||||||
use Crypt;
|
use Crypt;
|
||||||
use MsgIdHandler;
|
use MsgIdHandler;
|
||||||
use SeqNoHandler;
|
use SeqNoHandler;
|
||||||
|
|
||||||
use \danog\Serializable;
|
use \danog\Serializable;
|
||||||
use Tools;
|
use Tools;
|
||||||
|
|
||||||
@ -55,39 +55,23 @@ class Connection
|
|||||||
|
|
||||||
public $stream;
|
public $stream;
|
||||||
|
|
||||||
public $time_delta = 0;
|
|
||||||
public $type = 0;
|
public $type = 0;
|
||||||
public $peer_tag;
|
public $peer_tag;
|
||||||
|
|
||||||
public $temp_auth_key;
|
public $temp_auth_key;
|
||||||
public $auth_key;
|
public $auth_key;
|
||||||
public $session_id;
|
|
||||||
public $session_out_seq_no = 0;
|
|
||||||
public $session_in_seq_no = 0;
|
|
||||||
public $incoming_messages = [];
|
|
||||||
public $outgoing_messages = [];
|
|
||||||
public $new_incoming = [];
|
|
||||||
public $new_outgoing = [];
|
|
||||||
public $pending_outgoing = [];
|
public $pending_outgoing = [];
|
||||||
public $pending_outgoing_key = 0;
|
public $pending_outgoing_key = 0;
|
||||||
public $pending_outgoing_unencrypted = [];
|
|
||||||
public $pending_outgoing_unencrypted_key = 0;
|
|
||||||
public $max_incoming_id;
|
|
||||||
public $max_outgoing_id;
|
|
||||||
public $authorized = false;
|
public $authorized = false;
|
||||||
public $call_queue = [];
|
|
||||||
public $ack_queue = [];
|
|
||||||
public $i = [];
|
|
||||||
public $last_recv = 0;
|
|
||||||
public $last_http_wait = 0;
|
|
||||||
|
|
||||||
public $datacenter;
|
public $datacenter;
|
||||||
public $API;
|
public $API;
|
||||||
public $resumeWriterDeferred;
|
|
||||||
public $ctx;
|
|
||||||
public $pendingCheckWatcherId;
|
|
||||||
|
|
||||||
public $http_req_count = 0;
|
public $ctx;
|
||||||
public $http_res_count = 0;
|
|
||||||
|
|
||||||
public function getCtx()
|
public function getCtx()
|
||||||
{
|
{
|
||||||
@ -294,14 +278,4 @@ class Connection
|
|||||||
{
|
{
|
||||||
return ['peer_tag', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'max_incoming_id', 'max_outgoing_id', 'authorized', 'ack_queue'];
|
return ['peer_tag', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'max_incoming_id', 'max_outgoing_id', 'authorized', 'ack_queue'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __wakeup()
|
|
||||||
{
|
|
||||||
$this->time_delta = 0;
|
|
||||||
$this->pending_outgoing = [];
|
|
||||||
$this->new_outgoing = [];
|
|
||||||
$this->new_incoming = [];
|
|
||||||
$this->outgoing_messages = [];
|
|
||||||
$this->incoming_messages = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ class Conversion
|
|||||||
{
|
{
|
||||||
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
||||||
if (!extension_loaded('sqlite3')) {
|
if (!extension_loaded('sqlite3')) {
|
||||||
throw new Exception(['extension', 'sqlite3']);
|
throw Exception::extension('sqlite3');
|
||||||
}
|
}
|
||||||
if (!isset(pathinfo($session)['extension'])) {
|
if (!isset(pathinfo($session)['extension'])) {
|
||||||
$session .= '.session';
|
$session .= '.session';
|
||||||
|
@ -21,6 +21,7 @@ namespace danog\MadelineProto;
|
|||||||
|
|
||||||
use Amp\Artax\Client;
|
use Amp\Artax\Client;
|
||||||
use Amp\Artax\Cookie\ArrayCookieJar;
|
use Amp\Artax\Cookie\ArrayCookieJar;
|
||||||
|
use Amp\Artax\Cookie\CookieJar;
|
||||||
use Amp\Artax\DefaultClient;
|
use Amp\Artax\DefaultClient;
|
||||||
use Amp\Artax\HttpSocketPool;
|
use Amp\Artax\HttpSocketPool;
|
||||||
use Amp\CancellationToken;
|
use Amp\CancellationToken;
|
||||||
@ -38,6 +39,7 @@ use Amp\Socket\ClientConnectContext;
|
|||||||
use Amp\Socket\ClientSocket;
|
use Amp\Socket\ClientSocket;
|
||||||
use Amp\Socket\ClientTlsContext;
|
use Amp\Socket\ClientTlsContext;
|
||||||
use Amp\Socket\ConnectException;
|
use Amp\Socket\ConnectException;
|
||||||
|
use Amp\Socket\Socket;
|
||||||
use Amp\TimeoutException;
|
use Amp\TimeoutException;
|
||||||
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
||||||
use danog\MadelineProto\Stream\ConnectionContext;
|
use danog\MadelineProto\Stream\ConnectionContext;
|
||||||
@ -55,7 +57,6 @@ use danog\MadelineProto\Stream\Transport\WssStream;
|
|||||||
use danog\MadelineProto\Stream\Transport\WsStream;
|
use danog\MadelineProto\Stream\Transport\WsStream;
|
||||||
use function Amp\call;
|
use function Amp\call;
|
||||||
use function Amp\Socket\Internal\parseUri;
|
use function Amp\Socket\Internal\parseUri;
|
||||||
use Amp\Artax\Cookie\CookieJar;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages datacenters.
|
* Manages datacenters.
|
||||||
@ -98,7 +99,7 @@ class DataCenter
|
|||||||
$this->HTTPClient = new DefaultClient($this->CookieJar, new HttpSocketPool(new ProxySocketPool([$this, 'rawConnectAsync'])));
|
$this->HTTPClient = new DefaultClient($this->CookieJar, new HttpSocketPool(new ProxySocketPool([$this, 'rawConnectAsync'])));
|
||||||
|
|
||||||
$DoHHTTPClient = new DefaultClient(
|
$DoHHTTPClient = new DefaultClient(
|
||||||
$this->CookieJar,
|
$this->CookieJar,
|
||||||
new HttpSocketPool(
|
new HttpSocketPool(
|
||||||
new ProxySocketPool(
|
new ProxySocketPool(
|
||||||
function (string $uri, CancellationToken $token = null, ClientConnectContext $ctx = null) {
|
function (string $uri, CancellationToken $token = null, ClientConnectContext $ctx = null) {
|
||||||
@ -327,8 +328,34 @@ class DataCenter
|
|||||||
|
|
||||||
continue; // Could not connect to host, try next host in the list.
|
continue; // Could not connect to host, try next host in the list.
|
||||||
}
|
}
|
||||||
|
if ($dc = $ctx->getDc()) {
|
||||||
|
$callback = [$this->sockets[$dc], 'haveRead'];
|
||||||
|
$socket = new class($socket) extends ClientSocket
|
||||||
|
{
|
||||||
|
private $callback;
|
||||||
|
public function setReadCallback($callback)
|
||||||
|
{
|
||||||
|
$this->callback = $callback;
|
||||||
|
}
|
||||||
|
|
||||||
return new ClientSocket($socket);
|
/** @inheritdoc */
|
||||||
|
public function read(): Promise
|
||||||
|
{
|
||||||
|
$promise = parent::read();
|
||||||
|
$promise->onResolve(function ($e, $res) {
|
||||||
|
if ($res) {
|
||||||
|
($this->callback)();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$socket->setReadCallback($callback);
|
||||||
|
} else {
|
||||||
|
$socket = new ClientSocket($socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is reached if either all URIs failed or the maximum number of attempts is reached.
|
// This is reached if either all URIs failed or the maximum number of attempts is reached.
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
namespace danog\MadelineProto;
|
namespace danog\MadelineProto;
|
||||||
|
|
||||||
|
// This code was written a few years ago: it is garbage, and has to be rewritten
|
||||||
class DocsBuilder
|
class DocsBuilder
|
||||||
{
|
{
|
||||||
use \danog\MadelineProto\TL\TL;
|
use \danog\MadelineProto\TL\TL;
|
||||||
|
@ -23,9 +23,21 @@ trait Methods
|
|||||||
{
|
{
|
||||||
public function mk_methods()
|
public function mk_methods()
|
||||||
{
|
{
|
||||||
$bots = json_decode(file_get_contents('https://rpc.madelineproto.xyz/bot.json'), true)['result'];
|
static $bots;
|
||||||
$errors = json_decode(file_get_contents('https://rpc.madelineproto.xyz/v1.json'), true);
|
if (!$bots) $bots = json_decode(file_get_contents('https://rpc.madelineproto.xyz/bot.json'), true)['result'];
|
||||||
$errors['result'] = array_merge_recursive(...$errors['result']);
|
static $errors;
|
||||||
|
if (!$errors) $errors = json_decode(file_get_contents('https://rpc.madelineproto.xyz/v1.json'), true);
|
||||||
|
$new = ['result' => []];
|
||||||
|
foreach ($errors['result'] as $code => $suberrors) {
|
||||||
|
foreach ($suberrors as $method => $suberrors) {
|
||||||
|
if (!isset($new[$method])) {
|
||||||
|
$new[$method] = [];
|
||||||
|
}
|
||||||
|
foreach ($suberrors as $error) {
|
||||||
|
$new['result'][$method][] = [$error, $code];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach (glob('methods/'.$this->any) as $unlink) {
|
foreach (glob('methods/'.$this->any) as $unlink) {
|
||||||
unlink($unlink);
|
unlink($unlink);
|
||||||
}
|
}
|
||||||
@ -76,11 +88,12 @@ trait Methods
|
|||||||
$this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\\['.$params.'\\]) === [$'.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md)<a name="'.$method.'"></a>
|
$this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\\['.$params.'\\]) === [$'.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md)<a name="'.$method.'"></a>
|
||||||
|
|
||||||
';
|
';
|
||||||
|
/*
|
||||||
if (!isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]) && isset($this->td_descriptions['methods'][$data['method']])) {
|
if (!isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]) && isset($this->td_descriptions['methods'][$data['method']])) {
|
||||||
$this->human_docs_methods[$this->td_descriptions['methods'][$data['method']]['description'].': '.$data['method']] = '* <a href="'.$method.'.html" name="'.$method.'">'.$this->td_descriptions['methods'][$data['method']]['description'].': '.$data['method'].'</a>
|
$this->human_docs_methods[$this->td_descriptions['methods'][$data['method']]['description'].': '.$data['method']] = '* <a href="'.$method.'.html" name="'.$method.'">'.$this->td_descriptions['methods'][$data['method']]['description'].': '.$data['method'].'</a>
|
||||||
|
|
||||||
';
|
';
|
||||||
}
|
}*/
|
||||||
$params = '';
|
$params = '';
|
||||||
$lua_params = '';
|
$lua_params = '';
|
||||||
$pwr_params = '';
|
$pwr_params = '';
|
||||||
@ -192,11 +205,12 @@ image: https://docs.madelineproto.xyz/favicons/android-chrome-256x256.png
|
|||||||
|
|
||||||
|
|
||||||
';
|
';
|
||||||
|
/*
|
||||||
if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) {
|
if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) {
|
||||||
$header .= '**'.\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]."**\n\n\n\n\n";
|
$header .= '**'.\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]."**\n\n\n\n\n";
|
||||||
file_put_contents('methods/'.$method.'.md', $header);
|
file_put_contents('methods/'.$method.'.md', $header);
|
||||||
continue;
|
continue;
|
||||||
}
|
}*/
|
||||||
if ($this->td) {
|
if ($this->td) {
|
||||||
$header .= 'YOU CANNOT USE THIS METHOD IN MADELINEPROTO
|
$header .= 'YOU CANNOT USE THIS METHOD IN MADELINEPROTO
|
||||||
|
|
||||||
@ -293,14 +307,15 @@ You can also use normal markdown, note that to create mentions you must use the
|
|||||||
MadelineProto supports all html entities supported by [html_entity_decode](http://php.net/manual/en/function.html-entity-decode.php).
|
MadelineProto supports all html entities supported by [html_entity_decode](http://php.net/manual/en/function.html-entity-decode.php).
|
||||||
';
|
';
|
||||||
}
|
}
|
||||||
if (isset($errors['result'][$data['method']])) {
|
if (isset($new['result'][$data['method']])) {
|
||||||
$example .= '### Errors this method can return:
|
$example .= '### Errors
|
||||||
|
|
||||||
| Error | Description |
|
| Code | Type | Description |
|
||||||
|----------|---------------|
|
|------|----------|---------------|
|
||||||
';
|
';
|
||||||
foreach ($errors['result'][$data['method']] as $error) {
|
foreach ($new['result'][$data['method']] as $error) {
|
||||||
$example .= '|'.$error.'|'.$errors['human_result'][$error][0].'|'."\n";
|
[$error, $code] = $error;
|
||||||
|
$example .= "|$code|$error|".$errors['human_result'][$error][0].'|'."\n";
|
||||||
}
|
}
|
||||||
$example .= "\n\n";
|
$example .= "\n\n";
|
||||||
}
|
}
|
||||||
|
@ -31,27 +31,8 @@ class Exception extends \Exception
|
|||||||
|
|
||||||
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
|
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
|
||||||
{
|
{
|
||||||
if (is_array($message) && $message[0] === 'extension') {
|
|
||||||
if ($message[1] === 'libtgvoip') {
|
|
||||||
$additional = 'Follow the instructions @ https://voip.madelineproto.xyz to install it.';
|
|
||||||
} elseif ($message[1] === 'prime') {
|
|
||||||
$additional = 'Follow the instructions @ https://prime.madelineproto.xyz to install it.';
|
|
||||||
} else {
|
|
||||||
$additional = 'Try running sudo apt-get install php'.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'-'.$message[1].'.';
|
|
||||||
}
|
|
||||||
$message = 'MadelineProto requires the '.$message[1].' extension to run. '.$additional;
|
|
||||||
if (php_sapi_name() !== 'cli') {
|
|
||||||
echo $message.'<br>';
|
|
||||||
}
|
|
||||||
$file = 'MadelineProto';
|
|
||||||
$line = 1;
|
|
||||||
}
|
|
||||||
$this->prettify_tl();
|
$this->prettify_tl();
|
||||||
if ($file !== null) {
|
if ($file !== null) {
|
||||||
if (basename($file) === 'Threaded.php') {
|
|
||||||
$line = debug_backtrace(0)[2]['line'];
|
|
||||||
$file = debug_backtrace(0)[2]['file'];
|
|
||||||
}
|
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
}
|
}
|
||||||
if ($line !== null) {
|
if ($line !== null) {
|
||||||
@ -61,6 +42,7 @@ class Exception extends \Exception
|
|||||||
if (strpos($message, 'socket_accept') === false) {
|
if (strpos($message, 'socket_accept') === false) {
|
||||||
\danog\MadelineProto\Logger::log($message.' in '.basename($this->file).':'.$this->line, \danog\MadelineProto\Logger::FATAL_ERROR);
|
\danog\MadelineProto\Logger::log($message.' in '.basename($this->file).':'.$this->line, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($message, ['The session is corrupted!', 'Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist', 'Please install this fork of phpseclib: https://github.com/danog/phpseclib'])) {
|
if (in_array($message, ['The session is corrupted!', 'Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist', 'Please install this fork of phpseclib: https://github.com/danog/phpseclib'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -72,6 +54,22 @@ class Exception extends \Exception
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function extension(string $extensionName)
|
||||||
|
{
|
||||||
|
$additional = 'Try running sudo apt-get install php'.PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'-'.$extensionName.'.';
|
||||||
|
if ($extensionName === 'libtgvoip') {
|
||||||
|
$additional = 'Follow the instructions @ https://voip.madelineproto.xyz to install it.';
|
||||||
|
} elseif ($extensionName === 'prime') {
|
||||||
|
$additional = 'Follow the instructions @ https://prime.madelineproto.xyz to install it.';
|
||||||
|
}
|
||||||
|
$message = 'MadelineProto requires the '.$extensionName.' extension to run. '.$additional;
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
echo $message.'<br>';
|
||||||
|
}
|
||||||
|
$file = 'MadelineProto';
|
||||||
|
$line = 1;
|
||||||
|
return new self($message, 0, null, $file, $line);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* ExceptionErrorHandler.
|
* ExceptionErrorHandler.
|
||||||
*
|
*
|
||||||
|
@ -55,7 +55,9 @@ class CheckLoop extends ResumableSignalLoop
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($connection->hasPendingCalls()) {
|
if ($connection->hasPendingCalls()) {
|
||||||
$last_recv = $connection->get_max_id(true);
|
$last_msgid = $connection->get_max_id(true);
|
||||||
|
$last_chunk = $connection->getLastChunk();
|
||||||
|
|
||||||
if ($connection->temp_auth_key !== null) {
|
if ($connection->temp_auth_key !== null) {
|
||||||
$full_message_ids = $connection->getPendingCalls(); //array_values($connection->new_outgoing);
|
$full_message_ids = $connection->getPendingCalls(); //array_values($connection->new_outgoing);
|
||||||
foreach (array_chunk($full_message_ids, 8192) as $message_ids) {
|
foreach (array_chunk($full_message_ids, 8192) as $message_ids) {
|
||||||
@ -139,7 +141,7 @@ class CheckLoop extends ResumableSignalLoop
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($connection->get_max_id(true) === $last_recv) {
|
if ($connection->get_max_id(true) === $last_msgid && $connection->getLastChunk() === $last_chunk) {
|
||||||
$API->logger->logger("We did not receive a response for $timeout seconds: reconnecting and exiting check loop on DC $datacenter");
|
$API->logger->logger("We did not receive a response for $timeout seconds: reconnecting and exiting check loop on DC $datacenter");
|
||||||
$this->exitedLoop();
|
$this->exitedLoop();
|
||||||
yield $connection->reconnect();
|
yield $connection->reconnect();
|
||||||
|
@ -214,7 +214,6 @@ class ReadLoop extends SignalLoop
|
|||||||
$connection->incoming_messages[$message_id]['content'] = $deserialized;
|
$connection->incoming_messages[$message_id]['content'] = $deserialized;
|
||||||
$connection->incoming_messages[$message_id]['response'] = -1;
|
$connection->incoming_messages[$message_id]['response'] = -1;
|
||||||
$connection->new_incoming[$message_id] = $message_id;
|
$connection->new_incoming[$message_id] = $message_id;
|
||||||
$connection->last_recv = time();
|
|
||||||
$connection->last_http_wait = 0;
|
$connection->last_http_wait = 0;
|
||||||
|
|
||||||
$API->logger->logger('Received payload from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
$API->logger->logger('Received payload from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||||
|
@ -187,7 +187,7 @@ class WriteLoop extends ResumableSignalLoop
|
|||||||
$body_length = strlen($message['serialized_body']);
|
$body_length = strlen($message['serialized_body']);
|
||||||
$actual_length = $body_length + 32;
|
$actual_length = $body_length + 32;
|
||||||
if ($total_length && $total_length + $actual_length > 32760 || $count >= 1020) {
|
if ($total_length && $total_length + $actual_length > 32760 || $count >= 1020) {
|
||||||
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::NOTICE);
|
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,6 +215,7 @@ class WriteLoop extends ResumableSignalLoop
|
|||||||
'system_lang_code' => $API->settings['app_info']['lang_code'],
|
'system_lang_code' => $API->settings['app_info']['lang_code'],
|
||||||
'lang_code' => $API->settings['app_info']['lang_code'],
|
'lang_code' => $API->settings['app_info']['lang_code'],
|
||||||
'lang_pack' => $API->settings['app_info']['lang_pack'],
|
'lang_pack' => $API->settings['app_info']['lang_pack'],
|
||||||
|
'proxy' => $connection->getCtx()->getInputClientProxy(),
|
||||||
'query' => $MTmessage['body'],
|
'query' => $MTmessage['body'],
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@ -247,7 +248,7 @@ class WriteLoop extends ResumableSignalLoop
|
|||||||
$body_length = strlen($MTmessage['body']);
|
$body_length = strlen($MTmessage['body']);
|
||||||
$actual_length = $body_length + 32;
|
$actual_length = $body_length + 32;
|
||||||
if ($total_length && $total_length + $actual_length > 32760) {
|
if ($total_length && $total_length + $actual_length > 32760) {
|
||||||
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::NOTICE);
|
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$count++;
|
$count++;
|
||||||
|
@ -159,6 +159,9 @@ class FeedLoop extends ResumableSignalLoop
|
|||||||
case 'updateNewChannelMessage':
|
case 'updateNewChannelMessage':
|
||||||
case 'updateEditChannelMessage':
|
case 'updateEditChannelMessage':
|
||||||
$channelId = isset($update['message']['to_id']['channel_id']) ? $update['message']['to_id']['channel_id'] : false;
|
$channelId = isset($update['message']['to_id']['channel_id']) ? $update['message']['to_id']['channel_id'] : false;
|
||||||
|
if (!$channelId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'updateChannelWebPage':
|
case 'updateChannelWebPage':
|
||||||
case 'updateDeleteChannelMessages':
|
case 'updateDeleteChannelMessages':
|
||||||
|
@ -31,8 +31,6 @@ use danog\MadelineProto\MTProtoTools\UpdatesState;
|
|||||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
||||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||||
use danog\MadelineProto\TL\TLCallback;
|
use danog\MadelineProto\TL\TLCallback;
|
||||||
use function Amp\ByteStream\getStdin;
|
|
||||||
use function Amp\ByteStream\getInputBufferStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all of the mtproto stuff.
|
* Manages all of the mtproto stuff.
|
||||||
@ -136,6 +134,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
public $authorized = 0;
|
public $authorized = 0;
|
||||||
public $authorized_dc = -1;
|
public $authorized_dc = -1;
|
||||||
private $rsa_keys = [];
|
private $rsa_keys = [];
|
||||||
|
private $cdn_rsa_keys = [];
|
||||||
private $dh_config = ['version' => 0];
|
private $dh_config = ['version' => 0];
|
||||||
public $chats = [];
|
public $chats = [];
|
||||||
public $channel_participants = [];
|
public $channel_participants = [];
|
||||||
@ -164,21 +163,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
\danog\MadelineProto\Magic::class_exists();
|
\danog\MadelineProto\Magic::class_exists();
|
||||||
// Parse settings
|
// Parse settings
|
||||||
$this->parse_settings($settings);
|
$this->parse_settings($settings);
|
||||||
if (!defined('\\phpseclib\\Crypt\\Common\\SymmetricKey::MODE_IGE') || \phpseclib\Crypt\Common\SymmetricKey::MODE_IGE !== 7) {
|
|
||||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['phpseclib_fork']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('xml')) {
|
|
||||||
throw new Exception(['extension', 'xml']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('fileinfo')) {
|
|
||||||
throw new Exception(['extension', 'fileinfo']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('json')) {
|
|
||||||
throw new Exception(['extension', 'json']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('mbstring')) {
|
|
||||||
throw new Exception(['extension', 'mbstring']);
|
|
||||||
}
|
|
||||||
// Connect to servers
|
// Connect to servers
|
||||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE);
|
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['inst_dc'], Logger::ULTRA_VERBOSE);
|
||||||
if (!($this->channels_state instanceof CombinedUpdatesState)) {
|
if (!($this->channels_state instanceof CombinedUpdatesState)) {
|
||||||
@ -199,6 +183,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
}
|
}
|
||||||
// Load rsa keys
|
// Load rsa keys
|
||||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE);
|
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['load_rsa'], Logger::ULTRA_VERBOSE);
|
||||||
|
$this->rsa_keys = [];
|
||||||
foreach ($this->settings['authorization']['rsa_keys'] as $key) {
|
foreach ($this->settings['authorization']['rsa_keys'] as $key) {
|
||||||
$key = yield (new RSA())->load($key);
|
$key = yield (new RSA())->load($key);
|
||||||
$this->rsa_keys[$key->fp] = $key;
|
$this->rsa_keys[$key->fp] = $key;
|
||||||
@ -327,22 +312,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
if (isset($this->settings['app_info']['lang_code']) && isset(Lang::$lang[$this->settings['app_info']['lang_code']])) {
|
if (isset($this->settings['app_info']['lang_code']) && isset(Lang::$lang[$this->settings['app_info']['lang_code']])) {
|
||||||
Lang::$current_lang = &Lang::$lang[$this->settings['app_info']['lang_code']];
|
Lang::$current_lang = &Lang::$lang[$this->settings['app_info']['lang_code']];
|
||||||
}
|
}
|
||||||
if (!defined('\\phpseclib\\Crypt\\AES::MODE_IGE')) {
|
|
||||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['phpseclib_fork']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('xml')) {
|
|
||||||
throw new Exception(['extension', 'xml']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('fileinfo')) {
|
|
||||||
throw new Exception(['extension', 'fileinfo']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('mbstring')) {
|
|
||||||
throw new Exception(['extension', 'mbstring']);
|
|
||||||
}
|
|
||||||
if (!extension_loaded('json')) {
|
|
||||||
throw new Exception(['extension', 'json']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($this->referenceDatabase)) {
|
if (!isset($this->referenceDatabase)) {
|
||||||
$this->referenceDatabase = new ReferenceDatabase($this);
|
$this->referenceDatabase = new ReferenceDatabase($this);
|
||||||
}
|
}
|
||||||
@ -471,9 +440,9 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*if (!$this->settings['updates']['handle_old_updates']) {
|
/*if (!$this->settings['updates']['handle_old_updates']) {
|
||||||
$this->channels_state = new CombinedUpdatesState();
|
$this->channels_state = new CombinedUpdatesState();
|
||||||
$this->msg_ids = [];
|
$this->msg_ids = [];
|
||||||
$this->got_state = false;
|
$this->got_state = false;
|
||||||
}*/
|
}*/
|
||||||
yield $this->connect_to_all_dcs_async();
|
yield $this->connect_to_all_dcs_async();
|
||||||
foreach ($this->calls as $id => $controller) {
|
foreach ($this->calls as $id => $controller) {
|
||||||
@ -819,9 +788,11 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
], 'upload' => [
|
], 'upload' => [
|
||||||
'allow_automatic_upload' => true,
|
'allow_automatic_upload' => true,
|
||||||
'part_size' => 512 * 1024,
|
'part_size' => 512 * 1024,
|
||||||
|
'parallel_chunks' => 20,
|
||||||
], 'download' => [
|
], 'download' => [
|
||||||
'report_broken_media' => true,
|
'report_broken_media' => true,
|
||||||
'part_size' => 1024 * 1024,
|
'part_size' => 1024 * 1024,
|
||||||
|
'parallel_chunks' => 20,
|
||||||
], 'pwr' => [
|
], 'pwr' => [
|
||||||
'pwr' => false,
|
'pwr' => false,
|
||||||
// Need info ?
|
// Need info ?
|
||||||
@ -986,7 +957,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
$this->dialog_params = ['_' => 'MadelineProto.dialogParams', 'limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
|
$this->dialog_params = ['_' => 'MadelineProto.dialogParams', 'limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
|
||||||
$this->full_chats = [];
|
$this->full_chats = [];
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public function resetUpdateState()
|
public function resetUpdateState()
|
||||||
{
|
{
|
||||||
@ -1082,7 +1052,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
try {
|
try {
|
||||||
foreach ((yield $this->method_call_async_read('help.getCdnConfig', [], ['datacenter' => $datacenter]))['public_keys'] as $curkey) {
|
foreach ((yield $this->method_call_async_read('help.getCdnConfig', [], ['datacenter' => $datacenter]))['public_keys'] as $curkey) {
|
||||||
$tempkey = new \danog\MadelineProto\RSA($curkey['public_key']);
|
$tempkey = new \danog\MadelineProto\RSA($curkey['public_key']);
|
||||||
$this->rsa_keys[$tempkey->fp] = $tempkey;
|
$this->cdn_rsa_keys[$tempkey->fp] = $tempkey;
|
||||||
}
|
}
|
||||||
} catch (\danog\MadelineProto\TL\Exception $e) {
|
} catch (\danog\MadelineProto\TL\Exception $e) {
|
||||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::FATAL_ERROR);
|
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||||
|
@ -35,7 +35,8 @@ trait AuthKeyHandler
|
|||||||
|
|
||||||
public function create_auth_key_async($expires_in, $datacenter): \Generator
|
public function create_auth_key_async($expires_in, $datacenter): \Generator
|
||||||
{
|
{
|
||||||
$req_pq = strpos($datacenter, 'cdn') ? 'req_pq' : 'req_pq_multi';
|
$cdn = strpos($datacenter, 'cdn');
|
||||||
|
$req_pq = $cdn ? 'req_pq' : 'req_pq_multi';
|
||||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||||
try {
|
try {
|
||||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE);
|
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE);
|
||||||
@ -69,7 +70,7 @@ trait AuthKeyHandler
|
|||||||
* ***********************************************************************
|
* ***********************************************************************
|
||||||
* Find our key in the server_public_key_fingerprints vector
|
* Find our key in the server_public_key_fingerprints vector
|
||||||
*/
|
*/
|
||||||
foreach ($this->rsa_keys as $curkey) {
|
foreach ($cdn ? array_merge($this->cdn_rsa_keys, $this->rsa_keys) : $this->rsa_keys as $curkey) {
|
||||||
if (in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
|
if (in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
|
||||||
$key = $curkey;
|
$key = $curkey;
|
||||||
}
|
}
|
||||||
|
@ -19,62 +19,198 @@
|
|||||||
|
|
||||||
namespace danog\MadelineProto\MTProtoTools;
|
namespace danog\MadelineProto\MTProtoTools;
|
||||||
|
|
||||||
|
use Amp\ByteStream\InputStream;
|
||||||
|
use Amp\ByteStream\OutputStream;
|
||||||
|
use Amp\ByteStream\ResourceInputStream;
|
||||||
|
use Amp\ByteStream\ResourceOutputStream;
|
||||||
|
use Amp\ByteStream\StreamException;
|
||||||
|
use Amp\Deferred;
|
||||||
|
use Amp\File\Handle;
|
||||||
|
use Amp\File\StatCache;
|
||||||
|
use Amp\Promise;
|
||||||
|
use Amp\Success;
|
||||||
use danog\MadelineProto\Async\AsyncParameters;
|
use danog\MadelineProto\Async\AsyncParameters;
|
||||||
use danog\MadelineProto\Exception;
|
use danog\MadelineProto\Exception;
|
||||||
|
use danog\MadelineProto\FileCallbackInterface;
|
||||||
use danog\MadelineProto\Logger;
|
use danog\MadelineProto\Logger;
|
||||||
use danog\MadelineProto\RPCErrorException;
|
use danog\MadelineProto\RPCErrorException;
|
||||||
|
use danog\MadelineProto\Stream\Common\SimpleBufferedRawStream;
|
||||||
|
use danog\MadelineProto\Stream\ConnectionContext;
|
||||||
|
use danog\MadelineProto\Stream\Transport\PremadeStream;
|
||||||
|
use function Amp\File\exists;
|
||||||
|
use function Amp\File\open;
|
||||||
|
use function Amp\File\stat;
|
||||||
|
use function Amp\File\touch;
|
||||||
use danog\MadelineProto\Tools;
|
use danog\MadelineProto\Tools;
|
||||||
use function Amp\Promise\all;
|
use function Amp\Promise\all;
|
||||||
|
use Amp\File\BlockingHandle;
|
||||||
|
use Amp\Artax\Client;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages upload and download of files.
|
* Manages upload and download of files.
|
||||||
*/
|
*/
|
||||||
trait Files
|
trait Files
|
||||||
{
|
{
|
||||||
public function upload_async($file, $file_name = '', $cb = null, $encrypted = false): \Generator
|
public function upload_async($file, $file_name = '', $cb = null, $encrypted = false)
|
||||||
{
|
{
|
||||||
if (is_object($file)) {
|
if (is_object($file) && $file instanceof FileCallbackInterface) {
|
||||||
if (!isset(class_implements($file)['danog\MadelineProto\FileCallbackInterface'])) {
|
|
||||||
throw new \danog\MadelineProto\Exception('Provided object does not implement FileCallbackInterface');
|
|
||||||
}
|
|
||||||
$cb = $file;
|
$cb = $file;
|
||||||
$file = $file->getFile();
|
$file = $file->getFile();
|
||||||
}
|
}
|
||||||
|
if (is_string($file) || (is_object($file) && method_exists($file, '__toString'))) {
|
||||||
$t = microtime(true);
|
if (filter_var($file, FILTER_VALIDATE_URL)) {
|
||||||
|
return yield $this->upload_from_url_async($file);
|
||||||
|
}
|
||||||
|
} else if (is_array($file)) {
|
||||||
|
return yield $this->upload_from_tgfile_async($file, $cb, $encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
$file = \danog\MadelineProto\Absolute::absolute($file);
|
$file = \danog\MadelineProto\Absolute::absolute($file);
|
||||||
if (!file_exists($file)) {
|
if (!yield exists($file)) {
|
||||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['file_not_exist']);
|
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['file_not_exist']);
|
||||||
}
|
}
|
||||||
if (empty($file_name)) {
|
if (empty($file_name)) {
|
||||||
$file_name = basename($file);
|
$file_name = basename($file);
|
||||||
}
|
}
|
||||||
$datacenter = $this->settings['connection_settings']['default_dc'];
|
|
||||||
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
StatCache::clear($file);
|
||||||
$datacenter .= '_media';
|
|
||||||
}
|
$size = (yield stat($file))['size'];
|
||||||
$file_size = filesize($file);
|
if ($size > 512 * 1024 * 3000) {
|
||||||
if ($file_size > 512 * 1024 * 3000) {
|
|
||||||
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stream = yield open($file, 'rb');
|
||||||
|
$mime = $this->get_mime_from_file($file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return yield $this->upload_from_stream_async($stream, $size, $stream, $mime, $cb, $encrypted);
|
||||||
|
} finally {
|
||||||
|
yield $stream->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function upload_from_url_async($url, int $size = 0, string $file_name = '', $cb = null, bool $encrypted = false)
|
||||||
|
{
|
||||||
|
if (is_object($url) && $url instanceof FileCallbackInterface) {
|
||||||
|
$cb = $url;
|
||||||
|
$url = $url->getFile();
|
||||||
|
}
|
||||||
|
/** @var $response \Amp\Artax\Response */
|
||||||
|
$response = yield $this->datacenter->getHTTPClient()->request($url, [Client::OP_MAX_BODY_BYTES => 512 * 1024 * 3000, Client::OP_TRANSFER_TIMEOUT => 10*1000*3600]);
|
||||||
|
if (200 !== $status = $response->getStatus()) {
|
||||||
|
throw new Exception("Wrong status code: $status ".$response->getReason());
|
||||||
|
}
|
||||||
|
$mime = trim(explode(';', $response->getHeader('content-type') ?? 'application/octet-stream')[0]);
|
||||||
|
$size = $response->getHeader('content-length') ?? $size;
|
||||||
|
|
||||||
|
$stream = $response->getBody();
|
||||||
|
if (!$size) {
|
||||||
|
$this->logger->logger("No content length for $url, caching first");
|
||||||
|
|
||||||
|
$body = $stream;
|
||||||
|
$stream = new BlockingHandle(fopen('php://temp', 'r+b'), 'php://temp', 'r+b');
|
||||||
|
|
||||||
|
while (null !== $chunk = yield $body->read()) {
|
||||||
|
yield $stream->write($chunk);
|
||||||
|
}
|
||||||
|
$size = $stream->tell();
|
||||||
|
if (!$size) {
|
||||||
|
throw new Exception('Wrong size!');
|
||||||
|
}
|
||||||
|
yield $stream->seek(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return yield $this->upload_from_stream_async($stream, $size, $mime, $file_name, $cb, $encrypted);
|
||||||
|
}
|
||||||
|
public function upload_from_stream_async($stream, int $size, string $mime, string $file_name = '', $cb = null, bool $encrypted = false)
|
||||||
|
{
|
||||||
|
if (is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||||
|
$cb = $stream;
|
||||||
|
$stream = $stream->getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var $stream \Amp\ByteStream\OutputStream */
|
||||||
|
if (!is_object($stream)) {
|
||||||
|
$stream = new ResourceOutputStream($stream);
|
||||||
|
}
|
||||||
|
if (!$stream instanceof InputStream) {
|
||||||
|
throw new Exception("Invalid stream provided");
|
||||||
|
}
|
||||||
|
$seekable = false;
|
||||||
|
if (method_exists($stream, 'seek')) {
|
||||||
|
try {
|
||||||
|
yield $stream->seek(0);
|
||||||
|
$seekable = true;
|
||||||
|
} catch (StreamException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$created = false;
|
||||||
|
|
||||||
|
if ($stream instanceof Handle) {
|
||||||
|
$callable = static function (int $offset, int $size) use ($stream, $seekable) {
|
||||||
|
if ($seekable) {
|
||||||
|
while ($stream->tell() !== $offset) {
|
||||||
|
yield $stream->seek($offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yield $stream->read($size);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
if (!$stream instanceof BufferedRawStream) {
|
||||||
|
$ctx = (new ConnectionContext)
|
||||||
|
->addStream(PremadeStream::getName(), $stream)
|
||||||
|
->addStream(SimpleBufferedRawStream::getName());
|
||||||
|
$stream = yield $ctx->getStream();
|
||||||
|
$created = true;
|
||||||
|
}
|
||||||
|
$callable = static function (int $offset, int $size) use ($stream) {
|
||||||
|
$reader = yield $stream->getReadBuffer($l);
|
||||||
|
try {
|
||||||
|
return yield $reader->bufferRead($size);
|
||||||
|
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||||
|
$reader = yield $stream->getReadBuffer($size);
|
||||||
|
return yield $reader->bufferRead($size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$seekable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = yield $this->upload_from_callable_async($callable, $size, $mime, $file_name, $cb, $seekable, $encrypted);
|
||||||
|
if ($created) {
|
||||||
|
$stream->disconnect();
|
||||||
|
}
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
public function upload_from_callable_async($callable, int $size, string $mime, string $file_name = '', $cb = null, bool $refetchable = true, bool $encrypted = false)
|
||||||
|
{
|
||||||
|
if (is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||||
|
$cb = $callable;
|
||||||
|
$callable = $callable->getFile();
|
||||||
|
}
|
||||||
|
if (!is_callable($callable)) {
|
||||||
|
throw new Exception('Invalid callable provided');
|
||||||
|
}
|
||||||
if ($cb === null) {
|
if ($cb === null) {
|
||||||
$cb = function ($percent) {
|
$cb = function ($percent) {
|
||||||
$this->logger->logger('Upload status: '.$percent.'%', \danog\MadelineProto\Logger::NOTICE);
|
$this->logger->logger('Upload status: '.$percent.'%', \danog\MadelineProto\Logger::NOTICE);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
$part_size = $this->settings['upload']['part_size'];
|
|
||||||
$part_total_num = (int) ceil($file_size / $part_size);
|
|
||||||
$part_num = 0;
|
|
||||||
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
|
||||||
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
|
|
||||||
$file_id = $this->random(8);
|
|
||||||
$f = fopen($file, 'r');
|
|
||||||
|
|
||||||
$seekable = stream_get_meta_data($f)['seekable'];
|
$datacenter = $this->settings['connection_settings']['default_dc'];
|
||||||
if ($seekable) {
|
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||||
fseek($f, 0);
|
$datacenter .= '_media';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$part_size = $this->settings['upload']['part_size'];
|
||||||
|
$parallel_chunks = $this->settings['upload']['parallel_chunks'] ? $this->settings['upload']['parallel_chunks'] : 3000;
|
||||||
|
|
||||||
|
$part_total_num = (int) ceil($size / $part_size);
|
||||||
|
$part_num = 0;
|
||||||
|
$method = $size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
||||||
|
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
|
||||||
|
$file_id = $this->random(8);
|
||||||
|
|
||||||
$ige = null;
|
$ige = null;
|
||||||
if ($encrypted === true) {
|
if ($encrypted === true) {
|
||||||
$key = $this->random(32);
|
$key = $this->random(32);
|
||||||
@ -85,39 +221,65 @@ trait Files
|
|||||||
$ige->setIV($iv);
|
$ige->setIV($iv);
|
||||||
$ige->setKey($key);
|
$ige->setKey($key);
|
||||||
$ige->enableContinuousBuffer();
|
$ige->enableContinuousBuffer();
|
||||||
|
$refetchable = false;
|
||||||
}
|
}
|
||||||
$ctx = hash_init('md5');
|
$ctx = hash_init('md5');
|
||||||
$promises = [];
|
$promises = [];
|
||||||
$cur_part_num = 0;
|
|
||||||
|
|
||||||
|
$cb = function () use ($cb, $part_total_num) {
|
||||||
|
static $cur = 0;
|
||||||
|
$cur++;
|
||||||
|
$this->callFork($cb($cur * 100 / $part_total_num));
|
||||||
|
};
|
||||||
|
|
||||||
|
$start = microtime(true);
|
||||||
while ($part_num < $part_total_num) {
|
while ($part_num < $part_total_num) {
|
||||||
$t = microtime(true);
|
|
||||||
$read_deferred = yield $this->method_call_async_write(
|
$read_deferred = yield $this->method_call_async_write(
|
||||||
$method,
|
$method,
|
||||||
new AsyncParameters(
|
new AsyncParameters(
|
||||||
static function () use ($file_id, $part_num, $part_total_num, $part_size, $f, $ctx, $ige, $seekable) {
|
static function () use ($file_id, $part_num, $part_total_num, $part_size, $callable, $ctx, $ige) {
|
||||||
if ($seekable) {
|
static $fetched = false;
|
||||||
fseek($f, $part_num * $part_size);
|
$already_fetched = $fetched;
|
||||||
} elseif (ftell($f) !== $part_num * $part_size) {
|
$fetched = true;
|
||||||
throw new \danog\MadelineProto\Exception('Wrong position!');
|
|
||||||
}
|
|
||||||
|
|
||||||
$bytes = stream_get_contents($f, $part_size);
|
$bytes = yield $callable($part_num * $part_size, $part_size);
|
||||||
|
|
||||||
|
if (!$already_fetched) {
|
||||||
|
hash_update($ctx, $bytes);
|
||||||
|
}
|
||||||
if ($ige) {
|
if ($ige) {
|
||||||
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
||||||
}
|
}
|
||||||
hash_update($ctx, $bytes);
|
|
||||||
|
|
||||||
return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
|
return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
|
||||||
},
|
},
|
||||||
$seekable
|
$refetchable
|
||||||
),
|
),
|
||||||
['heavy' => true, 'file' => true, 'datacenter' => $datacenter]
|
['heavy' => true, 'file' => true, 'datacenter' => &$datacenter]
|
||||||
);
|
);
|
||||||
$this->callFork($cb(ftell($f) * 100 / $file_size));
|
$read_deferred->promise()->onResolve(static function ($e, $res) use ($cb) {
|
||||||
$this->logger->logger('Speed for chunk: '.(($part_size * 8 / 1000000) / (microtime(true) - $t)));
|
if ($res) {
|
||||||
|
$cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$part_num++;
|
$part_num++;
|
||||||
$promises[] = $read_deferred->promise();
|
$promises[] = $read_deferred->promise();
|
||||||
|
|
||||||
|
if (!($part_num % $parallel_chunks)) { // 20 mb at a time, for a typical bandwidth of 1gbps (run the code in this if every second)
|
||||||
|
$result = yield $this->all($promises);
|
||||||
|
foreach ($result as $kkey => $result) {
|
||||||
|
if (!$result) {
|
||||||
|
throw new \danog\MadelineProto\Exception('Upload of part '.$kkey.' failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$promises = [];
|
||||||
|
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||||
|
$this->logger->logger("Partial upload time: $time");
|
||||||
|
$this->logger->logger("Partial upload speed: $speed mbps");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = yield all($promises);
|
$result = yield all($promises);
|
||||||
@ -126,18 +288,18 @@ trait Files
|
|||||||
throw new \danog\MadelineProto\Exception('Upload of part '.$kkey.' failed');
|
throw new \danog\MadelineProto\Exception('Upload of part '.$kkey.' failed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||||
|
$this->logger->logger("Total upload time: $time");
|
||||||
|
$this->logger->logger("Total upload speed: $speed mbps");
|
||||||
|
|
||||||
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'mime_type' => $this->get_mime_from_file($file)];
|
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'mime_type' => $mime];
|
||||||
if ($encrypted === true) {
|
if ($encrypted === true) {
|
||||||
$constructor['key_fingerprint'] = $fingerprint;
|
$constructor['key_fingerprint'] = $fingerprint;
|
||||||
$constructor['key'] = $key;
|
$constructor['key'] = $key;
|
||||||
$constructor['iv'] = $iv;
|
$constructor['iv'] = $iv;
|
||||||
}
|
}
|
||||||
|
$constructor['md5_checksum'] = hash_final($ctx);
|
||||||
fclose($f);
|
|
||||||
clearstatcache();
|
|
||||||
|
|
||||||
$this->logger->logger('Speed: '.(($file_size * 8) / (microtime(true) - $t) / 1000000));
|
|
||||||
|
|
||||||
return $constructor;
|
return $constructor;
|
||||||
}
|
}
|
||||||
@ -147,7 +309,80 @@ trait Files
|
|||||||
return $this->upload_async($file, $file_name, $cb, true);
|
return $this->upload_async($file, $file_name, $cb, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function gen_all_file_async($media, $regenerate)
|
public function upload_from_tgfile_async($media, $cb = null, $encrypted = false)
|
||||||
|
{
|
||||||
|
if (is_object($media) && $media instanceof FileCallbackInterface) {
|
||||||
|
$cb = $media;
|
||||||
|
$media = $media->getFile();
|
||||||
|
}
|
||||||
|
$media = yield $this->get_download_info_async($media);
|
||||||
|
if (!isset($media['size'], $media['mime'])) {
|
||||||
|
throw new Exception('Wrong file provided!');
|
||||||
|
}
|
||||||
|
$size = $media['size'];
|
||||||
|
$mime = $media['mime'];
|
||||||
|
|
||||||
|
$chunk_size = $this->settings['upload']['part_size'];
|
||||||
|
|
||||||
|
$bridge = new class
|
||||||
|
|
||||||
|
{
|
||||||
|
private $done = [];
|
||||||
|
private $pending = [];
|
||||||
|
public $nextRead;
|
||||||
|
public $size;
|
||||||
|
public $part_size;
|
||||||
|
|
||||||
|
public function read(int $offset, int $size)
|
||||||
|
{
|
||||||
|
$nextRead = $this->nextRead;
|
||||||
|
$this->nextRead = new Deferred;
|
||||||
|
|
||||||
|
if ($nextRead) {
|
||||||
|
$nextRead->resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->done[$offset])) {
|
||||||
|
if (strlen($this->done[$offset]) > $size) {
|
||||||
|
throw new Exception('Wrong size!');
|
||||||
|
}
|
||||||
|
$result = $this->done[$offset];
|
||||||
|
unset($this->done[$offset]);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
$this->pending[$offset] = new Deferred;
|
||||||
|
return $this->pending[$offset]->promise();
|
||||||
|
}
|
||||||
|
public function write(string $data, int $offset)
|
||||||
|
{
|
||||||
|
if (isset($this->pending[$offset])) {
|
||||||
|
$promise = $this->pending[$offset];
|
||||||
|
unset($this->pending[$offset]);
|
||||||
|
$promise->resolve($data);
|
||||||
|
} else {
|
||||||
|
$this->done[$offset] = $data;
|
||||||
|
}
|
||||||
|
$length = strlen($data);
|
||||||
|
if ($offset + $length === $this->size || $length < $this->part_size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return $this->nextRead->promise();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$bridge->size = $size;
|
||||||
|
$bridge->part_size = $chunk_size;
|
||||||
|
$reader = [$bridge, 'read'];
|
||||||
|
$writer = [$bridge, 'write'];
|
||||||
|
|
||||||
|
$read = $this->upload_from_callable_async($reader, $size, $mime, '', $cb, false, $encrypted);
|
||||||
|
$write = $this->download_to_callable_async($media, $writer, null, true, 0, -1, $chunk_size);
|
||||||
|
|
||||||
|
list($res) = yield $this->all([$read, $write]);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gen_all_file_async($media)
|
||||||
{
|
{
|
||||||
$res = [$this->constructors->find_by_predicate($media['_'])['type'] => $media];
|
$res = [$this->constructors->find_by_predicate($media['_'])['type'] => $media];
|
||||||
switch ($media['_']) {
|
switch ($media['_']) {
|
||||||
@ -240,7 +475,7 @@ trait Files
|
|||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_file_info_async($constructor, $regenerate = false)
|
public function get_file_info_async($constructor)
|
||||||
{
|
{
|
||||||
if (is_string($constructor)) {
|
if (is_string($constructor)) {
|
||||||
$constructor = $this->unpack_file_id($constructor)['MessageMedia'];
|
$constructor = $this->unpack_file_id($constructor)['MessageMedia'];
|
||||||
@ -256,7 +491,7 @@ trait Files
|
|||||||
$constructor = $constructor['media'];
|
$constructor = $constructor['media'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return yield $this->gen_all_file_async($constructor, $regenerate);
|
return yield $this->gen_all_file_async($constructor);
|
||||||
}
|
}
|
||||||
public function get_propic_info_async($data)
|
public function get_propic_info_async($data)
|
||||||
{
|
{
|
||||||
@ -325,13 +560,13 @@ trait Files
|
|||||||
$res['name'] .= ' - '.$audio['performer'];
|
$res['name'] .= ' - '.$audio['performer'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isset($res['ext'])) {
|
if (!isset($res['ext']) || $res['ext'] === '') {
|
||||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime(isset($res['mime']) ? $res['mime'] : 'image/jpeg'));
|
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($res['mime'] ?? 'image/jpeg'));
|
||||||
}
|
}
|
||||||
if (!isset($res['mime'])) {
|
if (!isset($res['mime']) || $res['mime'] === '') {
|
||||||
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
|
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
|
||||||
}
|
}
|
||||||
if (!isset($res['name'])) {
|
if (!isset($res['name']) || $res['name'] === '') {
|
||||||
$res['name'] = Tools::unpack_signed_long_string($message_media['file']['access_hash']);
|
$res['name'] = Tools::unpack_signed_long_string($message_media['file']['access_hash']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,10 +727,10 @@ trait Files
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!isset($res['ext'])) {
|
if (!isset($res['ext']) || $res['ext'] === '') {
|
||||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
|
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
|
||||||
}
|
}
|
||||||
if (!isset($res['name'])) {
|
if (!isset($res['name']) || $res['name'] === '') {
|
||||||
$res['name'] = Tools::unpack_signed_long_string($message_media['document']['access_hash']);
|
$res['name'] = Tools::unpack_signed_long_string($message_media['document']['access_hash']);
|
||||||
}
|
}
|
||||||
if (isset($message_media['document']['size'])) {
|
if (isset($message_media['document']['size'])) {
|
||||||
@ -509,10 +744,105 @@ trait Files
|
|||||||
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
|
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
public function download_to_browser_single_async($message_media, $cb = null)
|
||||||
|
{
|
||||||
|
if (php_sapi_name() === 'cli') {
|
||||||
|
throw new Exception('Cannot download file to browser from command line: start this script from a browser');
|
||||||
|
}
|
||||||
|
if (headers_sent()) {
|
||||||
|
throw new Exception('Headers already sent, cannot stream file to browser!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_object($message_media) && $message_media instanceof FileCallbackInterface) {
|
||||||
|
$cb = $message_media;
|
||||||
|
$message_media = $message_media->getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
$message_media = yield $this->get_download_info_async($message_media);
|
||||||
|
|
||||||
|
$servefile = $_SERVER['REQUEST_METHOD'] !== 'HEAD';
|
||||||
|
|
||||||
|
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||||
|
$range = explode('=', $_SERVER['HTTP_RANGE'], 2);
|
||||||
|
if (count($range) == 1) {
|
||||||
|
$range[1] = '';
|
||||||
|
}
|
||||||
|
list($size_unit, $range_orig) = $range;
|
||||||
|
if ($size_unit == 'bytes') {
|
||||||
|
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
|
||||||
|
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
|
||||||
|
$list = explode(',', $range_orig, 2);
|
||||||
|
if (count($list) == 1) {
|
||||||
|
$list[1] = '';
|
||||||
|
}
|
||||||
|
list($range, $extra_ranges) = $list;
|
||||||
|
} else {
|
||||||
|
$range = '';
|
||||||
|
return Tools::noCache(416, '<html><body><h1>416 Requested Range Not Satisfiable.</h1><br><p>Could not use selected range.</p></body></html>');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$range = '';
|
||||||
|
}
|
||||||
|
$listseek = explode('-', $range, 2);
|
||||||
|
if (count($listseek) == 1) {
|
||||||
|
$listseek[1] = '';
|
||||||
|
}
|
||||||
|
list($seek_start, $seek_end) = $listseek;
|
||||||
|
|
||||||
|
$seek_end = empty($seek_end) ? ($message_media['size'] - 1) : min(abs(intval($seek_end)), $message_media['size'] - 1);
|
||||||
|
|
||||||
|
if (!empty($seek_start) && $seek_end < abs(intval($seek_start))) {
|
||||||
|
return Tools::noCache(416, '<html><body><h1>416 Requested Range Not Satisfiable.</h1><br><p>Could not use selected range.</p></body></html>');
|
||||||
|
}
|
||||||
|
$seek_start = empty($seek_start) ? 0 : abs(intval($seek_start));
|
||||||
|
if ($servefile) {
|
||||||
|
if ($seek_start > 0 || $seek_end < $select['file_size'] - 1) {
|
||||||
|
header('HTTP/1.1 206 Partial Content');
|
||||||
|
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$select['file_size']);
|
||||||
|
header('Content-Length: '.($seek_end - $seek_start + 1));
|
||||||
|
} else {
|
||||||
|
header('Content-Length: '.$select['file_size']);
|
||||||
|
}
|
||||||
|
header('Content-Type: '.$select['mime']);
|
||||||
|
header('Cache-Control: max-age=31556926;');
|
||||||
|
header('Content-Transfer-Encoding: Binary');
|
||||||
|
header('Accept-Ranges: bytes');
|
||||||
|
//header('Content-disposition: attachment: filename="'.basename($select['file_path']).'"');
|
||||||
|
$MadelineProto->download_to_stream($select['file_id'], fopen('php://output', 'w'), function ($percent) {
|
||||||
|
flush();
|
||||||
|
ob_flush();
|
||||||
|
\danog\MadelineProto\Logger::log('Download status: '.$percent.'%');
|
||||||
|
}, $seek_start, $seek_end + 1);
|
||||||
|
//analytics(true, $file_path, $MadelineProto->get_self()['id'], $dbuser, $dbpassword);
|
||||||
|
$MadelineProto->API->getting_state = false;
|
||||||
|
$MadelineProto->API->store_db([], true);
|
||||||
|
$MadelineProto->API->reset_session();
|
||||||
|
} else {
|
||||||
|
if ($seek_start > 0 || $seek_end < $select['file_size'] - 1) {
|
||||||
|
header('HTTP/1.1 206 Partial Content');
|
||||||
|
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$select['file_size']);
|
||||||
|
header('Content-Length: '.($seek_end - $seek_start + 1));
|
||||||
|
} else {
|
||||||
|
header('Content-Length: '.$select['file_size']);
|
||||||
|
}
|
||||||
|
header('Content-Type: '.$select['mime']);
|
||||||
|
header('Cache-Control: max-age=31556926;');
|
||||||
|
header('Content-Transfer-Encoding: Binary');
|
||||||
|
header('Accept-Ranges: bytes');
|
||||||
|
analytics(true, $file_path, null, $dbuser, $dbpassword);
|
||||||
|
//header('Content-disposition: attachment: filename="'.basename($select['file_path']).'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Length: '.$info['size']);
|
||||||
|
header('Content-Type: '.$info['mime']);
|
||||||
|
}*/
|
||||||
|
public function extract_photosize($photo)
|
||||||
|
{
|
||||||
|
}
|
||||||
public function download_to_dir_async($message_media, $dir, $cb = null)
|
public function download_to_dir_async($message_media, $dir, $cb = null)
|
||||||
{
|
{
|
||||||
if (is_object($dir) && class_implements($dir)['danog\MadelineProto\FileCallbackInterface']) {
|
if (is_object($dir) && $dir instanceof FileCallbackInterface) {
|
||||||
$cb = $dir;
|
$cb = $dir;
|
||||||
$dir = $dir->getFile();
|
$dir = $dir->getFile();
|
||||||
}
|
}
|
||||||
@ -524,69 +854,100 @@ trait Files
|
|||||||
|
|
||||||
public function download_to_file_async($message_media, $file, $cb = null)
|
public function download_to_file_async($message_media, $file, $cb = null)
|
||||||
{
|
{
|
||||||
if (is_object($file) && class_implements($file)['danog\MadelineProto\FileCallbackInterface']) {
|
if (is_object($file) && $file instanceof FileCallbackInterface) {
|
||||||
$cb = $file;
|
$cb = $file;
|
||||||
$file = $file->getFile();
|
$file = $file->getFile();
|
||||||
}
|
}
|
||||||
$file = \danog\MadelineProto\Absolute::absolute(preg_replace('|/+|', '/', $file));
|
$file = \danog\MadelineProto\Absolute::absolute(preg_replace('|/+|', '/', $file));
|
||||||
if (!file_exists($file)) {
|
if (!yield exists($file)) {
|
||||||
touch($file);
|
yield touch($file);
|
||||||
}
|
}
|
||||||
$file = realpath($file);
|
$file = realpath($file);
|
||||||
$message_media = yield $this->get_download_info_async($message_media);
|
$message_media = yield $this->get_download_info_async($message_media);
|
||||||
$stream = fopen($file, 'r+b');
|
|
||||||
$size = fstat($stream)['size'];
|
StatCache::clear($file);
|
||||||
|
|
||||||
|
$size = (yield stat($file))['size'];
|
||||||
|
$stream = yield open($file, 'cb');
|
||||||
|
|
||||||
$this->logger->logger('Waiting for lock of file to download...');
|
$this->logger->logger('Waiting for lock of file to download...');
|
||||||
do {
|
$unlock = yield $this->flock($file, LOCK_EX);
|
||||||
$res = flock($stream, LOCK_EX | LOCK_NB);
|
|
||||||
if (!$res) {
|
|
||||||
yield $this->sleep(0.1);
|
|
||||||
}
|
|
||||||
} while (!$res);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
yield $this->download_to_stream_async($message_media, $stream, $cb, $size, -1);
|
yield $this->download_to_stream_async($message_media, $stream, $cb, $size, -1);
|
||||||
} finally {
|
} finally {
|
||||||
flock($stream, LOCK_UN);
|
$unlock();
|
||||||
fclose($stream);
|
yield $stream->close();
|
||||||
clearstatcache();
|
StatCache::clear($file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $file;
|
return $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function download_to_stream_async($message_media, $stream, $cb = null, $offset = 0, $end = -1)
|
public function download_to_stream_async($message_media, $stream, $cb = null, $offset = 0, $end = -1)
|
||||||
{
|
{
|
||||||
if (is_object($stream) && class_implements($stream)['danog\MadelineProto\FileCallbackInterface']) {
|
$message_media = yield $this->get_download_info_async($message_media);
|
||||||
|
|
||||||
|
if (is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||||
$cb = $stream;
|
$cb = $stream;
|
||||||
$stream = $stream->getFile();
|
$stream = $stream->getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var $stream \Amp\ByteStream\OutputStream */
|
||||||
|
if (!is_object($stream)) {
|
||||||
|
$stream = new ResourceOutputStream($stream);
|
||||||
|
}
|
||||||
|
if (!$stream instanceof OutputStream) {
|
||||||
|
throw new Exception("Invalid stream provided");
|
||||||
|
}
|
||||||
|
$seekable = false;
|
||||||
|
if (method_exists($stream, 'seek')) {
|
||||||
|
try {
|
||||||
|
yield $stream->seek($offset);
|
||||||
|
$seekable = true;
|
||||||
|
} catch (StreamException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$callable = static function (string $payload, int $offset) use ($stream, $seekable) {
|
||||||
|
if ($seekable) {
|
||||||
|
while ($stream->tell() !== $offset) {
|
||||||
|
yield $stream->seek($offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return yield $stream->write($payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
return yield $this->download_to_callable_async($message_media, $callable, $cb, $seekable, $offset, $end);
|
||||||
|
}
|
||||||
|
public function download_to_callable_async($message_media, $callable, $cb = null, $parallelize = true, $offset = 0, $end = -1, int $part_size = null)
|
||||||
|
{
|
||||||
|
$message_media = yield $this->get_download_info_async($message_media);
|
||||||
|
|
||||||
|
if (is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||||
|
$cb = $callable;
|
||||||
|
$callable = $callable->getFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_callable($callable)) {
|
||||||
|
throw new Exception('Wrong callable provided');
|
||||||
|
}
|
||||||
if ($cb === null) {
|
if ($cb === null) {
|
||||||
$cb = function ($percent) {
|
$cb = function ($percent) {
|
||||||
$this->logger->logger('Download status: '.$percent.'%', \danog\MadelineProto\Logger::NOTICE);
|
$this->logger->logger('Download status: '.$percent.'%', \danog\MadelineProto\Logger::NOTICE);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
$message_media = yield $this->get_download_info_async($message_media);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (stream_get_meta_data($stream)['seekable']) {
|
|
||||||
fseek($stream, $offset);
|
|
||||||
}
|
|
||||||
} catch (\danog\MadelineProto\Exception $e) {
|
|
||||||
}
|
|
||||||
$downloaded_size = 0;
|
|
||||||
if ($end === -1 && isset($message_media['size'])) {
|
if ($end === -1 && isset($message_media['size'])) {
|
||||||
$end = $message_media['size'];
|
$end = $message_media['size'];
|
||||||
}
|
}
|
||||||
$size = $end - $offset;
|
|
||||||
$part_size = $this->settings['download']['part_size'];
|
$part_size = $part_size ?? $this->settings['download']['part_size'];
|
||||||
$percent = 0;
|
$parallel_chunks = $this->settings['download']['parallel_chunks'] ? $this->settings['download']['parallel_chunks'] : 3000;
|
||||||
|
|
||||||
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->settings['connection_settings']['default_dc'];
|
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->settings['connection_settings']['default_dc'];
|
||||||
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||||
$datacenter .= '_media';
|
$datacenter .= '_media';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($message_media['key'])) {
|
if (isset($message_media['key'])) {
|
||||||
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
|
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
|
||||||
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
|
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
|
||||||
@ -597,43 +958,126 @@ trait Files
|
|||||||
$ige->setIV($message_media['iv']);
|
$ige->setIV($message_media['iv']);
|
||||||
$ige->setKey($message_media['key']);
|
$ige->setKey($message_media['key']);
|
||||||
$ige->enableContinuousBuffer();
|
$ige->enableContinuousBuffer();
|
||||||
|
$parallelize = false;
|
||||||
}
|
}
|
||||||
$theend = false;
|
|
||||||
|
if ($offset === $end) {
|
||||||
|
$cb(100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$params = [];
|
||||||
|
$start_at = $offset % $part_size;
|
||||||
|
$probable_end = $end !== -1 ? $end : 512 * 1024 * 3000;
|
||||||
|
|
||||||
|
$breakOut = false;
|
||||||
|
for ($x = $offset - $start_at; $x < $probable_end; $x += $part_size) {
|
||||||
|
$end_at = $part_size;
|
||||||
|
|
||||||
|
if ($end !== -1 && $x + $part_size > $end) {
|
||||||
|
$end_at = $end % $part_size;
|
||||||
|
$breakOut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[] = [
|
||||||
|
'offset' => $x,
|
||||||
|
'limit' => $part_size,
|
||||||
|
'part_start_at' => $start_at,
|
||||||
|
'part_end_at' => $end_at,
|
||||||
|
];
|
||||||
|
|
||||||
|
$start_at = 0;
|
||||||
|
if ($breakOut) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$params) {
|
||||||
|
$cb(100);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$count = count($params);
|
||||||
|
|
||||||
|
$cb = function () use ($cb, $count) {
|
||||||
|
static $cur = 0;
|
||||||
|
$cur++;
|
||||||
|
$this->callFork($cb($cur * 100 / $count));
|
||||||
|
};
|
||||||
|
|
||||||
$cdn = false;
|
$cdn = false;
|
||||||
|
|
||||||
while (true) {
|
$params[0]['previous_promise'] = new Success(true);
|
||||||
if ($start_at = $offset % $part_size) {
|
|
||||||
$offset -= $start_at;
|
$start = microtime(true);
|
||||||
|
$size = yield $this->download_part($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, array_shift($params), $callable, $parallelize);
|
||||||
|
|
||||||
|
if ($params) {
|
||||||
|
$previous_promise = new Success(true);
|
||||||
|
|
||||||
|
$promises = [];
|
||||||
|
foreach ($params as $key => $param) {
|
||||||
|
$param['previous_promise'] = $previous_promise;
|
||||||
|
$previous_promise = $this->call($this->download_part($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, $param, $callable, $parallelize));
|
||||||
|
$previous_promise->onResolve(static function ($e, $res) use (&$size) {
|
||||||
|
if ($res) {
|
||||||
|
$size += $res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$promises[] = $previous_promise;
|
||||||
|
|
||||||
|
if (!($key % $parallel_chunks)) { // 20 mb at a time, for a typical bandwidth of 1gbps
|
||||||
|
yield $this->all($promises);
|
||||||
|
$promises = [];
|
||||||
|
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||||
|
$this->logger->logger("Partial download time: $time");
|
||||||
|
$this->logger->logger("Partial download speed: $speed mbps");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($promises) {
|
||||||
|
yield $this->all($promises);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$time = microtime(true) - $start;
|
||||||
|
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||||
|
$this->logger->logger("Total download time: $time");
|
||||||
|
$this->logger->logger("Total download speed: $speed mbps");
|
||||||
|
|
||||||
|
if ($cdn) {
|
||||||
|
$this->clear_cdn_hashes($message_media['file_token']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function download_part(&$message_media, &$cdn, &$datacenter, &$old_dc, &$ige, $cb, $offset, $callable, $seekable, $postpone = false)
|
||||||
|
{
|
||||||
|
static $method = [
|
||||||
|
false => 'upload.getFile', // non-cdn
|
||||||
|
true => 'upload.getCdnFile', // cdn
|
||||||
|
];
|
||||||
|
do {
|
||||||
|
if (!$cdn) {
|
||||||
|
$basic_param = [
|
||||||
|
'location' => $message_media['InputFileLocation'],
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$basic_param = [
|
||||||
|
'file_token' => $message_media['file_token'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$res = $cdn ?
|
$res = yield $this->method_call_async_read(
|
||||||
yield $this->method_call_async_read(
|
$method[$cdn],
|
||||||
'upload.getCdnFile',
|
$basic_param + $offset,
|
||||||
[
|
|
||||||
'file_token' => $message_media['file_token'],
|
|
||||||
'offset' => $offset,
|
|
||||||
'limit' => $part_size,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'heavy' => true,
|
|
||||||
'file' => true,
|
|
||||||
'FloodWaitLimit' => 0,
|
|
||||||
'datacenter' => $datacenter,
|
|
||||||
]
|
|
||||||
) :
|
|
||||||
yield $this->method_call_async_read(
|
|
||||||
'upload.getFile',
|
|
||||||
[
|
|
||||||
'location' => $message_media['InputFileLocation'],
|
|
||||||
'offset' => $offset,
|
|
||||||
'limit' => $part_size,
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'heavy' => true,
|
'heavy' => true,
|
||||||
'file' => true,
|
'file' => true,
|
||||||
'FloodWaitLimit' => 0,
|
'FloodWaitLimit' => 0,
|
||||||
'datacenter' => &$datacenter,
|
'datacenter' => &$datacenter,
|
||||||
|
'postpone' => $postpone,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||||
@ -658,6 +1102,7 @@ trait Files
|
|||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($res['_'] === 'upload.fileCdnRedirect') {
|
if ($res['_'] === 'upload.fileCdnRedirect') {
|
||||||
$cdn = true;
|
$cdn = true;
|
||||||
$message_media['file_token'] = $res['file_token'];
|
$message_media['file_token'] = $res['file_token'];
|
||||||
@ -670,9 +1115,7 @@ trait Files
|
|||||||
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
||||||
}
|
}
|
||||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['stored_on_cdn'], \danog\MadelineProto\Logger::NOTICE);
|
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['stored_on_cdn'], \danog\MadelineProto\Logger::NOTICE);
|
||||||
continue;
|
} else if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
|
||||||
}
|
|
||||||
if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
|
|
||||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['cdn_reupload'], \danog\MadelineProto\Logger::NOTICE);
|
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['cdn_reupload'], \danog\MadelineProto\Logger::NOTICE);
|
||||||
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
||||||
|
|
||||||
@ -691,51 +1134,35 @@ trait Files
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
|
if ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
|
||||||
$datacenter = 1;
|
$datacenter = 0;
|
||||||
}
|
}
|
||||||
while ($cdn === false && $res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
|
while ($cdn === false &&
|
||||||
$res = yield $this->method_call_async_read('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]);
|
$res['type']['_'] === 'storage.fileUnknown' &&
|
||||||
$datacenter++;
|
$res['bytes'] === '' &&
|
||||||
if (!isset($this->datacenter->sockets[$datacenter])) {
|
isset($this->datacenter->sockets[++$datacenter])
|
||||||
break;
|
) {
|
||||||
}
|
$res = yield $this->method_call_async_read('upload.getFile', $basic_param + $offset, ['heavy' => true, 'file' => true, 'FloodWaitLimit' => 0, 'datacenter' => $datacenter]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($message_media['cdn_key'])) {
|
if (isset($message_media['cdn_key'])) {
|
||||||
$ivec = substr($message_media['cdn_iv'], 0, 12).pack('N', $offset >> 4);
|
$ivec = substr($message_media['cdn_iv'], 0, 12).pack('N', $offset['offset'] >> 4);
|
||||||
$res['bytes'] = $this->ctr_encrypt($res['bytes'], $message_media['cdn_key'], $ivec);
|
$res['bytes'] = $this->ctr_encrypt($res['bytes'], $message_media['cdn_key'], $ivec);
|
||||||
$this->check_cdn_hash($message_media['file_token'], $offset, $res['bytes'], $old_dc);
|
$this->check_cdn_hash($message_media['file_token'], $offset['offset'], $res['bytes'], $old_dc);
|
||||||
}
|
}
|
||||||
if (isset($message_media['key'])) {
|
if (isset($message_media['key'])) {
|
||||||
$res['bytes'] = $ige->decrypt($res['bytes']);
|
$res['bytes'] = $ige->decrypt($res['bytes']);
|
||||||
}
|
}
|
||||||
if ($start_at) {
|
if ($offset['part_start_at'] || $offset['part_end_at'] !== $offset['limit']) {
|
||||||
$res['bytes'] = substr($res['bytes'], $start_at);
|
$res['bytes'] = substr($res['bytes'], $offset['part_start_at'], $offset['part_end_at'] - $offset['part_start_at']);
|
||||||
}
|
}
|
||||||
if ($end !== -1 && strlen($res['bytes']) + $downloaded_size >= $size) {
|
|
||||||
$res['bytes'] = substr($res['bytes'], 0, $size - $downloaded_size);
|
|
||||||
$theend = true;
|
|
||||||
}
|
|
||||||
if ($res['bytes'] === '') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$offset += strlen($res['bytes']);
|
|
||||||
$downloaded_size += strlen($res['bytes']);
|
|
||||||
$this->logger->logger(fwrite($stream, $res['bytes']), \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
|
||||||
if ($theend) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($end !== -1) {
|
|
||||||
$this->callFork($cb($percent = $downloaded_size * 100 / $size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($end === -1) {
|
|
||||||
$this->callFork($cb(100));
|
|
||||||
}
|
|
||||||
if ($cdn) {
|
|
||||||
$this->clear_cdn_hashes($message_media['file_token']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (!$seekable) {
|
||||||
|
yield $offset['previous_promise'];
|
||||||
|
}
|
||||||
|
$res = yield $callable((string) $res['bytes'], $offset['offset'] + $offset['part_start_at']);
|
||||||
|
$cb();
|
||||||
|
return $res;
|
||||||
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private $cdn_hashes = [];
|
private $cdn_hashes = [];
|
||||||
|
@ -376,6 +376,10 @@ trait ResponseHandler
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (in_array($response['error_message'], ['MSGID_DECREASE_RETRY', 'RPC_CALL_FAIL', 'RPC_MCGET_FAIL', 'no workers running'])) {
|
||||||
|
Loop::delay(1 * 1000, [$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||||
|
|
||||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], isset($request['_']) ? $request['_'] : ''));
|
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code'], isset($request['_']) ? $request['_'] : ''));
|
||||||
|
@ -23,10 +23,10 @@ use Amp\DoH\DoHConfig;
|
|||||||
use Amp\DoH\Nameserver;
|
use Amp\DoH\Nameserver;
|
||||||
use Amp\DoH\Rfc8484StubResolver;
|
use Amp\DoH\Rfc8484StubResolver;
|
||||||
use Amp\Loop;
|
use Amp\Loop;
|
||||||
|
use function Amp\ByteStream\getInputBufferStream;
|
||||||
|
use function Amp\ByteStream\getStdin;
|
||||||
use function Amp\Dns\resolver;
|
use function Amp\Dns\resolver;
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
use function Amp\ByteStream\getStdin;
|
|
||||||
use function Amp\ByteStream\getInputBufferStream;
|
|
||||||
|
|
||||||
class Magic
|
class Magic
|
||||||
{
|
{
|
||||||
@ -68,6 +68,14 @@ class Magic
|
|||||||
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
||||||
set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
|
set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
|
||||||
if (!self::$inited) {
|
if (!self::$inited) {
|
||||||
|
if (!defined('\\phpseclib\\Crypt\\Common\\SymmetricKey::MODE_IGE') || \phpseclib\Crypt\Common\SymmetricKey::MODE_IGE !== 7) {
|
||||||
|
throw new Exception(\danog\MadelineProto\Lang::$current_lang['phpseclib_fork']);
|
||||||
|
}
|
||||||
|
foreach (['intl', 'xml', 'fileinfo', 'json', 'mbstring'] as $extension) {
|
||||||
|
if (!extension_loaded($extension)) {
|
||||||
|
throw Exception::extension($extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread');
|
self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread');
|
||||||
self::$BIG_ENDIAN = pack('L', 1) === pack('N', 1);
|
self::$BIG_ENDIAN = pack('L', 1) === pack('N', 1);
|
||||||
self::$bigint = PHP_INT_SIZE < 8;
|
self::$bigint = PHP_INT_SIZE < 8;
|
||||||
@ -147,14 +155,14 @@ class Magic
|
|||||||
Loop::onSignal(SIGINT, static function () {
|
Loop::onSignal(SIGINT, static function () {
|
||||||
getStdin()->unreference();
|
getStdin()->unreference();
|
||||||
getInputBufferStream()->unreference();
|
getInputBufferStream()->unreference();
|
||||||
Logger::log('Got sigint', Logger::FATAL_ERROR);
|
Logger::log('Got sigint', Logger::FATAL_ERROR);
|
||||||
die();
|
die();
|
||||||
});
|
});
|
||||||
/*Loop::onSignal(SIGTERM, static function () {
|
/*Loop::onSignal(SIGTERM, static function () {
|
||||||
Logger::log('Got sigterm', Logger::FATAL_ERROR);
|
Logger::log('Got sigterm', Logger::FATAL_ERROR);
|
||||||
Loop::stop();
|
Loop::stop();
|
||||||
die();
|
die();
|
||||||
});*/
|
});*/
|
||||||
}
|
}
|
||||||
if (!self::$altervista && !self::$zerowebhost) {
|
if (!self::$altervista && !self::$zerowebhost) {
|
||||||
$DohConfig = new DoHConfig(
|
$DohConfig = new DoHConfig(
|
||||||
|
@ -24,6 +24,9 @@ use danog\MadelineProto\Exception;
|
|||||||
use danog\MadelineProto\Stream\Async\RawStream;
|
use danog\MadelineProto\Stream\Async\RawStream;
|
||||||
use danog\MadelineProto\Stream\ConnectionContext;
|
use danog\MadelineProto\Stream\ConnectionContext;
|
||||||
use function Amp\Socket\connect;
|
use function Amp\Socket\connect;
|
||||||
|
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||||
|
use danog\MadelineProto\Stream\BufferInterface;
|
||||||
|
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||||
use Amp\ByteStream\ClosedException;
|
use Amp\ByteStream\ClosedException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,7 +34,7 @@ use Amp\ByteStream\ClosedException;
|
|||||||
*
|
*
|
||||||
* @author Daniil Gentili <daniil@daniil.it>
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
*/
|
*/
|
||||||
class BufferedRawStream implements \danog\MadelineProto\Stream\BufferedStreamInterface, \danog\MadelineProto\Stream\BufferInterface, \danog\MadelineProto\Stream\RawStreamInterface
|
class BufferedRawStream implements BufferedStreamInterface, BufferInterface, RawStreamInterface
|
||||||
{
|
{
|
||||||
use RawStream;
|
use RawStream;
|
||||||
|
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Buffered raw stream.
|
||||||
|
*
|
||||||
|
* This file is part of MadelineProto.
|
||||||
|
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU Affero General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||||
|
*
|
||||||
|
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace danog\MadelineProto\Stream\Common;
|
||||||
|
|
||||||
|
use Amp\Promise;
|
||||||
|
use Amp\Success;
|
||||||
|
use danog\MadelineProto\Exception;
|
||||||
|
use danog\MadelineProto\Stream\Async\RawStream;
|
||||||
|
use danog\MadelineProto\Stream\ConnectionContext;
|
||||||
|
use function Amp\Socket\connect;
|
||||||
|
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||||
|
use danog\MadelineProto\Stream\BufferInterface;
|
||||||
|
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffered raw stream.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
*/
|
||||||
|
class SimpleBufferedRawStream extends BufferedRawStream implements BufferedStreamInterface, BufferInterface, RawStreamInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Read data asynchronously.
|
||||||
|
*
|
||||||
|
* @param int $length Amount of data to read
|
||||||
|
*
|
||||||
|
* @return \Generator
|
||||||
|
*/
|
||||||
|
public function bufferReadAsync(int $length): \Generator
|
||||||
|
{
|
||||||
|
$size = fstat($this->memory_stream)['size'];
|
||||||
|
$offset = ftell($this->memory_stream);
|
||||||
|
$buffer_length = $size - $offset;
|
||||||
|
if ($buffer_length < $length && $buffer_length) {
|
||||||
|
fseek($this->memory_stream, $offset + $buffer_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($buffer_length < $length) {
|
||||||
|
$chunk = yield $this->read();
|
||||||
|
if ($chunk === null) {
|
||||||
|
fseek($this->memory_stream, $offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fwrite($this->memory_stream, $chunk);
|
||||||
|
$buffer_length += strlen($chunk);
|
||||||
|
}
|
||||||
|
fseek($this->memory_stream, $offset);
|
||||||
|
|
||||||
|
return fread($this->memory_stream, $length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get class name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function getName(): string
|
||||||
|
{
|
||||||
|
return __CLASS__;
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,8 @@ use Amp\CancellationToken;
|
|||||||
use Amp\Socket\ClientConnectContext;
|
use Amp\Socket\ClientConnectContext;
|
||||||
use Amp\Uri\Uri;
|
use Amp\Uri\Uri;
|
||||||
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
||||||
|
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
|
||||||
|
use danog\MadelineProto\Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection context class.
|
* Connection context class.
|
||||||
@ -217,6 +219,7 @@ class ConnectionContext
|
|||||||
{
|
{
|
||||||
return $this->isDns;
|
return $this->isDns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this connection context will only be used by the DNS client
|
* Whether this connection context will only be used by the DNS client
|
||||||
*
|
*
|
||||||
@ -261,6 +264,10 @@ class ConnectionContext
|
|||||||
*/
|
*/
|
||||||
public function setDc($dc): self
|
public function setDc($dc): self
|
||||||
{
|
{
|
||||||
|
$int = intval($dc);
|
||||||
|
if (!(1 <= $int && $int <= 1000)) {
|
||||||
|
throw new Exception("Invalid DC id provided: $dc");
|
||||||
|
}
|
||||||
$this->dc = $dc;
|
$this->dc = $dc;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -363,6 +370,23 @@ class ConnectionContext
|
|||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the inputClientProxy proxy MTProto object
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getInputClientProxy(): ?array
|
||||||
|
{
|
||||||
|
foreach ($this->nextStreams as $couple) {
|
||||||
|
list($streamName, $extra) = $couple;
|
||||||
|
if ($streamName === ObfuscatedStream::getName() && isset($extra['address'])) {
|
||||||
|
$extra['_'] = 'inputClientProxy';
|
||||||
|
return $extra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get a description "name" of the context.
|
* Get a description "name" of the context.
|
||||||
*
|
*
|
||||||
|
@ -24,6 +24,9 @@ namespace danog\MadelineProto\Stream\MTProtoTools;
|
|||||||
*/
|
*/
|
||||||
trait MsgIdHandler
|
trait MsgIdHandler
|
||||||
{
|
{
|
||||||
|
public $max_incoming_id;
|
||||||
|
public $max_outgoing_id;
|
||||||
|
|
||||||
public function check_message_id($new_message_id, $aargs)
|
public function check_message_id($new_message_id, $aargs)
|
||||||
{
|
{
|
||||||
if (!is_object($new_message_id)) {
|
if (!is_object($new_message_id)) {
|
||||||
|
@ -26,6 +26,11 @@ trait SeqNoHandler
|
|||||||
{
|
{
|
||||||
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
||||||
|
|
||||||
|
public $session_out_seq_no = 0;
|
||||||
|
public $session_in_seq_no = 0;
|
||||||
|
|
||||||
|
public $session_id;
|
||||||
|
|
||||||
public function generate_out_seq_no($content_related)
|
public function generate_out_seq_no($content_related)
|
||||||
{
|
{
|
||||||
$in = $content_related ? 1 : 0;
|
$in = $content_related ? 1 : 0;
|
||||||
|
59
src/danog/MadelineProto/Stream/MTProtoTools/Session.php
Normal file
59
src/danog/MadelineProto/Stream/MTProtoTools/Session.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Session module.
|
||||||
|
*
|
||||||
|
* This file is part of MadelineProto.
|
||||||
|
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU Affero General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||||
|
*
|
||||||
|
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace danog\MadelineProto\Stream\MTProtoTools;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages MTProto session-specific data
|
||||||
|
*/
|
||||||
|
class Session
|
||||||
|
{
|
||||||
|
use MsgIdHandler;
|
||||||
|
use SaltHandler;
|
||||||
|
use SeqNoHandler;
|
||||||
|
public $incoming_messages = [];
|
||||||
|
public $outgoing_messages = [];
|
||||||
|
public $new_incoming = [];
|
||||||
|
public $new_outgoing = [];
|
||||||
|
|
||||||
|
public $http_req_count = 0;
|
||||||
|
public $http_res_count = 0;
|
||||||
|
|
||||||
|
public $last_http_wait = 0;
|
||||||
|
private $last_chunk = 0;
|
||||||
|
|
||||||
|
public $time_delta = 0;
|
||||||
|
|
||||||
|
public $call_queue = [];
|
||||||
|
public $ack_queue = [];
|
||||||
|
|
||||||
|
|
||||||
|
public function haveRead()
|
||||||
|
{
|
||||||
|
$this->last_chunk = microtime(true);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get the receive date of the latest chunk of data from the socket
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function getLastChunk()
|
||||||
|
{
|
||||||
|
return $this->last_chunk;
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ class DefaultStream extends Socket implements RawStreamInterface, ProxyStreamInt
|
|||||||
|
|
||||||
public function enableCrypto(ClientTlsContext $tlsContext = null): \Amp\Promise
|
public function enableCrypto(ClientTlsContext $tlsContext = null): \Amp\Promise
|
||||||
{
|
{
|
||||||
return $this->enableCrypto($tlsContext);
|
return $this->stream->enableCrypto($tlsContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStream()
|
public function getStream()
|
||||||
|
136
src/danog/MadelineProto/Stream/Transport/PremadeStream.php
Normal file
136
src/danog/MadelineProto/Stream/Transport/PremadeStream.php
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Premade stream wrapper.
|
||||||
|
*
|
||||||
|
* This file is part of MadelineProto.
|
||||||
|
* MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
* MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU Affero General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||||
|
* If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @copyright 2016-2019 Daniil Gentili <daniil@daniil.it>
|
||||||
|
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||||
|
*
|
||||||
|
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace danog\MadelineProto\Stream\Transport;
|
||||||
|
|
||||||
|
use Amp\Promise;
|
||||||
|
use Amp\Socket\Socket;
|
||||||
|
use danog\MadelineProto\Stream\Async\RawStream;
|
||||||
|
use danog\MadelineProto\Stream\RawStreamInterface;
|
||||||
|
use function Amp\Socket\connect;
|
||||||
|
use function Amp\Socket\cryptoConnect;
|
||||||
|
use danog\MadelineProto\Stream\ProxyStreamInterface;
|
||||||
|
use Amp\ByteStream\ClosedException;
|
||||||
|
use danog\MadelineProto\Stream\ConnectionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Premade stream wrapper.
|
||||||
|
*
|
||||||
|
* Manages reading data in chunks
|
||||||
|
*
|
||||||
|
* @author Daniil Gentili <daniil@daniil.it>
|
||||||
|
*/
|
||||||
|
class PremadeStream extends Socket implements RawStreamInterface, ProxyStreamInterface
|
||||||
|
{
|
||||||
|
use RawStream;
|
||||||
|
private $stream;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableCrypto(ClientTlsContext $tlsContext = null): \Amp\Promise
|
||||||
|
{
|
||||||
|
return $this->stream->enableCrypto($tlsContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStream()
|
||||||
|
{
|
||||||
|
return $this->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connectAsync(ConnectionContext $ctx, string $header = ''): \Generator
|
||||||
|
{
|
||||||
|
if ($header !== '') {
|
||||||
|
yield $this->stream->write($header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async chunked read.
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function read(): Promise
|
||||||
|
{
|
||||||
|
return $this->stream ? $this->stream->read() : new \Amp\Success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async write.
|
||||||
|
*
|
||||||
|
* @param string $data Data to write
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function write(string $data): Promise
|
||||||
|
{
|
||||||
|
if (!$this->stream) {
|
||||||
|
throw new ClosedException("MadelineProto stream was disconnected");
|
||||||
|
}
|
||||||
|
return $this->stream->write($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async close.
|
||||||
|
*
|
||||||
|
* @return Generator
|
||||||
|
*/
|
||||||
|
public function disconnect()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($this->stream) {
|
||||||
|
if (method_exists($this->stream, 'close')) {
|
||||||
|
$this->stream->close();
|
||||||
|
}
|
||||||
|
$this->stream = null;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
\danog\MadelineProto\Logger::log('Got exception while closing stream: '.$e->getMessage());
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
\danog\MadelineProto\Logger::log('Got exception while closing stream: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
$this->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*
|
||||||
|
* @return \Amp\Socket\Socket
|
||||||
|
*/
|
||||||
|
public function getSocket(): \Amp\Socket\Socket
|
||||||
|
{
|
||||||
|
return $this->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setExtra($extra)
|
||||||
|
{
|
||||||
|
$this->stream = $extra;
|
||||||
|
}
|
||||||
|
public static function getName(): string
|
||||||
|
{
|
||||||
|
return __CLASS__;
|
||||||
|
}
|
||||||
|
}
|
@ -536,9 +536,6 @@ trait BotAPI
|
|||||||
$arguments['message'] = trim($this->html_fixtags($arguments['message']));
|
$arguments['message'] = trim($this->html_fixtags($arguments['message']));
|
||||||
|
|
||||||
$dom = new \DOMDocument();
|
$dom = new \DOMDocument();
|
||||||
if (!extension_loaded('mbstring')) {
|
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'mbstring']);
|
|
||||||
}
|
|
||||||
$dom->loadHTML(mb_convert_encoding($arguments['message'], 'HTML-ENTITIES', 'UTF-8'));
|
$dom->loadHTML(mb_convert_encoding($arguments['message'], 'HTML-ENTITIES', 'UTF-8'));
|
||||||
if (!isset($arguments['entities'])) {
|
if (!isset($arguments['entities'])) {
|
||||||
$arguments['entities'] = [];
|
$arguments['entities'] = [];
|
||||||
|
@ -463,7 +463,13 @@ trait TL
|
|||||||
}
|
}
|
||||||
} elseif ($method === 'messages.sendEncryptedFile') {
|
} elseif ($method === 'messages.sendEncryptedFile') {
|
||||||
if (isset($arguments['file'])) {
|
if (isset($arguments['file'])) {
|
||||||
if (!is_array($arguments['file']) && $this->settings['upload']['allow_automatic_upload']) {
|
if (
|
||||||
|
(
|
||||||
|
!is_array($arguments['file']) ||
|
||||||
|
!(isset($arguments['file']['_']) && $this->constructors->find_by_predicate($arguments['file']['_']) === 'InputEncryptedFile')
|
||||||
|
) &&
|
||||||
|
$this->settings['upload']['allow_automatic_upload']
|
||||||
|
) {
|
||||||
$arguments['file'] = yield $this->upload_encrypted_async($arguments['file']);
|
$arguments['file'] = yield $this->upload_encrypted_async($arguments['file']);
|
||||||
}
|
}
|
||||||
if (isset($arguments['file']['key'])) {
|
if (isset($arguments['file']['key'])) {
|
||||||
@ -622,7 +628,16 @@ trait TL
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputFile' && $this->settings['upload']['allow_automatic_upload']) {
|
if ($current_argument['type'] === 'InputFile'
|
||||||
|
&& (
|
||||||
|
!is_array($arguments[$current_argument['name']])
|
||||||
|
|| !(
|
||||||
|
isset($arguments[$current_argument['name']]['_'])
|
||||||
|
&& $this->constructors->find_by_predicate($arguments[$current_argument['name']]['_']) === 'InputFile'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
&& $this->settings['upload']['allow_automatic_upload']
|
||||||
|
) {
|
||||||
$arguments[$current_argument['name']] = yield $this->upload_async($arguments[$current_argument['name']]);
|
$arguments[$current_argument['name']] = yield $this->upload_async($arguments[$current_argument['name']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ use Amp\Failure;
|
|||||||
use Amp\Loop;
|
use Amp\Loop;
|
||||||
use Amp\Promise;
|
use Amp\Promise;
|
||||||
use Amp\Success;
|
use Amp\Success;
|
||||||
use function Amp\ByteStream\getOutputBufferStream;
|
|
||||||
use function Amp\ByteStream\getStdin;
|
use function Amp\ByteStream\getStdin;
|
||||||
use function Amp\ByteStream\getStdout;
|
use function Amp\ByteStream\getStdout;
|
||||||
use function Amp\Promise\all;
|
use function Amp\Promise\all;
|
||||||
@ -32,6 +31,10 @@ use function Amp\Promise\first;
|
|||||||
use function Amp\Promise\some;
|
use function Amp\Promise\some;
|
||||||
use function Amp\Promise\timeout;
|
use function Amp\Promise\timeout;
|
||||||
use function Amp\Promise\wait;
|
use function Amp\Promise\wait;
|
||||||
|
use function Amp\ByteStream\getOutputBufferStream;
|
||||||
|
use function Amp\File\exists;
|
||||||
|
use function Amp\File\touch;
|
||||||
|
use Amp\File\StatCache;
|
||||||
use phpseclib\Math\BigInteger;
|
use phpseclib\Math\BigInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -368,7 +371,50 @@ trait Tools
|
|||||||
|
|
||||||
return $deferred->promise();
|
return $deferred->promise();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Asynchronously lock a file
|
||||||
|
* Resolves with a callbable that MUST eventually be called in order to release the lock
|
||||||
|
*
|
||||||
|
* @param string $file File to lock
|
||||||
|
* @param integer $operation Locking mode (see flock)
|
||||||
|
* @param numeric $polling Polling interval for lock
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public static function flock(string $file, int $operation, $polling = 0.1): Promise
|
||||||
|
{
|
||||||
|
return self::call(self::flockAsync($file, $operation, $polling));
|
||||||
|
}
|
||||||
|
public static function noCache(int $status, string $message)
|
||||||
|
{
|
||||||
|
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
|
||||||
|
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
http_response_code($status);
|
||||||
|
return self::echo($message);
|
||||||
|
}
|
||||||
|
public static function flockAsync(string $file, int $operation, $polling)
|
||||||
|
{
|
||||||
|
if (!yield exists($file)) {
|
||||||
|
yield touch($file);
|
||||||
|
StatCache::clear($file);
|
||||||
|
}
|
||||||
|
$operation |= LOCK_NB;
|
||||||
|
$res = fopen($file, 'c');
|
||||||
|
do {
|
||||||
|
$result = flock($res, $operation);
|
||||||
|
if (!$result) {
|
||||||
|
yield self::sleep($polling);
|
||||||
|
}
|
||||||
|
} while (!$result);
|
||||||
|
|
||||||
|
return static function () use (&$res) {
|
||||||
|
if ($res) {
|
||||||
|
flock($res, LOCK_UN);
|
||||||
|
fclose($res);
|
||||||
|
$res = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
public static function sleep($time)
|
public static function sleep($time)
|
||||||
{
|
{
|
||||||
return new \Amp\Delayed($time * 1000);
|
return new \Amp\Delayed($time * 1000);
|
||||||
|
@ -47,7 +47,7 @@ trait AuthKeyHandler
|
|||||||
public function request_call_async($user)
|
public function request_call_async($user)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
$user = yield $this->get_info_async($user);
|
$user = yield $this->get_info_async($user);
|
||||||
if (!isset($user['InputUser']) || $user['InputUser']['_'] === 'inputUserSelf') {
|
if (!isset($user['InputUser']) || $user['InputUser']['_'] === 'inputUserSelf') {
|
||||||
@ -114,7 +114,7 @@ trait AuthKeyHandler
|
|||||||
public function confirm_call_async($params)
|
public function confirm_call_async($params)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) {
|
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) {
|
||||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id']));
|
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id']));
|
||||||
@ -163,7 +163,7 @@ trait AuthKeyHandler
|
|||||||
public function complete_call_async($params)
|
public function complete_call_async($params)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) {
|
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) {
|
||||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id']));
|
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id']));
|
||||||
@ -198,7 +198,7 @@ trait AuthKeyHandler
|
|||||||
public function call_status($id)
|
public function call_status($id)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
if (isset($this->calls[$id])) {
|
if (isset($this->calls[$id])) {
|
||||||
return $this->calls[$id]->getCallState();
|
return $this->calls[$id]->getCallState();
|
||||||
@ -210,7 +210,7 @@ trait AuthKeyHandler
|
|||||||
public function get_call($call)
|
public function get_call($call)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->calls[$call];
|
return $this->calls[$call];
|
||||||
@ -219,7 +219,7 @@ trait AuthKeyHandler
|
|||||||
public function discard_call_async($call, $reason, $rating = [], $need_debug = true)
|
public function discard_call_async($call, $reason, $rating = [], $need_debug = true)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
|
||||||
throw new \danog\MadelineProto\Exception(['extension', 'libtgvoip']);
|
throw \danog\MadelineProto\Exception::extension('libtgvoip');
|
||||||
}
|
}
|
||||||
if (!isset($this->calls[$call['id']])) {
|
if (!isset($this->calls[$call['id']])) {
|
||||||
return;
|
return;
|
||||||
|
@ -175,12 +175,13 @@ trait Loop
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->logger->logger($message);
|
$this->logger->logger($message);
|
||||||
|
$buffer = @ob_get_contents();
|
||||||
@ob_end_clean();
|
@ob_end_clean();
|
||||||
header('Connection: close');
|
header('Connection: close');
|
||||||
ignore_user_abort(true);
|
ignore_user_abort(true);
|
||||||
ob_start();
|
$buffer .= '<html><body><h1>'.htmlentities($message).'</h1></body></html>';
|
||||||
echo '<html><body><h1>'.$message.'</h1></body</html>';
|
echo $buffer;
|
||||||
$size = ob_get_length();
|
$size = max(ob_get_length(), strlen($buffer));
|
||||||
header("Content-Length: $size");
|
header("Content-Length: $size");
|
||||||
header('Content-Type: text/html');
|
header('Content-Type: text/html');
|
||||||
ob_end_flush();
|
ob_end_flush();
|
||||||
|
@ -58,6 +58,15 @@ try {
|
|||||||
$MadelineProto->accept_tos();
|
$MadelineProto->accept_tos();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$inputMediaUploadedPhoto1 = ['_' => 'inputMediaUploadedPhoto','file' => '1.jpg'];
|
||||||
|
$inputMediaUploadedPhoto2 = ['_' => 'inputMediaUploadedPhoto','file' => '2.jpg'];
|
||||||
|
$inputMediaUploadedPhoto3 = ['_' => 'inputMediaUploadedPhoto','file' => '3.jpg'];
|
||||||
|
$inputSingleMedia1 = ['_' => 'inputSingleMedia', 'media' => $inputMediaUploadedPhoto1, 'message' => 'str'];
|
||||||
|
$inputSingleMedia2 = ['_' => 'inputSingleMedia', 'media' => $inputMediaUploadedPhoto2, 'message' => 'str'];
|
||||||
|
$inputSingleMedia3 = ['_' => 'inputSingleMedia', 'media' => $inputMediaUploadedPhoto3, 'message' => 'str'];
|
||||||
|
$Updates = $this->messages->sendMultiMedia(['peer' => 'danogentili','multi_media' => [$inputSingleMedia3, $inputSingleMedia2,$inputSingleMedia1]]);
|
||||||
|
|
||||||
//var_dump(count($MadelineProto->get_pwr_chat('@madelineproto')['participants']));
|
//var_dump(count($MadelineProto->get_pwr_chat('@madelineproto')['participants']));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user