Compare commits

...

1374 Commits
v1.3 ... v1

Author SHA1 Message Date
Lonami Exo
67765f84a5 Update to layer 200 2025-03-09 13:05:29 +01:00
Lonami Exo
4bfe7849f6 Add missing input conversion for MessageMediaGeoLive
Fixes #4576.
2025-03-09 13:00:15 +01:00
Shrimadhav U K
859f7423f2 Add send_as and effect parameters in high level 2025-03-08 17:21:49 +01:00
Nick80835
6f5556373f Call getDifference on login to ensure qts is up to date 2025-03-07 18:03:01 +01:00
Nick80835
0fc9b14674 Make URL regex match everything except ]
Hopefully this is the last time I touch this.
2025-03-07 08:03:31 +01:00
Nick80835
0c2a3c144b Make URL regex match anything again
Including spaces.
2025-03-06 08:03:44 +01:00
martín
a03a8673e1
Clarify warning when reusing unintended session (#4568) 2025-03-02 19:40:34 +01:00
Lonami Exo
045df418df Update to v1.39 2025-02-20 18:26:00 +01:00
Nick80835
592a899aab Update to layer 199 2025-02-13 17:20:59 +01:00
Nick80835
1cb5ff1dd5 Consider range list-like
This allows you to pass range() to things such as get_messages as ids= without first explicitly converting it to a list.
2025-02-09 08:42:44 +01:00
s1xu
9762a83541 fix: support batch sending of image URLs and video URLs in albums 2025-02-08 15:40:56 +01:00
Nick80835
141b620437 Make markdown URL regex less greedy
Fixes multiple URLs in a single message.
2025-02-05 16:56:04 +01:00
Nick80835
551c24f3e4 Fix overlapping URLs and improve overlapping in md
Also remove the unused overlap function.
2025-02-02 04:11:46 +01:00
Nick80835
38d024312e Unconditionally match text and link text in markdown
Fixes cases where there's a nested [] in the text by matching until "](" is reached. This doesn't match newlines in URLs because that makes no sense.
2025-02-01 21:02:27 +01:00
Nick80835
a2926b548f Update to layer 198 2025-01-28 18:23:18 +01:00
Nick80835
455acc43f6 Improve edit_message message type hint
This also allows utils.get_message_id to get the ID of InputMessageID.
2025-01-19 10:40:11 +01:00
Nick80835
792adb78b3 Respect receive_updates=False 2025-01-18 19:03:57 +01:00
Nick80835
5a0e69693b Document drop_author and add drop_media_captions
drop_author is already supported but is undocumented. drop_media_captions for consistency with drop_author being implemented.
2025-01-17 17:02:42 +01:00
Nick80835
b9aafa3441 Fix IOError with some image modes in photo resize
This fixes image compression with mode "P" (potentially others) which is necessary as the server has erroneous alpha color with some types of images (mode "P" for example). This also properly applies the background argument that may be passed to _resize_photo_if_needed by always compressing images with alpha regardless of whether the server will compress the image for us.
2025-01-13 20:37:36 +01:00
Nick80835
494b20db2d
Add missing parameters to Message constructor (#4529) 2025-01-04 19:29:19 +01:00
Danish
0a6b649ead
Updated to Layer 196 (#4525) 2025-01-04 15:15:50 +01:00
Lonami
cfce68e9ad
Avoid error when trying to reset deadline for no msgbox entries
Closes #4520.
2024-12-22 15:07:35 +01:00
Lonami Exo
b09c8c83f7 Update to layer 195 2024-12-15 12:31:27 +01:00
Danipulok
225ea9c3ab fix(session): persist session after session.set_dc 2024-11-20 17:25:56 +01:00
Lonami Exo
9ca3b599fc Fix Python 3.6 compatibility
Introduced on accident by #4475.
2024-11-18 17:04:07 +01:00
Lonami Exo
63d55bbe3d Bump to v1.38 2024-11-17 22:32:16 +01:00
Lonami Exo
c9cce8aa81 Update to layer 193 2024-11-17 22:23:37 +01:00
Colin Watson
70098c58a5 Fix test broken by removal of imghdr
This has been broken since bd11564579.
2024-11-06 16:23:33 +01:00
Colin Watson
769b65efb1 Exclude sign_up from test_all_methods_present
The sign_up method was deprecated in
07a7a8b404 and removed from the
documentation, but since the method technically still exists (even
though it immediately raises ValueError), that broke a test.
2024-11-06 16:23:33 +01:00
Devesh Pal
f03e4b1137
Update to Layer 192 (#4503) 2024-10-31 22:10:02 +01:00
Greg Eremeev
a77835a7d9
Fix reuse of captions during send msg with file (#4500) 2024-10-25 23:27:58 +02:00
Mohsen
85c4a91317
Fix typos in FAQ section (#4498) 2024-10-24 18:56:15 +02:00
Arkadii Halchenko
3f589b287d
Fix parse_mode with albums (#4494)
Closes #4492.
2024-10-20 19:50:08 +02:00
Lonami Exo
8138be2503 Update to layer 190 2024-10-15 23:20:57 +02:00
Devesh Pal
a0e42c1eb7
Update to layer 189 (#4476) 2024-10-05 21:43:51 +02:00
Arkadii Halchenko
4553f04e49
Fix formatting_entities not working for albums (#4475) 2024-10-05 11:13:20 +02:00
Lonami Exo
f652f3f01a Bump to v1.37 2024-09-23 19:01:20 +02:00
Lonami Exo
693c73ec1d Update to layer 188 2024-09-23 19:01:10 +02:00
Mohsen
a9442ef1be
Remove right-adjust from logging example docs (#4461)
Only works with %-style formatting, but not in the logging module.
2024-09-11 20:54:59 +02:00
Devesh Pal
d37b0f812f
Update to layer 187 (#4457) 2024-09-07 10:06:07 +02:00
kristal
b01d3d7a2f
Fix edge case on get_messages when reverse=True (#4455)
Closes #4453.
2024-09-05 17:00:22 +02:00
delobanov
aec957d62d
Add error message back to proxy errors (#4443) 2024-08-29 23:54:34 +02:00
delobanov
46854a7660
Fix ConnectionError() takes no keyword arguments with proxies (#4440) 2024-08-28 22:41:38 +02:00
Lonami Exo
90f1e5b073 Update to layer 186 2024-08-25 21:14:51 +02:00
灰白草
75408483ad
Support CDN downloads (#4420)
Closes #4327.
2024-08-07 20:25:35 +02:00
Lonami Exo
946f803de7 Handle FloodPremiumWaitError
Closes #4417.
2024-07-24 16:38:34 +02:00
Shrimadhav U K
087191e9c5
Update API Scheme to Layer 184 (#4413) 2024-07-19 17:43:02 +02:00
Lonami Exo
a5c98aec50 Update to layer 183 2024-07-06 21:03:29 +02:00
2ei
cfebb9df05
Remove unused imports (#4397) 2024-06-21 00:06:40 +02:00
Lonami Exo
04aea46fe4 Update to v1.36 2024-06-11 16:42:47 +02:00
Lonami Exo
3def9433b8 qts_count is always assumed to be 1
Per the docs https://core.telegram.org/api/updates:
> events are never grouped,
> so it's assumed that qts_count is always equal to 1.
2024-06-04 23:12:02 +02:00
Lonami Exo
b3e210a1fb Update to layer 181 2024-06-01 11:00:14 +02:00
Lonami Exo
47673680f4 Update to layer 179 2024-05-01 17:43:24 +02:00
Lonami Exo
1974b663a2 Add missing type to _store_own_updates 2024-04-28 10:16:56 +02:00
Lonami Exo
881bfaac5c Update to layer 179 2024-04-27 16:56:38 +02:00
Lonami Exo
0f6dd5987e Add readthedocs dependency on sphinx-rtd-theme 2024-04-24 19:03:26 +02:00
Lonami Exo
d77ac18695 Bump to v1.35 2024-04-24 18:51:29 +02:00
Lonami Exo
8137b12bec Fix readthedocs requirements 2024-04-24 18:46:57 +02:00
Lonami Exo
10a6d16af6 Update to layer 178 2024-04-24 18:45:09 +02:00
Darskiy
3ac11e15ec
Fix get_messages type hint (#4357) 2024-04-23 16:54:41 +02:00
Darskiy
3625bf849d
Fix get_entity type hint (#4352) 2024-04-18 20:52:29 +02:00
Shubham Kumar
d3a201a277
Fix regression on supported Python version (#4347) 2024-04-12 21:06:15 +02:00
CoderX
49a8f111d3
Add missing attributes to Message (#4346) 2024-04-08 21:24:43 +02:00
Lonami Exo
723fbd570f Update to layer 177 2024-04-05 18:28:31 +02:00
Lonami Exo
26aa178cf6 Handle FileReferenceExpiredError during download
May fix #4341.
2024-04-02 11:02:32 +02:00
Lindsay Zhou
9f3e7e4aa8
Fix TelegramClient init with None session (#4339) 2024-03-30 15:10:12 +01:00
Jordan Gillard
75d609ab2a
Fix type hint in start (#4332) 2024-03-26 20:30:01 +01:00
John Bezustally
4d34243b98
Add drop_author param to forward_messages (#4329) 2024-03-18 08:30:38 +01:00
Lonami Exo
7ceb2e0b25 Add missing quick_reply_shortcut_id 2024-03-10 10:53:32 +01:00
Lonami Exo
47178dfaef Update to layer 176 2024-03-08 23:15:54 +01:00
Lonami Exo
d90d0dc00f message parameter must be optional 2024-02-23 20:53:54 +01:00
Lonami Exo
d1518f002a Fix custom Message lacking parameter 2024-02-21 17:01:03 +01:00
Lonami Exo
319db57ccb Update to layer 174 2024-02-16 23:09:44 +01:00
Confused Character
22bf0b4310
Add custom secret support to TcpMTProxy (#4309) 2024-02-16 22:45:38 +01:00
Lonami Exo
2b99ff65c5 Support pathlib.Path as session again
See #3737.
2024-02-13 18:18:52 +01:00
Lonami Exo
39fc5c5fef Update changelog 2024-02-02 18:23:52 +01:00
Lonami Exo
65c27c5ced Bump to v1.34 2024-02-02 18:17:21 +01:00
Lonami Exo
d76f3b7556 Update to layer 173 2024-02-02 18:16:56 +01:00
Just-a-xD
41eb665c9d
Fix custom parse_mode instances (#4304) 2024-02-02 18:16:01 +01:00
Lonami Exo
70201a9ff1 Fix Message finish init for reply_to stories 2024-01-31 21:57:41 +01:00
Prashant Sengar
63d9b267f4
Fix unparsing of message.text (#4301)
Co-authored-by: Disk6969 <disk6969@users.noreply.github.com>
2024-01-20 10:42:13 +01:00
Lonami Exo
6187ff7dcb Update to layer 172 2024-01-18 18:48:36 +01:00
exovoq
6ee2fffce8
Add reply_to_chat and reply_to_sender in Message (#4300) 2024-01-18 18:48:22 +01:00
Lonami Exo
32a4cb82ce Update to layer 171 2024-01-17 17:09:12 +01:00
Lonami Exo
a97a7a5400 Add new config file for readthedocs 2024-01-14 11:49:49 +01:00
Lonami Exo
9dbe9a7669 Add missing saved_peer_id parameter to Message 2024-01-04 13:01:28 +01:00
Lonami Exo
c445684be8 Update to layer 170 2024-01-01 22:07:20 +01:00
Lonami Exo
1241671e72 Update to layer 169 2023-12-26 10:55:15 +01:00
Jahongir Qurbonov
b882348a2b
Fix restriction_reason type hint (#4282) 2023-12-25 10:13:03 +01:00
Allen Calderwood
2082a0e4de
Fix typo in documentation example (#4277) 2023-12-18 17:32:17 +01:00
Lonami Exo
6cf1be93ae Bump to v1.33.1 2023-12-08 17:31:43 +01:00
Lonami
3d58dc355e
Fix ordering of closing tags of sequential entities (#4268) 2023-12-08 08:12:02 +01:00
udf
3b428f97a9
Fix ordering of nested entities 2023-12-07 18:25:11 +02:00
udf
abeb8c4d8d
Prioritise closing tags when sorting tags 2023-12-07 18:09:02 +02:00
Lonami Exo
985d12e169 Bump to v1.33 2023-12-01 20:30:19 +01:00
Lonami Exo
1ef66896bd Update to layer 167 2023-12-01 20:16:56 +01:00
Balázs Triszka
584735afe1
Conditional webbrowser import (#4261) 2023-11-28 00:04:36 +01:00
mario-ttide
cf3bc71e1d
Retry on TimedOutError (#4255) 2023-11-19 18:14:34 +01:00
Lonami Exo
ddc9bef503 Force filename with JPG extension after resizing
Old name does not matter, since we just encoded it as JPEG
2023-11-12 21:13:24 +01:00
Lonami
308f8e8bf8
Add PR template mentioning v1 is feature-frozen
Should prevent efforts like #4244 going to waste in the future.
2023-11-09 17:04:05 +01:00
Lonami Exo
6ccd6b0a41 Bump to v1.32 2023-10-31 19:12:41 +01:00
Lonami Exo
b17e10af1d Fix init of custom Draft after layer update 2023-10-29 11:41:54 +01:00
Lonami Exo
046dbb58b8 Update to layer 166 2023-10-29 11:00:13 +01:00
Lonami Exo
fda6840449 Fix file name could be lost when uploading files
Leading to invalid extension when sending photos.
2023-10-17 20:31:58 +02:00
Lonami Exo
eb67ef1b15 Update to v1.31 2023-10-12 18:39:29 +02:00
Lonami Exo
7d7dbdf47f Update to layer 165 2023-09-29 22:12:41 +02:00
Lonami Exo
6a36066d19 Update to layer 164 2023-09-22 20:49:39 +02:00
Lonami Exo
bd11564579 Remove uses of imghdr
It's deprecated. Closes #4207.
2023-09-20 18:30:57 +02:00
Alexander Goryushkin
ad19987cd6
Fixed sorting of markup entities with the same offsets (#4201) 2023-09-14 18:52:04 +02:00
Lonami Exo
7325718f0e Fix date empty when getting difference 2023-09-13 17:35:15 +02:00
Lonami Exo
7ce0b2f940 Fix invalid date type in UpdateShort 2023-09-12 17:16:55 +02:00
Dingyuan Wang
5ba312555a
Fix generator for pypy (#4198) 2023-09-12 08:28:14 +02:00
Lonami Exo
2cef715921 Bump to v1.30 2023-09-10 12:28:10 +02:00
Lonami Exo
ba99b8b466 Update to layer 162 2023-09-10 12:15:14 +02:00
Non
72faa89361
Remove client-side check in message.edit (#4195)
Fixes #4193.
2023-09-08 18:35:25 +02:00
Kacnep89
e928fbdac0
Fix date empty (#4191) 2023-09-06 16:47:46 +02:00
Shubham Kumar
9b1d9aa672
Fix incorrect param type in apply_channel_difference (#4185) 2023-08-29 18:40:07 +02:00
Lonami Exo
72f16ef73e Update to layer 161
Closes #4184.
2023-08-29 15:53:25 +02:00
Lonami Exo
33f3e27e7d Change apply_deadlines_reset micro-optimization
No need for buffer reuse in Python. It simply complicates the code.
And even then it was not as optimal as it could.
2023-08-29 15:04:04 +02:00
Lonami Exo
ac483e6812 Only update seq if pts changed
This solves UpdateChatParticipant being missed after UpdateChat,
which seems to reliable occur when a bot is in a Chat that gets
deleted.
2023-08-29 15:04:04 +02:00
Lonami Exo
d40aae75f3 Further improve MessageBox trace logging 2023-08-29 15:04:04 +02:00
Lonami Exo
574e8876ec Fix getting_diff_for with empty set was being spammed
Because the above check used >= but the inner check >.
2023-08-29 15:04:04 +02:00
Lonami Exo
2011a329b0 Make MessageBox trace logs more useful 2023-08-29 15:04:04 +02:00
misuzu
0cc9ca9bd9
Fix is_inline check for KeyboardButtonWebView (#4183) 2023-08-28 17:40:23 +02:00
Kacnep89
e617b59d48
Return marked ID from MemorySession.get_entity_rows_by_id (#4177)
Otherwise the unpacking done later won't work.
2023-08-23 16:07:32 +02:00
Lonami Exo
b0f9fd1f25 Except all types of timeout error
Closes #4172.
2023-08-18 18:36:30 +02:00
Lonami Exo
128b707488 Don't treat asyncio.IncompleteReadError as unhandled
The library will behave the same, but the log severity is lowered.
2023-08-03 19:01:10 +02:00
Bernhard M. Wiedemann
6ded164b85
Sort tlobjects before generating their listing (#4163) 2023-08-01 20:23:24 +02:00
Lonami Exo
211238fcd2 Fix reply_to when sending albums 2023-07-24 17:25:03 +02:00
Nick80835
694c78c8e9
Improve image compression heuristics and algorithm used (#4158) 2023-07-23 21:58:10 +02:00
Lonami Exo
ce010e9bfb Fix handling of UpdateShortSentMessage 2023-07-23 17:12:16 +02:00
Lonami Exo
413a2bb9f3 Bump to v1.29.0 2023-07-23 10:48:04 +02:00
novenary
9cf4cd70d1
Disable blank issues in GitHub (#4157) 2023-07-23 10:32:57 +02:00
Lonami Exo
131f021d51 Don't attempt thumb download if there is no thumb 2023-07-22 10:52:03 +02:00
Lonami Exo
438aff3545 Handle FloodWaitError in update loop
Likely temporary server issues, since getDifference should
realistically not fail with flood waits. In any case,
stopping early until the problem is resolved is the correct
approach.
2023-07-21 23:01:12 +02:00
Lonami Exo
4eef9b52c9 Handle sqlite3.OperationalError in update loop 2023-07-21 22:56:16 +02:00
Lonami Exo
a0cda0c37c Remove client-side checks when editing permissions
The server should instead fail with proper RPC errors,
as the rules could change any time (and the local checks
get out of date).
2023-07-21 22:53:38 +02:00
Lonami Exo
816b0bdf9f Fix _get_thumb failed when document had no thumbs 2023-07-21 22:48:12 +02:00
Lonami Exo
164d35681e Fix reply_to can be optional 2023-07-21 22:44:35 +02:00
Lonami Exo
75ed58ad89 Update to layer 160 2023-07-21 21:24:10 +02:00
Lonami
16ed9614f9 Change html.unparse logic to mimic markdown's
It was overcomplicated and had some subtle bugs.
Closes #4133.
2023-06-17 13:11:14 +02:00
Lonami Exo
9267917031 Improve error message when trying to delete inline messages
Closes #4129.
2023-06-09 17:48:08 +02:00
rozha
1e63de9b68
Fix lack of support for anon channel restrictions (#4130) 2023-06-09 17:42:34 +02:00
Lonami Exo
2826c942c0 Support most usernames in VALID_USERNAME_RE
See #4128.
2023-06-09 17:41:45 +02:00
Devesh Pal
65407fc899
Document more RPC errors (#4127) 2023-06-06 22:51:27 +02:00
Lonami Exo
c3bddf9440 Add missing formatting arg in logging call
Noticed in #4123.
2023-06-02 23:04:14 +02:00
Lonami Exo
4ff7ac6b75 Handle CancelledError inside mtprotosender recv loop 2023-06-02 19:04:51 +02:00
novenary
c3ec775607 Clarify OS field in bug report template 2023-06-02 12:20:27 +03:00
iamilya
aab8009a5a
Fix comment_to for a group of messages (#4120) 2023-05-31 17:04:33 +02:00
Lonami
0f0ca6b0d9
Upgrade issue templates to issue forms (#4118) 2023-05-31 00:02:20 +02:00
Lonami
c89644eec4
Update some fields in the GH issue template 2023-05-31 00:01:16 +02:00
novenary
ed825a2c7d Add dedicated form for documentation issues 2023-05-29 21:58:28 +03:00
novenary
9751b356fe Change feature request template to an issue form 2023-05-29 21:53:41 +03:00
novenary
6acc39ac04 Change bug report template to an issue form
Lifted from Tachiyomi and adapted for Telethon.

See: https://github.com/tachiyomiorg/tachiyomi/blob/master/.github/ISSUE_TEMPLATE/report_issue.yml
2023-05-29 21:29:26 +03:00
Lonami Exo
9fe5937ae1 Update FAQ with more common questions 2023-05-24 19:23:12 +02:00
Lonami Exo
16122545ec Add check for asyncio event loop to remain the same 2023-05-24 19:15:46 +02:00
Lonami Exo
6a7a981b7a Fix asyncio.CancelledError was being swallowed by inner except
Closes #4104.
2023-05-08 22:34:12 +02:00
Lonami Exo
980f8b32fc Fix KeyError when ID is in cache but queried without mark
Closes #4084.
2023-05-05 00:04:30 +02:00
Lonami Exo
c4a41adae5 Better document breaking ToS will lead to bans
Closes #4102.
2023-05-04 19:05:07 +02:00
Lonami Exo
2889bd5bf3 except and propagate TypeNotFoundError during update handling 2023-05-03 17:56:13 +02:00
R.T
9c7ac3b210
Fix absolute import should be relative (#4101) 2023-04-30 18:27:09 +02:00
Lonami Exo
ce29f13606 Fix UserUpdate.last_seen check 2023-04-30 10:32:08 +02:00
Lonami Exo
d7bd554ba0 Fix ValueError during connect with catchup on bad cache
Closes #4080.
2023-04-29 13:10:00 +02:00
Lonami Exo
ccf67d0f4f Exit receive loop on IOError or unhandled exceptions
May help with #4088.
2023-04-29 12:53:25 +02:00
Lonami Exo
03ff996ace Improve unhelpful 'readexactly size can not be less than zero'
Technically closes #4092, as the error is now properly handled.
2023-04-29 12:33:32 +02:00
Lonami Exo
9aad453e1a Update to layer 158 2023-04-21 17:11:59 +02:00
Deer-Spangle
6e7423e894
Allowing nosound_video to be specified (#4090) 2023-04-14 22:03:03 +02:00
Nick80835
7b1b33f805
Save photos as progressive when uploading (#4089) 2023-04-13 20:11:52 +02:00
David
d419979406
Declare missing exception variable (#4087) 2023-04-12 17:36:43 +02:00
SsNiPeR1
acec8a776f
Fix documentation typo (#4086) 2023-04-11 17:43:44 +02:00
Nitan Alexandru Marcel
ced36adb03
Fix editing inline messages of type InputBotInlineMessageID64 (#4082) 2023-04-07 17:04:08 +02:00
Lonami Exo
5b1135734b Properly handle PhoneCodeExpiredError in sign_in
Should actually fix #3185 now.
2023-04-06 18:53:12 +02:00
Lonami Exo
10c74f8bab Bump to v1.28.0 2023-04-06 15:05:44 +02:00
Lonami Exo
af18538722 Handle PhoneCodeExpiredError during sign_in
Closes #3185.
2023-04-06 14:36:24 +02:00
Lonami Exo
fd09284598 Update FAQ
Closes #3759.
2023-04-06 14:32:45 +02:00
Lonami Exo
a657ae0134 Save self user ID in session file
Should result in one less request after connecting,
as there is no longer a need to fetch the self user.
2023-04-06 14:18:42 +02:00
Lonami Exo
88bc6a46a6 Store self user in entity cache 2023-04-06 13:58:26 +02:00
Lonami Exo
97b0ba6707 Flush in-memory cache to session after a limit is reached
Should fully close #3989.
Should help with #3235.
2023-04-06 13:45:12 +02:00
Lonami Exo
cb04e269c0 Fix _get_entity_pair could receive None as input 2023-04-06 13:39:56 +02:00
Lonami Exo
d1e3237c41 Remove now-unused EntityCache class 2023-04-06 13:37:40 +02:00
Lonami Exo
f7e38ee6f0 Remove redundant entity cache
Progress towards #3989.
May also help with #3235.
2023-04-06 13:25:48 +02:00
Lonami Exo
3e64ea35ff Update FAQ 2023-04-06 13:25:47 +02:00
Lonami Exo
f9001bc8e0 Include Telethon version on fatal errors during updates
Since a lot of people don't mention the version when reporting
issues, it makes it hard to determine whether it's already been
fixed or not.
2023-04-06 13:25:47 +02:00
Kacnep89
68ea208b43
Periodically save update state (#4071) 2023-03-28 19:00:36 +02:00
Lonami Exo
0f7756ac68 Remove dead code from send_file 2023-03-28 18:17:07 +02:00
Lonami Exo
33c5ee9be4 Implement progress_callback for sending albums
Closes #3190.
2023-03-28 18:15:57 +02:00
Kacnep89
a942b021bc
Fix conversion and time zone issues (#4072) 2023-03-28 17:38:46 +02:00
Lonami Exo
516a2e7435 Handle timeout error during getDifference
Closes #4043.
2023-03-12 17:46:25 +01:00
Lonami Exo
be59c36ed3 Handle errors at connection level
Closes #4042.
2023-03-12 17:43:36 +01:00
Lonami Exo
acd3407418 Propagate errors at the connection level 2023-03-12 17:43:19 +01:00
Lonami Exo
f3414d134a Handle invalid buffers at protocol level
See #4042.
2023-03-12 17:27:22 +01:00
Lonami Exo
177386e755 Update to layer 155 2023-03-12 17:15:28 +01:00
Lonami Exo
1f79f063a2 Expand documentation on using the raw API 2023-03-11 12:45:06 +01:00
Lonami Exo
b87a8d0c1f Remove mentions to methods in generated TL ref
Instead, consistently use the term request, to avoid confusion.
2023-03-11 12:26:17 +01:00
Lonami Exo
b68c1f4f03 Add docs warning to file.id about it not being maintained 2023-03-11 11:56:43 +01:00
Lonami Exo
6bc7245106 Add file IDs section to the FAQ 2023-03-11 11:56:39 +01:00
Lonami Exo
373601500f Update to layer 154 2023-03-09 21:34:48 +01:00
Lonami Exo
f334d5b8fe Move working with messages to the wiki 2023-02-28 21:58:07 +01:00
Lonami Exo
4de1609d4e Fix ChatAction for groups with hidden members 2023-02-26 11:17:12 +01:00
Lonami Exo
07a7a8b404 Deprecate force_sms and sign_up
On the 10th of February, Telegram sent the following message to
those with an application registered on https://my.telegram.org.

--

Telegram API Update. Hello [REDACTED]. Thank you for contributing to the
open Telegram ecosystem by developing your app, [REDACTED].

Please note that due to recent updates to Telegram's handling of SMS and
the integration of new SMS providers like Firebase, we are changing the
way login codes are handled in third-party apps based on the Telegram API.

Starting on 18.02.2023, users logging into third-party apps will only be
able to receive login codes via Telegram. It will no longer be possible
to request an SMS to log into your app - just like when logging into
Telegram's own desktop and web clients.

Exactly like with the Telegram Desktop and Web apps, if a user doesn't
have a Telegram account yet, they will need to create one first using
an official mobile Telegram app.

We kindly ask you to update your app's login and signup interfaces to
reflect these changes before they go live on 18.02.2023 at 13:00 UTC.

This change will not significantly affect users since, according to our
research, the vast majority of third-party app users also use official
Telegram apps. In the coming months, we expect to offer new tools for
third-party developers that will help streamline the login process.
2023-02-26 10:40:53 +01:00
Lonami Exo
0563430314 Slightly improve documentation 2023-02-26 10:10:19 +01:00
Lonami Exo
acfde7132b Support Message without client as inputs to forward_messages
Raw API does not monkey-patch the client on the returned messages,
so the get_input_chat call would return None, causing the forward
to fail. Instead, manually resolve the chat using the message's
peer_id.

The resolve call can also be avoided if from_peer is passed (which
may mismatch with where the messages actually belong, but that's not
really a concern).
2023-02-08 17:35:49 +01:00
Lonami Exo
610b8c34dd Prevent publishing to PyPi if tl.telethon.dev is out-of-date 2023-02-06 17:40:51 +01:00
Lonami Exo
daf21f12d9 Bump to v1.27 2023-02-05 15:19:41 +01:00
Lonami Exo
6dece6e8a1 Update to layer 152 2023-02-05 15:05:05 +01:00
Lonami Exo
9f077e356b Fix downloading vcard and webdoc with Path
Closes #4027.
2023-01-23 08:48:39 +01:00
Nick80835
1f8b59043b
Change maximum photo size to 2560px (#4024) 2023-01-18 17:37:56 +01:00
Lonami Exo
cc3d25eeb8 Wrap init request in invokeWithoutUpdates if requested
This may fix #3743.
2023-01-14 13:31:32 +01:00
Lonami Exo
d81eb0b2e8 Apply pts returned by some additional requests
When a bot account sends a message, deletes it, and sends a new one,
very reliably it would detect a gap, and as a result recover the
second message it sent, processing it itself (because the hack with
`_self_outgoing` cannot possibly work when catching up).

Now certain `rpc_result` are also processed as-if they were updates
(including the ones from deleting messages), which solves this gap
issue. Not entirely sure if it's a hack or the intended way to do it
(since Telegram *does* return proper `updates` for other RPCs), but
it seems to solve this particular problem.

Other requests such as reading history, mentions or reactions also
return an instance of this type, but the `pts_count` should be 0,
and at worst it should simply trigger a gap, which shouldn't be a
big deal.
2023-01-14 12:31:01 +01:00
Lonami Exo
83bafa25e3 Stop using asyncio.get_event_loop()
It is deprecated in newer Python versions.
Closes #4013.
2023-01-11 21:02:29 +01:00
Lonami Exo
fb97a8aa87 Propagate account being logged-out errors
Should close #4016.
2023-01-11 20:30:33 +01:00
Lonami Exo
c932d79ab3 Ignore improperly formatted errors from Telegram's JSON 2023-01-11 20:28:29 +01:00
Lonami Exo
94cc897019 Sync list of known errors with Telegram's JSON 2023-01-11 19:53:35 +01:00
Lonami Exo
7a74dedc48 Add script to sync errors with Telegram's JSON 2023-01-11 19:53:20 +01:00
Lonami Exo
6332690a51 Sort RPC error data 2023-01-11 19:36:30 +01:00
Lonami Exo
2007c83c9e Update to layer 151 2022-12-31 18:38:53 +01:00
Lonami Exo
7288c9933c Update to layer 150 2022-12-22 20:30:38 +01:00
Lonami Exo
979e38152d Bump to v1.26.1 2022-12-22 20:30:38 +01:00
Lonami Exo
6d2a5dada5 Fix PERSISTENT_TIMESTAMP_EMPTY for new entries with pts 1, count 0
Because Telegram can't actually use 0 for the pts, it uses 1, even
if the count is 0. This forces the next update to use 2, or else it
could not be fetched when using an offset of 1 (despite the count
being 0 on the first update, which should not have bumped the second
update to use 2).

This caused Telethon to create an initial state of 0 for the new entry
(and also "incorrectly" detected following updates as gaps, which
would quickly trigger the call to get difference with a bad pts).

Now Telethon is aware of this special-case and will not initialize
state as 0, even if that's not the "correct" thing to do.
2022-12-22 20:30:38 +01:00
yumupdate
061a84bef2
Fix payment.py example (#4005) 2022-12-22 13:42:30 +01:00
Lonami Exo
e750eb7ab5 Add more debug traces to the messagebox 2022-12-18 12:57:51 +01:00
Lonami Exo
59ffad0090 Promote messagebox warnings to errors
No point in making a warning if it immediately is raised.
2022-12-18 12:42:14 +01:00
Lonami Exo
a8ce308b7a Fix messagebox state trace was not logging the object
This state was a TL State, not the MessageBox State with repr.
2022-12-18 12:41:08 +01:00
Lonami Exo
c72c7b160a Introduce trace-level logs to MessageBox
These will log sensitive information.
They are disabled when running with PYTHONOPTIMIZED.
They can only be enabled by setting a level lower than DEBUG,
which is difficult to do on accident.
2022-12-17 23:13:06 +01:00
Lonami Exo
5080715565 Change updates add_done_callback to discard tasks more reliably
See #3235. This should help tone down memory usage a little.
2022-12-16 08:34:01 +01:00
Lonami Exo
b2925f8279 Update documentation and list of known errors 2022-12-14 20:00:55 +01:00
Lonami Exo
4a6ef97910 Fix calls to disconnect after logout
Introduced by 83f13da420.
2022-12-13 08:33:13 +01:00
Lonami Exo
83f13da420 Don't error when calling disconnect after logout 2022-11-27 11:22:30 +01:00
Lonami Exo
4f51604def Fix sending copies of a file message should ignore parse mode
Closes #3983.
2022-11-26 09:12:06 +01:00
Lonami Exo
ba7fc245ab Set documentation language to en 2022-11-26 09:11:49 +01:00
Lonami Exo
bd1ba3bf1e Bump to v1.26 and update layer to 149 2022-11-25 18:23:28 +01:00
Lonami Exo
8ae75db862 Sort updates preemptively
Closes #3936.

There are two changes made to ensure the first update in a channel
cannot be lost, first by always sorting updates before applying pts,
and second by cautiously initializing the local pts if the client
had no pts known beforehand.

It might be possible to cleanup the handling of possible gaps now
that updates are always sorted, but that requires more thought.
2022-11-20 22:51:59 +01:00
Lonami Exo
2c85ffea12 Fix get_dialogs could fail when count % chunk_size = 0
Closes #3971.
2022-11-09 16:14:12 +01:00
Shrimadhav U K
fb43f638ff
Update to layer 148 and document new more errors (#3970) 2022-11-05 20:09:08 +01:00
Lonami Exo
073b87ba1f Fix some typos and add note about BotFather in migration guide 2022-10-26 14:13:30 +02:00
Lonami Exo
0c868065c7 Handle ConnectionError during update handling 2022-10-25 12:28:40 +02:00
Lonami Exo
2ffac2dcdb Handle DestroyAuthKey result more gracefully 2022-10-21 15:52:00 +02:00
Lonami Exo
f902c9293a Fix log message formatting when obj was not updates 2022-10-21 15:50:37 +02:00
Lonami Exo
a7db08d020 Fix sender destroy session handling was not running 2022-10-21 15:49:18 +02:00
Lonami Exo
0980d55c34 Document how to send spoilers and custom emoji 2022-10-21 11:12:55 +02:00
Lonami Exo
b3266fabd8 Fix iter_messages could get stuck on global search
Found thanks to #920. Issue probably introduced in b6d8311.
2022-10-18 16:00:32 +02:00
th3c00lw0lf
ef4f9a962c
Fix MediaEmptyError error when sending some videos (#3951) 2022-10-15 20:01:29 +02:00
Devesh Pal
f819593cbf
Update to layer 146 (#3933) 2022-10-14 18:50:27 +02:00
Lonami Exo
2d237c41fe Revert accidental NO_UPDATES_TIMEOUT change and bump to v1.25.4 2022-10-14 18:43:44 +02:00
Lonami Exo
7f5a1ec5e1 Bump to v1.25.3 2022-10-14 18:41:39 +02:00
Lonami Exo
949b54fdb0 Fix edit_admin failing on small Chat 2022-10-13 17:56:47 +02:00
Lonami Exo
b6d8311a55 Fix iter_messages was stopping too early in some channels
Closes #3949.
2022-10-13 13:40:25 +02:00
Lonami Exo
db29e9b7ef Don't unnecessarily refetch the sender twice 2022-10-03 13:11:48 +02:00
Lonami Exo
299b090cde Let download_profile_photo work with min-User 2022-10-03 13:06:27 +02:00
Lonami Exo
04cf2953f6 Document that disconnect cancels event handlers
Closes #3942.
2022-10-03 10:25:15 +02:00
Lonami Exo
ad2238e788 Shield disconnect from cancellation
Relevant issue: #3942.
2022-10-03 10:25:15 +02:00
Alfian Pangetsu
908375ac42
Fix get_running_loop usage in Python3.6 (#3941)
Closes #3939.
2022-10-02 19:05:11 +02:00
Lonami Exo
7f472ee72c Add CHAT_FORWARDS_RESTRICTED to known errors 2022-10-02 16:25:52 +02:00
Lonami Exo
d2b1c3ec5f Lower severity of some log messages during update handling
Some people were complaining that their logs were being spammed by it.
2022-10-02 16:07:14 +02:00
Lonami Exo
1cf6cf46bd Bump to v1.25.2 2022-09-27 11:32:38 +02:00
Lonami Exo
bb98f4e68c Fix get_dialogs was not filling channels pts 2022-09-27 11:31:41 +02:00
Lonami Exo
105a7a7c56 Log channel ID when getting their difference 2022-09-27 11:31:26 +02:00
Lonami Exo
fd70b5a428 Update list of known errors 2022-09-27 10:49:12 +02:00
Lonami Exo
6fcd7dff38 Bump to v1.25.1 2022-09-24 11:03:09 +02:00
Lonami Exo
346a3f0ef5 Add note on 2FA for qr_login 2022-09-21 12:28:51 +02:00
Lonami Exo
c975b566a1 Handle ServerError while getting difference
Closes #3870.
2022-09-21 12:17:24 +02:00
Lonami Exo
49bdb762c9 Re-raise unhandled errors that occur during update handling
This should help the situation in #3870.
2022-09-21 12:13:21 +02:00
Lonami Exo
a83fe46baf Document the client instance cannot be used after logout
Closes #3780.
2022-09-21 12:00:55 +02:00
Lonami Exo
17516318e6 Add a hard timeout on disconnect
Closes #3917.
2022-09-21 10:58:35 +02:00
Lonami Exo
6d02a1c6ff Update some raw API examples 2022-09-21 10:50:07 +02:00
Lonami Exo
1f42e6e32f del_surrogate for HTML inline URLs
Closes #3693.
2022-09-20 18:18:26 +02:00
Lonami Exo
ff0f9b0e8f Ignore ChannelParticipantLeft during iter_participants
Closes #3231.
2022-09-20 18:12:29 +02:00
Lonami Exo
2d4305db76 Wrap buttons typehint in Optional
Closes #3762.
2022-09-20 18:03:16 +02:00
Lonami Exo
5a17397fc7 Fix events.Album did not have chat in PM
Closes #3773.
2022-09-20 17:54:05 +02:00
Lonami Exo
d7424ccb90 Ignore aggressive parameter in iter_participants
It's broken (it causes flood wait immediately).
Closes #3787.
2022-09-20 17:35:25 +02:00
Lonami Exo
e6ebe6b334 Replace mentions of master branch with v1 2022-09-20 17:08:53 +02:00
Lonami Exo
18da855dd4 Fix get_permissions for small group chats
Closes #3811.
2022-09-20 16:17:45 +02:00
Lonami Exo
75fe90005f Manually construct reply_to for send_message result
Closes #3803.
2022-09-20 16:05:48 +02:00
Lonami Exo
363c2604df Strip 0-length message entities
Closes #3884.
2022-09-20 13:12:01 +02:00
Lonami Exo
7cac3668d6 Make custom, functions and types proper modules
This allows "from telethon.types import Message" to work.
Closes #3929.

Not entirely sure how it used to work before, perhaps
it got changed at some point but this should revert previous
behaviour.
2022-09-20 12:59:36 +02:00
Lonami Exo
2f2a9901e2 Trust pts values during apply_difference
See #3873.
2022-09-20 12:52:24 +02:00
Lonami Exo
64bc73c41e Do not remove ENTRY_SECRET during apply difference
This probably occurs with bot accounts only.
It is strange that the qts is used by bots but is missing from the
results of getDifference. This would need more investigation, but
it might just be the way this thing works.

Closes #3873.
2022-09-20 12:40:48 +02:00
Lonami Exo
243f58c331 Handle auth errors during get difference 2022-09-20 11:35:59 +02:00
Lonami Exo
06536cfb91 Recognize invite links with plus sign prefix 2022-09-20 10:57:52 +02:00
Lonami Exo
299eceb6eb Document new known RPC errors 2022-09-20 10:56:57 +02:00
Lonami Exo
50aa92ebde Handle CancelledError inside update loop
This error is not really unexpected, since the library uses it to
cancel the task during disconnect.

Closes #3921.
2022-09-14 17:11:13 +02:00
Lonami Exo
7d4424ac2b Make use of AlbumHack for all albums
Closes #3916.

The new MessageBox system is not designed with "albums come in the
same updates container" in mind (in fact, there was a note about this).

This version was also not intended to be published to PyPi, but it is,
so a workaround must be made for events.Album to remain working.

In essence, AlbumHack will always be used even if it technically did not
need to be used previously. This will cause a small delay for those
updates, but it should not be a major issue.
2022-09-14 16:53:56 +02:00
Lonami Exo
a66df977f7 Fix UpdateShort from socket was not unboxed
This was causing UpdateShorts to be sent to Raw handlers,
which in turn broke things like QR login.

Should fix #3922.
2022-09-14 15:53:11 +02:00
Shubham Kumar
935be9dd6e
Fix some MD parsing of inline URLs (#3920) 2022-09-09 21:46:06 +02:00
Lonami Exo
6e8bc0d5b9 Fix raw API docs generation 2022-08-30 13:21:17 +02:00
Lonami Exo
8b1bfcdf9c Bump to v1.25 2022-08-30 12:57:34 +02:00
Lonami Exo
48d7dbe90b Remove missed async keywords from the revert
This should've been in 7d21b40401.
This completes the revert of async sessions.
2022-08-30 12:40:05 +02:00
Lonami Exo
e87e6738b5 Revert "Add missing async keywords in SQLiteSession"
This reverts commit 0f5eeb29e7.
2022-08-30 12:32:23 +02:00
Lonami Exo
7d21b40401 Revert "Make sessions async"
This reverts commit d2de0f3aca.
2022-08-30 12:32:21 +02:00
Lonami Exo
88b2b9372d Revert "Mark certain SQLiteSession methods as async"
This reverts commit f913ea6b75.
2022-08-30 12:22:05 +02:00
Lonami Exo
44e3651adf Revert "Add workaround for SQLiteSession needing save after init"
This reverts commit 8190a92aae.
2022-08-30 12:20:20 +02:00
Tulir Asokan
d5c864597c Update to layer 144 2022-08-30 12:11:38 +02:00
Lonami Exo
df96ead0ab Also except ChannelInvalidError during get_diff
This change comes from here:
2166d913e6
2022-08-30 12:07:14 +02:00
Tulir Asokan
809a07edac Fix missing variable and assignment in reset_deadline 2022-08-30 12:05:33 +02:00
Lonami Exo
4b151fbce9 Handle ValueError during get_channel_difference 2022-08-16 12:09:21 +02:00
Lonami Exo
396594060b Fix reset_deadline was not doing its job
This was leading to a soft deadlock, always trying to get difference
but always receiving empty one and not exiting.
2022-08-11 11:04:37 +02:00
Lonami Exo
dd55e7c748 Prevent double-logging of 'timeout for updates' 2022-08-11 10:53:21 +02:00
Devesh Pal
362d06654f
Support sending 4GB files (#3891) 2022-07-28 12:30:46 +02:00
Rongrong
db3faedbfc
Add ENTITY_BOUNDS_INVALID and POSTPONED_TIMEOUT to known errors (#3887) 2022-07-25 11:52:48 +02:00
Rongrong
046e2cb605
Fix HTML/MD parser producing empty MessageEntity (#3885)
Closes #3884. The implementation is also simplified.
2022-07-25 11:11:26 +02:00
Eugene Lam
066820900d
Update to layer 143 (#3862) 2022-07-02 09:27:35 +02:00
Lonami Exo
f90cdf2ffb Fix apply_difference should not end get diff for secret if not active 2022-05-31 11:58:38 +02:00
Lonami Exo
1af6d9a873 Properly log RpcError with no parent request
This should get rid of the unexpected BufferError traceback.
2022-05-31 11:02:34 +02:00
Lonami Exo
0f5eeb29e7 Add missing async keywords in SQLiteSession 2022-05-30 13:39:08 +02:00
Lonami Exo
441fe9d076 Remove TODOs which are no longer relevant in MessageBox 2022-05-30 13:33:56 +02:00
Lonami Exo
7e0639ac57 Add getting_diff_for assertion in get_channel_difference too 2022-05-30 13:31:29 +02:00
Lonami Exo
898e279218 Assert getting_diff_for is not filled when not possible in get_difference 2022-05-30 13:29:58 +02:00
Lonami Exo
a38170d26a Assert reset_deadline is not unnecessarily called 2022-05-30 13:28:22 +02:00
Lonami Exo
6f6b207866 Better fix for reset_deadline
Follow-up to 876af8f27c.
The issue was caused because we called end_get_diff to cleanup a diff
that could not actually be started.

This also enables further cleanup later on.
2022-05-30 13:27:04 +02:00
Lonami Exo
876af8f27c Fix reset_deadline failing when the state map is empty 2022-05-30 13:04:02 +02:00
Lonami Exo
8190a92aae Add workaround for SQLiteSession needing save after init 2022-05-30 12:59:04 +02:00
Lonami Exo
378ccd17bf Call catch_up before processing updates if the user wants so 2022-05-25 15:31:39 +02:00
Lonami Exo
aa7a083444 Add missing begin_get_diff call on updatesTooLong 2022-05-25 15:28:15 +02:00
Lonami
b180b53619
Fix typo in ChannelTooLong code path 2022-05-23 16:53:34 +02:00
Lonami Exo
6005585764 Don't crash if periodic session access fails
If saving every minute or new entities fails, it's not fatal.
Other places are not checked because it is more critical for
information to be saved, such as disconnect, where we want to
crash if the session cannot be accessed.
2022-05-23 14:02:56 +02:00
Lonami Exo
06b0ae56d4 Treat invalid pts as outdated pts 2022-05-23 13:56:10 +02:00
Lonami Exo
c5bf83eb86 Remove unnecessary workaround when updating local pts 2022-05-23 13:52:32 +02:00
Lonami Exo
5a1b9daf4c Add back UpdateChannelTooLong check
Removed in the previous commit 2bcedb98.
2022-05-23 13:17:12 +02:00
Lonami Exo
2bcedb9820 Process diff.other_updates as if they are socket updates
This prevents duplicates since it contains the control flow to check for pts.
2022-05-23 13:13:39 +02:00
Lonami Exo
9dbf3443d0 Better initialization pts for new channels 2022-05-23 12:20:02 +02:00
Lonami Exo
f50b2f5d61 Handle bans when getting difference 2022-05-23 11:41:59 +02:00
Lonami Exo
dfce1f53a8 Handle PersistentTimestampOutdatedError 2022-05-23 11:10:46 +02:00
Lonami Exo
5e46b6365c Use the correct type in apply_channel_difference
This one should not change the behaviour, but it's done for consistency.
2022-05-23 10:27:57 +02:00
Lonami Exo
d5bfb71e10 Handle get_difference case for secret chats 2022-05-20 20:44:36 +02:00
Lonami Exo
af56429e78 Add repr to MessageBox types 2022-05-20 18:15:43 +02:00
Lonami Exo
dfc6d448ed Expose catch_up in client constructor and default it to False 2022-05-20 14:55:47 +02:00
Lonami Exo
3a44f56f64 Also process own updates in MessageBox 2022-05-19 16:40:32 +02:00
Lonami Exo
80685191ab Add a hack to enable StringSession.save be (a)sync 2022-05-18 14:53:04 +02:00
Lonami Exo
184984ac51 Protect against potential replay attacks
See #3753.
2022-05-18 12:24:28 +02:00
Lonami Exo
09b9cd8193 Fix initial session state load 2022-05-16 19:01:05 +02:00
Lonami Exo
c16fb0dae6 Add missing await in qr_login 2022-05-16 18:56:24 +02:00
Lonami Exo
898eb5b82f Call GetState on login to init MessageBox 2022-05-16 10:05:07 +02:00
Lonami Exo
3c7f53802f Fix saving of update state 2022-05-16 09:36:57 +02:00
Lonami Exo
0dff21a80f Add missing async in sqlite 2022-05-16 09:36:41 +02:00
Lonami Exo
7963af1d17 Add repr to updates session types for easier debugging 2022-05-16 09:36:13 +02:00
Lonami Exo
001df933a5 Disable GHA workflows temporarily 2022-05-13 17:53:20 +02:00
Lonami Exo
db7b7fde3f Actually fix references to TL in MessageBox 2022-05-13 17:46:51 +02:00
Lonami Exo
a5c3df2743 Attempt to load and save MessageBox state 2022-05-13 17:40:03 +02:00
Lonami Exo
053a0052c8 Fix references to TL in MessageBox 2022-05-13 17:39:31 +02:00
Lonami Exo
db09a92bc5 Make use of the new MessageBox 2022-05-13 13:17:16 +02:00
Lonami Exo
b5bfe5d9a1 Remove StateCache 2022-05-13 12:43:50 +02:00
Lonami Exo
f4b2fe9540 Backport v2 MessageBox 2022-05-13 12:29:58 +02:00
Lonami Exo
fdb0720fe9 Don't reset the auth_key upon receiving -404 2022-05-12 12:03:48 +02:00
Lonami Exo
f913ea6b75 Mark certain SQLiteSession methods as async
Follow-up to d2de0f3aca.
2022-05-12 11:08:18 +02:00
Tulir Asokan
ecc036c7f4 Add option to clear unread reactions in send_read_acknowledge 2022-05-12 10:40:31 +02:00
Tulir Asokan
dda696cce4 Add INVITE_REQUEST_SENT to known errors 2022-05-12 10:40:04 +02:00
Tulir Asokan
f351d5dcfd Handle expired phone codes. Fixes mautrix/telegram#326 2022-05-12 10:39:27 +02:00
Tulir Asokan
d2de0f3aca Make sessions async
SQLiteSession is not updated, don't try to use it
2022-05-12 10:36:10 +02:00
Lonami Exo
43f629f665 Update to layer 140 2022-05-12 10:21:03 +02:00
Lonami Exo
5feb210442 Add support for flags2 in the TL 2022-05-12 10:20:42 +02:00
Reinier Romero Mir
f9643bf737
Add missing async when downloading from URL (#3222) 2021-12-01 20:28:55 +01:00
Lonami Exo
bda4259815 Bump to v1.24 2021-12-01 19:08:47 +01:00
Lonami Exo
c9ecd61f7e Fix peer ID check to work with higher IDs
This commit is taken from
5f4bfe6b9b
2021-12-01 19:03:59 +01:00
Shrimadhav U K
9c796e8d73
Fix typo, add errors, and update to 133 again (#3157) 2021-12-01 19:00:29 +01:00
Lonami Exo
2e1be01ad4 Add ttl parameter to send_file 2021-09-11 11:02:19 +02:00
Lonami Exo
3f5f5dbe48 Update documentation and list of known errors
Closes #3151.
2021-09-11 11:02:10 +02:00
Shrimadhav U K
28d3d4b122
Update to layer 133 (#3155) 2021-09-11 10:54:11 +02:00
Devesh Pal
391fbab674
Implement Sticker Choosing Action (#3144) 2021-09-01 15:08:58 +02:00
Shrimadhav U K
2182e7f6f1
Update to layer 132 (#3142) 2021-08-31 15:22:56 +02:00
Yusuf_M_Thon_iD
022c1db33f
Update ChatAction to include MessageActionGameScore (#1651) 2021-08-30 17:33:46 +02:00
Lonami Exo
8c56f95252 Include full request on the cause of RPC errors
Closes #3110, fixes #3109.
2021-08-29 12:14:03 +02:00
Lonami Exo
2cb6cd5dad Change the way no_updates mode is enabled
See discussion on https://github.com/LonamiWebs/Telethon/commit/49713b2.

The problem with the automatic approach is that some scripts may do
some "fancier" things with the way they register updates, so it was
prone to failure (a handler could be added but since the last request
was without updates, nothing would be received).

This new approach is a bit more annoying to opt-into but also more
explicit.
2021-08-29 12:14:03 +02:00
Devesh Pal
befba11657
Add support for scheduled messages in iter_messages (#3127) 2021-08-29 11:53:06 +02:00
painor
828cf2dcad
Include "chat" attribute in processing entities (#3133)
Requests like checkChatInvite return a chatInviteAlready, which has
a "chat" attribute similar to the "user" attribute other requests have.
2021-08-29 11:50:48 +02:00
Devesh Pal
9830c4e02b
Add Button.buy and Button.game (#3141) 2021-08-29 11:49:52 +02:00
Shrimadhav U K
0a4b827d8e
Document new known RPC errors (#3137) 2021-08-28 00:18:37 +02:00
Newbyte
2ea3153cd5
Update docs to reflect current length of login codes in test servers (#3140) 2021-08-28 00:18:22 +02:00
Lonami Exo
1e6be28e4b Fix pin_message not returning Message on PMs 2021-08-22 13:46:26 +02:00
Lonami Exo
49713b2784 Wrap requests in InvokeWithoutUpdatesRequest if no event handlers
Closes #1270.
2021-08-22 13:38:54 +02:00
Lonami Exo
9285e50c63 Handle non-user bans when iterating banned participants
Closes #3105.
2021-08-22 13:24:02 +02:00
Lonami Exo
bc6bcd31ad Fix InlineQuery.event.geo returning None
Closes #3136.
2021-08-22 13:09:57 +02:00
Shrimadhav U K
6a1f29d953
Add new known RPC errors (#3134) 2021-08-20 11:49:12 +02:00
ZubAnt
45ed6658fe
Fix add_admins property of custom.ParticipantPermissions (#3132)
Closes #3131.
2021-08-15 08:01:25 +02:00
penn5
e546ae2f85
Allow per-request flood sleep threshold selection (#3123) 2021-08-06 08:13:34 +02:00
Devesh Pal
e5599c178b
Expose more raw API params in friendly methods (#3104) 2021-08-05 10:54:07 +02:00
Devesh Pal
ad55b945c1
Add comment_to to InlineResult-click method (#3118) 2021-08-03 18:34:10 +02:00
alexkoay
196cef66fd
Fix typehint for callback in UpdateMethods (#3119) 2021-08-03 18:33:46 +02:00
Joshua Coales
e2d97b44c5
Update docs to have fewer grammatical mistakes (#3121) 2021-08-03 18:33:17 +02:00
Devesh Pal
79866750d2
Add new known RPCErrors (#3114) 2021-07-20 23:04:09 +02:00
Shrimadhav U K
3570953d14
Update to layer 131 (#3112) 2021-07-16 21:01:08 +02:00
Lonami Exo
06afd04b07 Update to version 1.23 2021-07-09 20:18:22 +02:00
Lonami Exo
2df1dd7215 Don't call getFullChannel during iter_participants unless necessary
This should reduce the floodwaits of this request by a lot.
2021-07-09 20:11:21 +02:00
Lonami Exo
1e09e133e3 Document new known RPC errors
Courtesy of #3097.
2021-07-09 20:11:21 +02:00
Shrimadhav U K
ecfc6ae87d
Add pm_oneside parameter in pin_message (#3095) 2021-07-09 19:50:47 +02:00
Shrimadhav U K
7763939e7d
Update to layer 130 (#3098) 2021-07-09 19:47:13 +02:00
MiyukiKun
249670827c
Change manage_call permission to default to None (#3093) 2021-07-02 21:42:57 +02:00
Devesh Pal
42bfc7bb3f
Fix inline force_document and new known RPC errors (#3084)
This should fix inline video notes.
2021-06-24 14:19:01 +02:00
Ivanzzzc
417bfcd36e
Fix encoding of QR login URL (#3082)
Closes #3081.
2021-06-20 15:57:05 +02:00
Lonami Exo
2052b502c8 Update to v1.22 2021-06-19 19:08:02 +02:00
Lonami Exo
7c1ad0cadb Document need to AcceptTermsOfService in apps
Closes #3040.
2021-06-19 18:32:23 +02:00
Lonami Exo
9d899e3dab Add EntityCache.clear
Closes #3073.
2021-06-19 18:32:23 +02:00
Lonami Exo
3f185aada2 Ignore IPv6 setting if there's no matching DC
May close #3075 (assuming this is what was happening).
It's better to return some DC rather than crashing.
2021-06-19 18:32:23 +02:00
BelgenOp
37b81c6418
Support retracting poll votes on message click without option (#3080) 2021-06-19 18:32:11 +02:00
Anonymous
7c5efee1de
Update to layer 129 and other additions/enhancements (#3074)
* Apply code corrections for the new layer types.
* Support not passing `user` to `get_permissions`.
* `download_profile_photo` now supports `MessageService`.
* `thumb` in send and edit message.
* Document new known errors.
2021-06-15 22:57:32 +02:00
BelgenOp
6b53d45ce2
Add attributes, supports_streaming to send_message and edit_message (#3066)
Closes #3047.
2021-05-31 15:36:40 +02:00
Lonami Exo
63f24d2282 Add new known RPC errors and update docs 2021-05-30 18:00:27 +02:00
Lonami
3d350c6087
Don't check if offset is divisible by limit if limit is None
Should fix #3058.
2021-05-16 22:27:29 +02:00
Anonymous
85381713b2
Document new known RPC errors (#3057) 2021-05-15 10:49:55 +02:00
blank X
f6a0f5f979
Make offset divisible by limit (#3042) 2021-05-14 08:11:54 +02:00
Anonymous
d44928c27b
Change outdated reference to archive with edit_folder (#3052) 2021-05-09 15:33:28 +02:00
Julian Haupt
08a11eeacf
Fix get_sender when using it on a ChannelForbidden (#3053)
Closes #3051.
2021-05-09 15:33:01 +02:00
Alisa Sireneva
b2c26a53ef
Document new known RPC errors (#3044) 2021-05-05 20:38:43 +02:00
Lonami Exo
319b6283a9 Update install/test server docs and add new known RPC error 2021-04-21 19:56:57 +02:00
FujiApple
5f16434346
Fix Message._needed_markup_bot not returning bot in some cases (#3030) 2021-04-19 08:20:03 +02:00
Joshua Coales
3001b620ec
Improve exception clarity for message parsing failure (#3029) 2021-04-17 19:10:33 +02:00
Non
a376faa3a8
Fix MD5_CHECKSUM_INVALID for small files with custom key/iv (#3024)
Closes #3023.
2021-04-10 21:46:14 +02:00
Lonami Exo
4b16183d2b Audio metadata may have performer under artist
Closes  #3008.
2021-03-31 10:57:20 +02:00
Lonami Exo
5b91adf62d Update documentation
Closes #1733.
2021-03-31 10:54:18 +02:00
Lonami Exo
2fbf850841 Update to layer 126
May help with #3010
2021-03-31 10:52:35 +02:00
Lonami Exo
e5a5ac5943 Remove sched_to_message special-case when mapping msgs
May fix #3012.
2021-03-31 10:30:24 +02:00
Lonami Exo
f326769fa8 Add support for messages to get_stats 2021-03-20 20:20:36 +01:00
Lonami Exo
4d3ff0e175 Revert "Use tgcrypto if available (#1715)"
This reverts commit 42cc9e61fb.

tgcrypto was made for Pyrogram, and seeing it used elsewhere
without much credit "hurts" the author. I personally do not endorse
its use, hence the lack of attention or notes in the documentation.

People who still want to benefit from the speed boost should go
out of their way to discover, install and patch Telethon's aes.py
module instead, all while complying with the respective license
(another reason to avoid said code in Telethon, which is under the
much more permissive MIT license).

People using tgcrypto for anything other than Pyrogram will do so
knowing full-well that this was not the library's intended usage.
2021-03-20 17:20:33 +01:00
Lonami Exo
1cef9173a0 Update to version 1.21.1 2021-03-16 08:24:00 +01:00
Lonami Exo
b06f496a27 Don't treat False field as flag omission
8724949b54 was only half the story.
2021-03-16 08:21:04 +01:00
Lonami Exo
58013f4f44 Fix file.width and .height not working on Photo 2021-03-15 22:36:46 +01:00
painor
ad0307fda6
Add password support for quart example (#1732) 2021-03-15 22:25:06 +01:00
Lonami Exo
3d6a2bb945 Update to version 1.21 2021-03-14 11:31:03 +01:00
Lonami Exo
bdc324760d Move message.out patching in self-chat to Message
May fix #1684.
2021-03-14 11:16:59 +01:00
Lonami Exo
eba95ebd07 Fix delete_dialog on chats
Closes #1727.
2021-03-14 11:05:47 +01:00
Lonami Exo
6f2f8ae69f Remove chat hack from events.UserUpdate
Turns out there was a specific update for channels.
2021-03-14 11:03:03 +01:00
Lonami Exo
8f46f704b1 Update to layer 125
Closes #1728, should close #1724.
2021-03-14 10:58:33 +01:00
Tulir Asokan
0ad9b1375e
Make input entity errors less useless (#1726) 2021-03-14 01:32:01 +01:00
Lonami
52ae9f09ce
Fix _get_input_notify on TLObjects
Closes #1725.
2021-03-13 19:06:19 +01:00
Lonami Exo
a1f91d6eb8 (De)serialize user_id as u32
https://t.me/BotNews/57.
2021-03-09 20:10:31 +01:00
Lonami Exo
bfa7e4ca37 Support clicking buttons that require password
Should close #1716.
2021-03-07 16:36:26 +01:00
Lonami Exo
3ee94bdc5e Update known errors and error message
Closes #1713
2021-03-07 16:09:47 +01:00
Lonami Exo
8724949b54 Don't omit False flag values from serialization 2021-03-07 16:05:09 +01:00
igerzog
42cc9e61fb
Use tgcrypto if available (#1715) 2021-03-02 21:38:02 +01:00
Lonami Exo
d9691c9342 Update to version 1.20 2021-02-27 16:24:39 +01:00
Lonami Exo
4c771bf2af Fix setting logout result was not checking for future cancellation 2021-02-27 15:14:44 +01:00
Lonami Exo
292a36f760 Handle DestroySessionRes
Should close #1706.
2021-02-27 15:13:53 +01:00
Lonami Exo
a955138021 Fix invoking requests ordered
Closes #1709.
2021-02-27 15:03:05 +01:00
Lonami Exo
b475a2ecc6 Add a new docs page for Chats vs Channels 2021-02-23 20:58:36 +01:00
Lonami Exo
175b30faf8 Add new event types to AdminLogEvent 2021-02-23 20:10:51 +01:00
Lonami Exo
0d05d0d8f5 Update message to include ttl_period 2021-02-23 19:42:09 +01:00
Andrew Lane
2c2a07d02f
Update to layer 124 (#1708) 2021-02-23 13:04:50 +01:00
Lonami Exo
0e8bd8248c Fix patched module was never automatically imported
Closes #1701. It has to be imported late in the process of
`import telethon` for its side-effects.
2021-02-14 00:26:04 +01:00
Lonami Exo
ff3c21c805 Update file.size to reflect the size of the largest thumbnail
This way we avoid relying on the order of the thumbnails, and just
pick the largest.
2021-02-13 22:52:27 +01:00
Lonami Exo
b102f1f345 Handle progressive size in _photo_size_byte_count 2021-02-13 22:49:03 +01:00
Lonami Exo
73b9de2085 Correctly sort PhotoSizeProgressive thumb size 2021-02-13 22:47:34 +01:00
Lonami Exo
b0158b3f65 Fix download of PhotoSizeProgressive
Closes #1700.
2021-02-13 22:45:12 +01:00
Lonami Exo
75db9f70df Update proxy docs and license year 2021-02-12 17:56:46 +01:00
Lonami Exo
8f0de3d285 Fix TypeNotFoundError was not being propagated
Closes #1697. This would cause deadlocks, as the request future
would never be resolved, so await would wait forever.
2021-02-11 19:27:57 +01:00
Lonami Exo
845fe88451 Fix definition typo in patched module 2021-02-10 20:21:16 +01:00
Lonami Exo
9a47fdc1ee Move Message redefinitions back to patched
Fixes #1695. This matches the older behaviour better, although the
patched module is now written manually.
2021-02-10 20:18:29 +01:00
Lonami Exo
23041f398b Fix messages.search accidentally being used over getHistory
Introduced by 668dcd52ca (this commit
did change a lot more than it should have); the condition for search
was never updated to account for the non-None value.

Closes #1693.
2021-02-08 22:56:27 +01:00
Lonami Exo
acb066ad2e Fix patched import 2021-02-06 12:51:01 +01:00
Lonami Exo
b85f50e314 Try to fix new custom.Message again 2021-02-06 12:41:33 +01:00
Lonami Exo
79f6da2dac Update to layer 123 again 2021-02-06 12:35:53 +01:00
Lonami Exo
abe4b8d5b0 Fix docs and imports for custom.Message 2021-02-05 20:52:08 +01:00
Lonami Exo
0997e3fa9f Remove _log_exc workaround and NullHandler
It was added back in bfc408b probably due to a misunderstanding of
https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library.

The default behaviour of logging WARNING and above is good and
desirable (hiding unhandled exceptions in update handlers by default
was a big, accidental mistake). NullHandler is used to *prevent*
this good default, so it shouldn't be used in the first place.
2021-02-02 20:47:02 +01:00
Lonami Exo
9a0e030db8 Add patched module back for compatibility 2021-02-02 19:12:22 +01:00
Lonami Exo
b88ec4b814 Print unhandled errors to stderr if logging is not configured
This should mitigate "the code doesn't work but there are no errors"
situations. Users not wanting this behaviour can configure logging
with a high-enough level that won't print anything, or set a filter.
2021-01-30 13:47:28 +01:00
Lonami Exo
4cc9645d76 Fix send_message not forwarding comment_to to send_file 2021-01-30 10:39:45 +01:00
Lonami Exo
8c38d7fb0e Add missing parenthesis 2021-01-30 10:32:42 +01:00
Lonami Exo
a12b49fd40 Change error mapping to be case insensitive 2021-01-29 20:19:07 +01:00
Lonami Exo
584e2b3743 Update list of RPC errors 2021-01-28 21:18:14 +01:00
Lonami Exo
ea57db7aad Add comment_to parameter to more easily post comments 2021-01-28 21:05:20 +01:00
Lonami Exo
6f7640af18 Fix utils.resolve_id
…assuming get_peer_id is correct, as changed by 0d8497b.
2021-01-28 20:01:46 +01:00
Lonami Exo
055643bd01 Fix type hinting for custom.Message 2021-01-28 19:58:03 +01:00
Lonami Exo
4e73577d59 Update to layer 123 2021-01-28 19:48:01 +01:00
Lonami Exo
2117f8f54b Update to v1.19.1 2021-01-26 21:46:42 +01:00
Lonami Exo
320ab75818 Respect exif metadata when resizing photos
Closes #1663.
2021-01-26 21:44:11 +01:00
Lonami Exo
9a6bc5ae72 Update defaults and docs for video attributes
Closes #1679.
2021-01-26 21:10:21 +01:00
Lonami Exo
ad4c49aa18 Fix global message search 2021-01-26 21:07:44 +01:00
Googleplex
a886d609d9
Support sending album with URL photos (#1681)
Fixes #1408.
2021-01-25 08:31:25 +01:00
Lonami Exo
65bf0e4c45 Add missing importç 2021-01-24 01:38:44 +01:00
Lonami Exo
fa99f6a1af Try to handle TimeoutError during file download 2021-01-24 01:36:10 +01:00
Lonami Exo
b1d6bd564e Fix several typos
Closes #1674.
2021-01-20 18:50:45 +01:00
Lonami Exo
de7cf03ba7 Stop storing asyncio loop in TelegramClient
The loop parameter was ignored because it shouldn't be used, but
the fact it still stored the current loop on creation messes up
with asyncio.run.
2021-01-18 22:59:19 +01:00
Shrimadhav U K
3ddb0a3903
Fix thumbnail for TDesktop and Telegram X users (#1673) 2021-01-17 17:31:26 +01:00
Lonami Exo
00aa0a4bf1 Avoid duplicate del in Conversation._on_read 2021-01-14 23:05:49 +01:00
Lonami Exo
cd51c9e47c Get rid of the patched/ module
This may fix #1669.
2021-01-14 22:56:55 +01:00
Lonami
c0738a7ae1
Trigger GitHub Actions workflow on PRs too 2021-01-12 20:30:31 +01:00
Lonami
4bf1d67eba
Fix resolve_invite_link in Python 3.6 2021-01-12 20:04:34 +01:00
painor
c0ed709adf
Add new format to resolve invite link (#1670) 2021-01-12 19:50:27 +01:00
Lonami Exo
82d25a7e52 Fix payment example using the wrong line endings 2021-01-05 20:05:42 +01:00
Lonami Exo
3150726f32 Fix tiny documentation nits
Closes #1659.
2021-01-05 20:03:16 +01:00
Lonami Exo
b192c3e6a3 Update to v1.19 2021-01-05 19:58:41 +01:00
Lonami Exo
3df4807fb9 Fix ChatAction.user_left was considered as user_kicked
Closes #1660.
2021-01-02 12:24:32 +01:00
Lonami Exo
d0ee3c3a56 Return the service message produced when kicking somebody
Helps with #1664.
2021-01-02 12:13:53 +01:00
Lonami Exo
acd4c8648e Update layer and known errors again 2020-12-23 20:11:16 +01:00
Lonami Exo
4b6c69ac1e Update docs and list of errors
Includes the changes of #1654.
2020-12-19 20:33:20 +01:00
Lonami Exo
dd00829f1e Ignore PhotoPathSize thumbnail sizes
Closes #1655.
2020-12-19 19:42:07 +01:00
Andrew Lane
5011747f1f
Update to layer 122 (again) (#1650) 2020-12-14 18:50:55 +01:00
Lonami Exo
ee0fc5cc29 Fix AttributeError on reconnect with no retries
Closes #1646.
2020-12-11 21:43:39 +01:00
Lonami Exo
becfe2ce7a Start reconnect if a second ping is sent without a pong for the first
May help with #1564.
2020-12-11 17:18:25 +01:00
Lonami Exo
0a4d54fca4 Update to layer 122
Closes #1645.
2020-12-11 16:55:49 +01:00
Lonami Exo
1cd11391c4 Unconditionally disconnect exported senders on user disconnect
Borrowed senders are not disconnected immediately, but after a while.
If a borrow was used recently but the user requested the main client
to disconnect, those borrows "shouldn't" disconnect because they were
used recently. However, if the user requests a disconnect, they mean
that EVERYTHING should be disconnected, even if the borrows are recent.

This actually gets rid of warnings about send/recv tasks being destroyed,
which partially addresses #1634. That issue may still have more causes
though.
2020-12-11 16:30:12 +01:00
penn5
44aca29057
Favour text parameter when editing inline messages (#1643) 2020-12-08 10:06:19 +01:00
Lonami Exo
0b0a1dc6a1 Add new known errors to the error list 2020-12-04 20:43:47 +01:00
x0x8x
12380207ba
Add admin log event.has_left (#1642) 2020-12-04 14:51:15 +01:00
Lonami Exo
2439404ad1 Include service messages for new pins in ChatAction again 2020-11-28 16:49:39 +01:00
Ali Alaee
23fc38f7c9
Fix crypto imports on macOS Big Sur (#1639) 2020-11-28 15:54:52 +01:00
Lonami Exo
e3a194acb4 Add set_proxy to the method summary 2020-11-28 13:07:36 +01:00
Lonami Exo
66a508a174 Update to v1.18 2020-11-28 12:17:25 +01:00
Lonami Exo
f2f43336c6 Always use python-socks when available
Relying on Python 3.6 or above to be installed to unconditionally
use this library would break user's code, because this is a new
optional dependency that users may not have installed.

Instead, always use the new library when available, which should
work better than pysocks because it natively supports asyncio.
2020-11-28 12:09:46 +01:00
vegeta1k95
ab3c5acf9a
Add client.set_proxy() (#1632) 2020-11-14 14:01:59 +01:00
Lonami Exo
9c87598950 Don't include *Empty entities in returned dialogs 2020-11-13 10:59:53 +01:00
Lonami Exo
c924365e24 Handle ChatEmpty in utils.get_peer 2020-11-13 10:59:53 +01:00
Lonami
46ee8e86c6
Fix conv.wait_event no longer raising Timeout
Should properly fix #1618.
2020-11-13 09:53:00 +01:00
Lonami Exo
ab9035acd2 Make large dates wrap around
Closes #1629.
2020-11-12 15:25:08 +01:00
Lonami
96a535fe4a
Merge replace PySocks with python-socks for Python >= 3.6 (#1627) 2020-11-10 11:18:00 +01:00
Serhii Dylda
59da53ec48 Fix typing once again 2020-11-09 20:22:22 +01:00
Serhii Dylda
a68800b3f0 Remove unnecessary if clause 2020-11-09 20:09:08 +01:00
Serhii Dylda
38d8a54cc1 Fix protocol typing for python-socks. 2020-11-09 20:05:09 +01:00
Serhii Dylda
633986cfa6 Replace PySocks with python-socks for Python >= 3.6
See discussion at (https://github.com/LonamiWebs/Telethon/pull/1623)

Small fixes for `local_addr` argument.
2020-11-09 19:59:54 +01:00
Alekseev Svyatoslav
c4cbead25b
Remove using deprecated as_album kwarg (#1621) 2020-11-07 22:19:50 +01:00
Richard
ba3a090a80
Update send_file to support grouping any file type (#1620) 2020-11-07 17:32:00 +01:00
Lonami Exo
e1d2c81dca Parse part of newer bot file IDs for photos
Helps with #1613.
2020-11-07 12:46:46 +01:00
Lonami Exo
0d8497bf3b Correct and simplify the way channel IDs are marked
Closes #1359.
2020-11-07 12:18:55 +01:00
Lonami Exo
a6781c8e34 Don't cache SLOW_MODE_WAIT in _flood_waited_requests
Closes #1600.
2020-11-07 12:09:00 +01:00
Lonami Exo
08d5bfcbd0 Fix conv.wait_event not clearing timed out events
Closes #1618.
2020-11-07 12:06:10 +01:00
Lonami Exo
b02a22eaa3 Fix .get_buttons failing for some messages sent by the bot
Closes #1619.
2020-11-07 11:59:56 +01:00
Lonami Exo
e4a6ec40cd Update error documentation, summary and license year 2020-11-05 10:49:34 +01:00
x0x8x
78514110de
Update errors.csv (#1609) 2020-11-05 10:40:44 +01:00
hematogender
b6fe4b8fec
Fix get_display_name not handling ChatForbidden (#1617)
Closes #1616.
2020-11-04 20:28:04 +01:00
vegeta1k95
39e899294f
Fix unhandled ValueError inside _dispatch_update() task (#1615) 2020-11-04 09:58:20 +01:00
Lonami Exo
64d751a397 messages.search from_user may now be a non-User 2020-10-31 11:41:37 +01:00
Lonami Exo
935ee2242d Add method to unpin messages 2020-10-31 11:31:09 +01:00
Lonami Exo
9e3cb8180b Update ChatAction to handle new pin updates 2020-10-31 11:21:38 +01:00
Lonami Exo
d83c154f8d Partial update to layer 120 2020-10-30 20:06:59 +01:00
Xiretza
353b88ea5a
Actually exclude tests from setup.py installation (#1612) 2020-10-27 12:31:31 +01:00
Lonami Exo
4ce2c0017a Somewhat improve packaging situation (#1605) 2020-10-25 10:50:12 +01:00
Lonami Exo
e7f174cdc8 Fix search with offset_date causing infinite recursion
Bug introduced by 668dcd5. Closes #1606.
2020-10-25 10:33:36 +01:00
Lonami Exo
aac4d03a70 Cleanup .gitignore to contain only what's needed 2020-10-25 10:26:06 +01:00
Lonami Exo
7790307595 Remove the (out of date) .nix files from the repo 2020-10-24 12:04:36 +02:00
Lonami Exo
62467b6318 Fix yet another typo
Never make commits in a rush from your phone.
2020-10-23 21:27:39 +02:00
Lonami Exo
60c5d0d8f4 Fix up typo from last commit 2020-10-23 21:24:51 +02:00
Lonami Exo
1a2e09487c Fix utils.get_peer not handling Self in get_messages 2020-10-23 19:02:43 +02:00
Lonami Exo
44e2ef6c79 Don't error when failing to extract response messages 2020-10-23 11:02:30 +02:00
Lonami Exo
e5476e6fef Add utils.split_text to split very large messages 2020-10-23 10:57:45 +02:00
Lonami Exo
d9ddf8858e Add missing local_addr to proxy connection, bump version
Bug introduced by #1587.
2020-10-22 10:13:29 +02:00
Lonami Exo
f450682a22 Document BOT_DOMAIN_INVALID 2020-10-21 09:32:29 +02:00
Lonami Exo
7ed5b4dfbe Explain what happens when a button is pressed in the docs 2020-10-19 10:49:50 +02:00
Qwerty-Space
d56b27e570
Fix several minor typos (#1603) 2020-10-18 21:11:59 +02:00
Lonami Exo
4db51dff8a Update to v1.17 2020-10-18 14:11:52 +02:00
Lonami Exo
94ce3b06eb Add missing raw API fields to Message and re-order them
Keeping them in order is important to easily change them when new
things are added so that we don't miss them again on another update.
2020-10-18 13:10:37 +02:00
Lonami Exo
1311b9393c Move alternative libraries to the wiki
It doesn't make sense to track what happens to Telegram's ecosystem
in the repository of a specific library. The wiki is better suited
for this and can be trivially updated by anyone, allowing it to better
evolve.
2020-10-16 11:00:14 +02:00
Lonami Exo
5952a40c6d Update iter_messages to support fetching channel comments
Closes #1598.
2020-10-16 10:39:02 +02:00
Lonami Exo
4e1f582b17 Call sign_in during sign_up if needed to send the code 2020-10-15 11:43:35 +02:00
Lonami Exo
3ff09f7b91 Use inline result mime to infer the result type 2020-10-15 11:04:54 +02:00
Lonami Exo
312dac90a3 Improve inline result documentation with more examples 2020-10-15 10:42:40 +02:00
Lonami Exo
9c5b9abb93 Fix sending of documents in inline results 2020-10-15 10:40:41 +02:00
Lonami Exo
7c3bbaca2a Support not including the media from inline results in the msg 2020-10-15 10:40:19 +02:00
Lonami Exo
15f7c27bce Fix .photo()/.document() inline results excluding media from msg 2020-10-15 09:29:19 +02:00
Andrew Lane
7de1c0e237
Document two new RPC errors (#1591) 2020-10-13 10:50:05 +02:00
Lonami Exo
adf52a1b74 Expose entity parameter in client.inline_query
Some bots, such as @gamee, use this to determine the type of results
to return (and "disable" themselves in channels).
2020-10-11 16:59:48 +02:00
vegeta1k95
d0faaa2ead
Fix internal get_me() was not expecting network errors (#1594) 2020-10-11 09:33:05 +02:00
Lonami Exo
61b0f09e1d Fix iter_messages(from_users='me') 2020-10-09 21:14:31 +02:00
Lonami Exo
e28fbc6678 Fix ChatAction check for self-user joining a chat 2020-10-07 10:40:34 +02:00
Lonami Exo
026c992395 Don't try to reconnect when authkey is invalid 2020-10-07 10:40:34 +02:00
Lonami Exo
5722ba8306 Revert add_admins property logic since it differs from the rest 2020-10-07 10:40:34 +02:00
Stefan
d2756cf68f
Add support for local_ip address binding (#1587) 2020-10-07 10:03:19 +02:00
khoben
ce71b3293b
Support Message.click() for polls (#1583) 2020-10-07 09:21:33 +02:00
Lonami Exo
05af5d0d74 Avoid redundant code in ParticipantPermissions 2020-10-06 11:14:16 +02:00
Lonami Exo
cf1645b598 Improve documentation for ParticipantPermissions 2020-10-06 11:14:16 +02:00
Lonami Exo
7f61b92f81 Add anonymous permission to edit_admin and get_permissions 2020-10-06 11:14:16 +02:00
Nick80835
ce120cba13
Fix get_permissions in chats and when using self user (#1584) 2020-10-05 19:21:07 +02:00
Lonami Exo
09f4c5c708 Only reset auth_key on error -404
This error is "auth key not found", and the authorization key should
probably not be reset on other error codes. This might address #1457.
2020-10-05 14:08:21 +02:00
Lonami Exo
185a93a105 Expect BufferError during automatic reconnect
This seems to occur whe the Telegram servers are dying and logging
everyone out.
2020-10-05 14:07:11 +02:00
Lonami Exo
20606b3a71 Fix from_users filter not accounting for None from_id 2020-10-05 14:01:50 +02:00
Lonami Exo
cb92a40156 Add additional asserts to debug issue with peer empty channels 2020-10-05 13:58:04 +02:00
Lonami Exo
52a247c156 Update documentation to include the new friendly method 2020-10-05 10:52:42 +02:00
Lonami Exo
bb3ccca333 Fix Python 3.5 compatibility issue 2020-10-05 10:50:47 +02:00
Lonami Exo
180105a965 Follow PEP 518 2020-10-05 10:47:46 +02:00
apepenkov
3e188d0344
Add missing check for permissions.is_creator (#1578) 2020-10-03 17:16:10 +02:00
kolay
fc765f6014
Add new get_permissions method (#1575)
Closes #1574.
2020-10-03 16:59:54 +02:00
Tulir Asokan
bf29cddbc9
Add parameter to pass raw entities when sending message (#1577) 2020-10-02 22:06:48 +02:00
Lonami Exo
4321153b06 Correctly emulate old to_id behaviour 2020-10-02 10:23:04 +02:00
Lonami Exo
e24c49f5be Fix patching of message.out for self-chat 2020-10-02 10:22:38 +02:00
Lonami Exo
53920a1568 Remove handling chat peer discrepancy in NewMessage 2020-10-02 10:04:51 +02:00
Lonami Exo
18f70b3bac Improve PEER_ID_INVALID description 2020-10-01 21:27:03 +02:00
Lonami Exo
5c93ea8019 Fix from_id/sender_id value on message updates 2020-10-01 21:22:27 +02:00
Lonami Exo
572229e536 Add aliases to access new msg fields with old names 2020-10-01 20:37:07 +02:00
Lonami Exo
522681f463 Handle UserEmpty in utils.get_peer
Closes #1552.
2020-10-01 14:02:54 +02:00
Lonami Exo
5c5cee16d9 Lower log severity when receiving empty messages 2020-10-01 13:22:38 +02:00
Lonami Exo
67b87a0ea0 Evict old cached usernames in case of collision 2020-10-01 13:20:29 +02:00
Lonami Exo
233daafd96 Fix global search would fail if last message had no peer 2020-10-01 13:18:54 +02:00
Lonami Exo
4683e83287 Add set -e to update-docs.sh
This should prevent accidentally comitting docs in master
2020-10-01 12:23:38 +02:00
Lonami Exo
668dcd52ca Update global search to properly use offset_rate 2020-10-01 12:23:34 +02:00
Tulir Asokan
8ce7e776c1 Add option to raise last error instead of generic ValueError (#1571) 2020-10-01 12:23:10 +02:00
Lonami Exo
d5e4398ace Adapt the rest of the library to layer 119 2020-10-01 12:22:55 +02:00
Lonami Exo
62737c1caf Partially upgrade to layer 119 2020-10-01 09:17:18 +02:00
Lonami Exo
10b2b60415 Fix requests were not re-enqueued if sending failed 2020-09-29 21:07:38 +02:00
Lonami Exo
c864ef7e16 Refetch msg if fileref expires while downloading docs
Closes #1301.
2020-09-24 10:03:28 +02:00
Lonami Exo
75fbd28d3e Add a workaround for sometimes-missing photos from channels 2020-09-22 11:08:17 +02:00
Lonami Exo
2c9d43d600 Move most of the code in assistant to the plugins repo
The assistant example will now simply be the "core" that initializes
the rest of plugins, allowing for more updates to the *bot* without
cluttering Telethon's git history.
2020-09-17 11:27:50 +02:00
Lonami Exo
219b4ecb77 Abstract away treating a file as a stream
Makes upload_file cleaner (context manager instead of try-finally)
and helps keep the logic "we may own this stream and need to close
it or not" separated.

It can be overengineered to allow nesting the same instance in
context managers and also provide implementations for synchronous
context managers but it adds quite a bit of unnecessary complexity
for the time being. YAGNI.
2020-09-14 16:20:44 +02:00
Daniil
9ec5707c32
Add more info on invalid sticker error (#1558) 2020-09-13 16:29:48 +02:00
yash-dk
1d6fd7898a
Consider all reconnect attempts as retrying (#1557)
This means that a value of 0 retries will no longer try to reconnect.
2020-09-13 09:43:01 +02:00
apepenkov
2a114917f1
Fix AlbumHack in combination with events.Raw (#1555) 2020-09-10 16:25:44 +02:00
Tanya Degurechaff
1afb5b95e3
Update init params to match those of tdesktop (#1549) 2020-09-10 14:52:25 +02:00
daaawx
8cbaacabdb
Add missing word in docs (#1538) 2020-09-10 14:51:31 +02:00
Allerter
1ed0f75c49
Support extracting metadata from bytes and stream objects (#1547)
This should enable more accurate uploads of in-memory files.
2020-09-08 00:20:37 +02:00
Daniil
02b8f1d007
Document STICKER_DOCUMENT_INVALID error (#1537) 2020-08-29 11:41:50 +02:00
Lonami Exo
daec282cdf Update to layer 117 2020-08-24 12:56:20 +02:00
Lonami Exo
0c9d0db730 Update to v1.16.4
v1.16.3 was accidentally released without the intended bug-fixes.
2020-08-24 12:54:56 +02:00
Lonami Exo
06f3dc3053 Revert "Update to layer 117"
This reverts commit 26ff92caa9.

Layer changes only go in minor releases, and the commit history is
linear, so temporarily revert to release a new patch version.
2020-08-24 12:53:43 +02:00
penn5
1a9accbe5d
Fix warning when using formatted phones in start (#1532) 2020-08-24 10:53:29 +02:00
Pascal Jürgens
bde38fb748
Improve description for FILE_ID_INVALID (#1531) 2020-08-23 19:39:29 +02:00
Lonami Exo
bc799fd82c Remove usage of the main group username in the docs 2020-08-23 11:45:45 +02:00
Lonami Exo
accb2142e7 Document new known RPC error 2020-08-23 11:37:59 +02:00
Lonami Exo
26ff92caa9 Update to layer 117 2020-08-15 12:54:05 +02:00
Lonami Exo
73109eb819 Add a workaround for channels that claim have no photos but do 2020-08-13 15:13:29 +02:00
Lonami Exo
e00496aa63 Update to v1.16.2 2020-08-11 23:16:09 +02:00
Lonami Exo
e19aa44d5c Sort thumbs to ensure -1 is largest
Closes #1519.
2020-08-11 23:14:31 +02:00
Lonami Exo
0cefc73448 Support both str and VideoSize as thumb on download_media 2020-08-11 22:31:12 +02:00
Lonami Exo
3c56a6db4d Update to v1.16.1 2020-08-10 16:19:31 +02:00
Lonami Exo
9a0d6b9931 Don't set force_file on force_document with images
Otherwise, Telegram won't analyze the image and won't add it the
DocumentAttributeImageSize, causing some bots like t.me/Stickers
to break.

Closes #1507.
2020-08-10 16:09:39 +02:00
Lonami Exo
ddeefff431 Add a warning when trying to connect to a different account
Closes #1172, and also fixed a typo.
2020-08-08 17:49:00 +02:00
conetra
958698bba7
Remove square bracket around IPv6 addresses (#1517) 2020-08-08 13:16:01 +02:00
Lonami Exo
1d71cdc9e0 Support autocast of polls into input media when possible
Closes #1516.
2020-08-07 16:03:50 +02:00
Lonami Exo
241c6c4ac8 Auto-retry on interdc call (rich) error 2020-08-03 12:35:25 +02:00
Lonami Exo
cb6ffeaabd Slightly improve docs heuristics for video files 2020-07-28 19:46:50 +02:00
Lonami Exo
34861ad1bc Update to v1.16 2020-07-28 18:12:24 +02:00
Lonami Exo
012cae051b Add a script to update online documentation 2020-07-26 13:50:48 +02:00
Lonami Exo
f18ab08334 Add new friendly method to get channel stats 2020-07-26 13:45:30 +02:00
Lonami Exo
b1ea7572dd Document new rpc errors in layer 116 2020-07-26 13:45:19 +02:00
Lonami Exo
e12f6c747f Extend use of force_document to work on files
This allows .webp files to be sent as documents and not stickers.
2020-07-26 13:03:59 +02:00
Lonami Exo
95ea2fb40c Remove uses of gif external
Since it has been removed in layer 116.
2020-07-26 12:59:10 +02:00
Lonami Exo
57b38b24dd Update to layer 116 2020-07-26 12:55:29 +02:00
Lonami Exo
ec8bb8a06a Document PRIVACY_TOO_LONG 2020-07-25 18:42:12 +02:00
Lonami Exo
1c3e7dda01 Avoid explicitly passing the loop to asyncio
This behaviour is deprecated and will be removed in future versions
of Python. Technically, it could be considered a bug (invalid usage
causing different behaviour from the expected one), and in practice
it should not break much code (because .get_event_loop() would likely
be the same event loop anyway).
2020-07-25 18:39:35 +02:00
Lonami Exo
de17a19168 Improve upload_file by properly supporting streaming files 2020-07-15 14:35:42 +02:00
Shrimadhav U K
bfb8de2736
Update upload file size limit to 2GB (#1499)
Source: https://t.me/tginfo/2656
Closes #1498.
2020-07-06 20:11:40 +02:00
Lonami Exo
e44926114a Bump to v1.15 2020-07-04 13:29:43 +02:00
Lonami Exo
326f70b678 Support clicking on buttons asking for phone/location
Closes #1492.
2020-07-04 13:29:43 +02:00
Lonami Exo
7b852206f1 Fix click timeout error is now different 2020-07-04 13:29:43 +02:00
Lonami Exo
ab594ed0cb Remove unused imports and variables 2020-07-04 13:29:43 +02:00
KnorpelSenf
0f8119c400
Fix typo in docs (#1493) 2020-06-24 14:30:41 +02:00
Tulir Asokan
ba4f4c1f78
Fix url property in QRLogin (#1494) 2020-06-24 14:11:54 +02:00
Lonami Exo
e0c3143763 Update documentation with new errors and further clarifications 2020-06-22 13:21:45 +02:00
Lonami Exo
fc07e6bba7 Document some RPC errors for channels.editCreator 2020-06-07 09:50:33 +02:00
Lonami Exo
3e511484c7 Support pathlib.Path on download_file
Fixes #1379.
2020-06-06 21:07:22 +02:00
Lonami Exo
4b933069f1 Add hacks to properly handle events.Album from other DCs
Fixes #1479.
2020-06-06 21:01:02 +02:00
Lonami Exo
faf7263d8f Handle RPC errors on auto-get_difference
Closes #1428.
2020-06-06 14:04:14 +02:00
Lonami Exo
db3e7656e0 Handle AssertionError when cancelling tasks
Fixes #1478.
2020-06-06 13:54:19 +02:00
Lonami Exo
20a6d7b26b Document several new RPC errors 2020-06-06 13:47:56 +02:00
Lonami Exo
3f74f83964 Move qrlogin with the rest of custom types 2020-06-06 13:47:56 +02:00
Lonami Exo
bc03419902 Move doc page of projects using Telethon to the wiki
New URL: https://github.com/LonamiWebs/Telethon/wiki/Projects-using-Telethon
2020-06-06 13:47:56 +02:00
penn5
8557effe13
Fix docs in InlineQuery (#1425) 2020-06-06 13:47:46 +02:00
Lonami Exo
c904b7ccd8 Add a friendly method for QR login
Closes #1471.
2020-06-05 21:58:59 +02:00
Lonami Exo
bfa995d52b Don't crash when receiving updates prior to login
Fixes #1467, and enables #1471.
2020-06-05 21:17:09 +02:00
Lonami Exo
493f69f195 Update to layer 114 2020-06-05 09:29:15 +02:00
Antoni Mur
6c7cfd79b9
Add Taas to the list of alternatives in other languages (#1474) 2020-05-27 19:34:27 +02:00
Lonami Exo
8330635a72 Bump to v1.14.0 2020-05-26 09:31:36 +02:00
Lonami Exo
a46ce053f1 Fix another crash for return value when sending albums 2020-05-24 19:01:05 +02:00
Lonami Exo
02d0cbcfab Document FILE_REFERENCE_EMPTY 2020-05-24 18:41:30 +02:00
Lonami Exo
88e7f0da65 Fix return value when fwding msgs if some are missing
It was supposed to return None for the spots were it failed to fwd
a message, but instead only those that were present were returned,
because we were iterating over the wrong object (dict and not list).
2020-05-24 18:41:30 +02:00
apepenkov
165950169f
Add payment example (#1470) 2020-05-21 13:22:37 +02:00
Lonami Exo
29eb90e503 Fix get_pinned_message in Chat
Closes #1458.
2020-05-17 09:35:44 +02:00
Lonami Exo
856538635d Document FRESH_CHANGE_PHONE_FORBIDDEN
Closes #1464.
2020-05-17 09:28:30 +02:00
JuniorJPDJ
634bc3a8bd
Allow event's func to be async (#1461)
Fixes #1344.
2020-05-16 09:58:37 +02:00
penn5
c45f2e7c39
Handle flood waits of 0 seconds more gracefully (#1460) 2020-05-13 18:50:56 +02:00
Lonami Exo
393da7e57a Expose missing embed_links param in edit_permissions 2020-05-09 17:35:26 +02:00
Lonami Exo
4393ec0b83 Support dice autocast and update docs on send_file for dice 2020-05-05 09:28:37 +02:00
Lonami Exo
db16cf5548 Update to layer 113 2020-05-01 14:10:58 +02:00
Komron Aripov
0f1f655e5d
Fix some missing things in the docs listing other libraries (#1445) 2020-04-29 15:42:59 +02:00
Lonami Exo
c43e2a0a3a Return produced service message with pin_message
Fixes #1394.
2020-04-29 10:29:14 +02:00
Lonami Exo
74bced75b4 Check if the conversation was cancelled on send methods
Fixes #1411.
2020-04-28 21:02:27 +02:00
Lonami Exo
7ea4686d6c Handle FloodWaitError in client.download_media
Fixes #1426. Not entirely happy with the new indirection layer,
but the original __call__ method is already a mess anyway and
the additional cost of an extra call should be neglible compared
to the actual IO.
2020-04-28 20:49:57 +02:00
Lonami Exo
7f3aa43ad4 Rely on types.UpdateChatPinnedMessage for chat unpins
Fixes #1405, probably.
2020-04-27 21:16:45 +02:00
Lonami Exo
71ed1564cb Add a new .dice property to Message 2020-04-27 20:43:09 +02:00
Ali Gasymov
202a8a171b
Update links to other MTProto libraries (#1442) 2020-04-27 20:12:49 +02:00
Lonami Exo
bfa46f47ed Register application/x-tgsticker to mimetypes 2020-04-26 13:42:16 +02:00
Lonami Exo
eb58e60dd1 Fix string formatting on events.Raw with bad input param 2020-04-26 13:42:16 +02:00
TishSerg
a16c60c886
Fix action 'song' should alias 'audio' (#1444) 2020-04-26 11:00:00 +02:00
Lonami Exo
c487340f8e Bump to v1.13.0 2020-04-25 16:28:13 +02:00
Lonami Exo
dcc450267f Document new EMOTICON_INVALID error 2020-04-24 12:38:51 +02:00
Lonami Exo
a353ae3b65 Update to layer 112 2020-04-24 12:17:33 +02:00
Lonami Exo
c37dc69592 Fix downloading thumb was using name inferred for video 2020-04-23 21:01:29 +02:00
Lonami Exo
0c8a90f2a3 Fix delete_messages(None, ...) not working 2020-04-23 20:40:23 +02:00
Lonami Exo
67a9718f9e Bump to v1.12.0 2020-04-20 15:12:00 +02:00
Lonami Exo
01cf4967a5 Clarify send_read_acknowledge behaviour and add new error 2020-04-13 15:03:13 +02:00
ov7a
79fb1a54cb
Switch to blocking connect when using proxy (#1432)
Until a better fix is found, this should help proxy users.
2020-04-12 14:28:40 +02:00
Lonami Exo
c0e523508b Update raw API method usability mapping 2020-04-06 17:44:22 +02:00
Lonami Exo
8ea5fae61b Add some missing layer 111 raw API methods 2020-04-06 10:09:11 +02:00
Lonami Exo
d0f937bcb6 Don't disconnect borrowed senders immediately (#1364) 2020-04-05 12:34:33 +02:00
Arne Beer
3729fde572
Fix editing of inline messages in some cases (#1427) 2020-04-03 18:37:46 +02:00
Lonami Exo
15f30ed942 Update documentation with some fixes and MongoDB sessions
Closes #1403 and #1406.
2020-04-01 19:56:17 +02:00
Dmitry D. Chernov
0ec612d71a utils: Style fix and simplify a bit the VALID_USERNAME_RE 2020-03-31 19:18:57 +10:00
YouTwitFace
1669d80082
Remove call to _cache_media (#1419)
Fixes #1418
2020-03-29 10:15:53 +02:00
Lonami Exo
65d8205eef Update to layer 111 2020-03-28 10:01:31 +01:00
Lonami Exo
3ab9986fc7 Slightly better flow in _file_to_media 2020-03-14 12:16:52 +01:00
Lonami Exo
ccfd7a1015 Don't ignore thumb in send_file(input file)
Fixes #1404
2020-03-14 12:12:40 +01:00
Lonami Exo
68438f4621 Don't store refs to files in cache
File cache has been unused since file_reference were introduced,
there's no point saving them to cache if they're never queried.

Fixes #1400.
2020-03-11 10:07:21 +01:00
Lonami Exo
e3d8109110 Fix a doc typo and update projects using the lib 2020-03-11 10:02:19 +01:00
painor
0e0052888f
Expose key and iv parameter in downloads/uploads (#1397) 2020-03-04 16:12:34 +01:00
Lonami Exo
1ec38aa5b2 Update and clarify some docs
cc #1388, #1396
2020-02-28 11:50:16 +01:00
Lonami Exo
e451abbf20 Avoid another MemoryError 2020-02-28 10:42:23 +01:00
Lonami Exo
673a2ecd5d Document two new errors 2020-02-25 15:52:22 +01:00
Lonami Exo
e9c5e719f1 Minor docs update, bump v1.11.3 2020-02-24 13:15:56 +01:00
Lonami Exo
9a86447b6e Fix get(_input)_users in ChatAction with no service msg 2020-02-24 13:07:13 +01:00
Pascal Jürgens
0814a20ec4
Fix macOS version parsing (again), bump v1.11.2
#1393
2020-02-21 20:37:24 +01:00
Lonami Exo
8aa15174ab Fix check in macOS (#1369), bump v1.11.1 2020-02-21 12:48:43 +01:00
Lonami Exo
64752d89fc Fix 1.11 changelog 2020-02-21 11:41:18 +01:00
Lonami Exo
f21abcd529 Update to v1.11 2020-02-20 20:57:17 +01:00
Lonami Exo
1e94fe25fa Log requests that trigger struct.error
The exception hardly provides any valuable information.
This will hopefully help troubleshooting why the error happens.
2020-02-20 13:40:08 +01:00
Lonami Exo
7ffb87170b Update some URLs
Some were out of date, some were examples pointing to a personal
link, which were replaced with generic examples.
2020-02-20 11:50:15 +01:00
Lonami Exo
3d32e16235 Fix within surrogate detection 2020-02-20 10:53:28 +01:00
Lonami Exo
3a6c955c90 Add examples to all events 2020-02-20 10:18:26 +01:00
Lonami Exo
9f73c35621 Fix unparsing of entities that are together 2020-02-20 09:43:37 +01:00
Lonami Exo
7c6fe5c4e9 Update to layer 110 2020-02-16 14:03:16 +01:00
Tulir Asokan
95dc775344
Fix errors found by new tests (#1389) 2020-02-14 18:35:42 +01:00
Tulir Asokan
c6bd620555
Make RPCError subclasses unpicklable again (#1387) 2020-02-14 18:22:17 +01:00
Lonami Exo
8bd60f7cde Update out-of-date docs 2020-02-11 16:44:25 +01:00
Lonami
ac8009af4a
Fix default DC ID value 2020-02-02 10:01:15 +01:00
Lonami Exo
5f8032584b Fix _get_response_message for sendMedia(live location) 2020-02-01 15:32:52 +01:00
Lonami Exo
22e645e22f Update to layer 109 2020-01-23 13:43:20 +01:00
Lonami Exo
dd4c22d02d Document two new known errors 2020-01-22 14:21:09 +01:00
Lonami Exo
acb8518911 Fix send_message not forwarding some args to send_file 2020-01-22 14:21:09 +01:00
Qwerty-Space
82943bd464 Replace hastbin with deldog (#1377)
Hopefully it's dead less often.
2020-01-22 12:29:04 +01:00
Lonami Exo
02bdf7d27c Improve question templates with contact links 2020-01-21 16:17:03 +01:00
Lonami Exo
a2fc7dca79 Handle users=None properly in ChatAction 2020-01-21 10:39:51 +01:00
Lonami Exo
54c8771885 Properly handle #ot command in assistant.py for PMs 2020-01-19 13:33:30 +01:00
Lonami Exo
da9505fa3c Add some missing words in the docs 2020-01-19 13:25:58 +01:00
Pascal Jürgens
72dc8052b3 Fix crypto imports on macOS Catalina (#1369) 2020-01-17 12:24:59 +01:00
Alexhol
76cc076d61 Fix send_file not considering videos for albums (#1371) 2020-01-17 11:12:20 +01:00
Lonami Exo
78ee787310 Fix utils._get_extension not working in pathlib objects
This was found while testing #1371.
2020-01-17 11:11:10 +01:00
Lonami Exo
d09f6a50b0 Add extra security checks during authkey gen 2020-01-14 12:12:55 +01:00
Lonami
76cf208619
Document where factorization.py comes from 2020-01-09 12:51:41 +01:00
Lonami Exo
76fa7918a5 Fix get_entity(chat) (#1367) 2020-01-08 12:07:58 +01:00
Lonami Exo
3c253734ac Clear old docs and fix formatting in ConnectionError messages 2020-01-07 12:20:01 +01:00
Lonami Exo
d68d70362b Handle PeerIdInvalidError in delete_dialog 2020-01-07 12:14:19 +01:00
Lonami Exo
582a61192a Fix MemoryError on get_input_media(game)
Because an integer was being passed where a TLObject was expected,
so the serialization with bytes() was actually requesting that many
bytes as opposed to properly converting the expected object.
2020-01-04 17:52:31 +01:00
Lonami Exo
364afd61e1 Execute get_me() on reconnect
This should let Telegram know we still want updates.
Ideally, we would catch up, but that requires more work.
2020-01-04 17:22:53 +01:00
Lonami Exo
0683d9771a Update to layer 108 2019-12-31 10:43:05 +01:00
Lonami Exo
d196c89825 Fix unparsing malformed entities, bump v1.10.10 2019-12-30 10:19:29 +01:00
Lonami Exo
be8838b5f8 Fix wrong call to determine entity type 2019-12-30 10:56:20 +01:00
Lonami Exo
a142b7de5e Handle invalid upload of text fd more gracefully 2019-12-27 12:05:27 +01:00
Lonami Exo
bdb74ac235 Support async fd on download/upload (#1334) 2019-12-27 12:04:08 +01:00
Lonami Exo
5d7e9f3879 Improve doc page on RPC errors (#1350) 2019-12-27 11:27:00 +01:00
Lonami Exo
bea4225d28 Don't reply to message on text button click (#1351)
Official clients don't do it, so we probably shouldn't either.
2019-12-27 10:56:26 +01:00
Lonami Exo
1bd02d64c5 Handle RuntimeError on helpers._cancel and improve logging 2019-12-27 10:46:01 +01:00
Lonami Exo
94ff5a8641 Handle ChannelForbidden on leaving ChatAction 2019-12-27 10:21:37 +01:00
Lonami Exo
0af823e86c Add three new errors regarding polls 2019-12-26 22:30:07 +01:00
Lonami Exo
29ff3708c4 Handle UpdateMessagePoll in _get_response_message (#1355) 2019-12-26 22:00:41 +01:00
Lonami Exo
86bb4b4e6c Whitelist instead of blacklist in flake8 2019-12-25 12:58:09 +01:00
Lonami Exo
bdff61653a Exclude telethon_examples/ from flake8 2019-12-25 12:07:22 +01:00
Lonami Exo
c3188ff0fa Handle empty list properly in buttons 2019-12-23 14:49:40 +01:00
Lonami Exo
fa736f81af Handle all entity types on isinstance checks
Only the uses of `isinstance` against `InputPeer*` types were
reviewed. Notably, `utils` is exempt on this because it needs
to deal with everything on a case-by-case basis.

Since the addition of `*FromMessage` peers, any manual `isinstance`
checks to determine the type were prone to breaking or being
forgotten to be updated, so a common `helpers._entity_type()`
method was made to share this logic.

Since the conversion to `Peer` would be too expensive, a simpler
check against the name is made, which should be fast and cheap.
2019-12-23 13:52:07 +01:00
Lonami Exo
627e176f8e Handle *FromMessage peers in utils' casts 2019-12-23 13:47:55 +01:00
Lonami Exo
ecb27f33f7 Lower log severity on error during disconnect 2019-12-23 11:54:56 +01:00
Lonami Exo
4499f3b95e Update to layer 106 2019-12-22 11:17:59 +01:00
Lonami Exo
f3111f93b2 Fix unparsing text with malformed message entities 2019-12-19 15:48:59 +01:00
Lonami
ccbc1c669c
Merge pull request #1343 from NotAFile/add-tests
Add example unit test and config for testing
2019-12-14 13:19:49 +01:00
Lonami Exo
7e6f12daa6 Fix ChatAction join/leave in channels, bump v1.10.9 2019-12-11 12:42:51 +01:00
NotAFile
9121478a2e fix first type found by tests :) 2019-12-08 00:30:25 +01:00
NotAFile
8b535473ce add gitlab actions config 2019-12-08 00:29:55 +01:00
NotAFile
30fdf17902 add documentation on test setup 2019-12-07 23:41:02 +01:00
NotAFile
acd14d7bf3 add test checking for #1324 2019-12-07 20:43:29 +01:00
NotAFile
a4876c1ac5 Add example unit test and config for testing
Add testing configuration:
 - pytest as test framework
 - tox for creating testing environments and running tests (run with `tox`)
 - pytest-asycio for asyncio testing
 - coverage/pytest-cov for measuring test coverage
 - flake8 for pep8 checking

I've also added one quick example test demonstrating basic unit testing
and use of the basic fixtures and marks provided by pytest-asyncio.

Just this already covers a suprising 32% of the codebase, mostly through
imports, but I wouldn't expect it to be helpful yet. This should provide
a good base to build on in the future though.
2019-12-07 04:23:27 +01:00
Lonami Exo
b8aa639f3c Check for event loop method, not type (#1337) 2019-12-06 10:23:15 +01:00
Lonami Exo
03f0533139 Fix global search with filter/from_user 2019-12-05 16:19:46 +01:00
Lonami Exo
99d4001db6 Warn users with ProactorEventLoop about proxy issues (#1337) 2019-12-05 11:27:47 +01:00
Lonami Exo
b985dcd248 Fix 3.8 syntax warning and erroneous documentation
Closes #1340 (syntax warning)
Closes #1341 (outdated docs)
2019-12-05 11:14:02 +01:00
Lonami Exo
cd37478e31 Don't send pings unless the connection is made
This will hopefully avoid batching tens of ping requests which
we don't care about their results.
2019-12-02 18:36:20 +01:00
Mengyang Li
149b26fb51 Allow force_document in edit_message (#1335) 2019-11-23 11:00:51 +01:00
Lonami Exo
3a56c8b0f4 Correctly handle flood_sleep_threshold=None and large values
The docstring said large values would be converted to 1 day,
but they were not. With this change None and large values are
handled correctly.

Prevents https://github.com/tulir/mautrix-telegram/issues/380.
2019-11-18 12:51:18 +01:00
Lonami Exo
6817e19923 Fix return value of send_file(album) of the same media 2019-11-10 14:59:01 +01:00
Lonami Exo
57dd0827f4 Slight improvement on ChatAction's docs 2019-11-10 14:59:01 +01:00
painor
38b929b973 Fix several typos (#1328) 2019-11-10 11:29:43 +01:00
painor
4a1310dc21 Fix RSA key unpacking on missing fingerprint (#1324) 2019-11-05 08:54:10 +01:00
painor
4839d8bf59 Fix pin_message not accepting Message objects (#1322) 2019-11-04 09:46:17 +01:00
penn5
3d1ce845be Don't parse Button's text on click (#1315) 2019-11-01 11:47:44 +01:00
Lonami Exo
a1aaa96120 Fix iter_download would not determine file_size alone 2019-10-31 19:38:49 +01:00
Lonami Exo
a67c94787b Make getting PhotoSize byte count more reusable internally 2019-10-31 19:38:27 +01:00
Lonami Exo
7e346180d7 Fix import ssl may fail under some Python installs
It's only required for certain proxy configurations, so we
don't want it to raise ImportError while the user imports
our library.
2019-10-31 19:20:18 +01:00
Lonami Exo
6850903d17 Fix get_edit not always returning awaitable, bump v1.10.8 2019-10-31 10:44:33 +01:00
Lonami Exo
0a3d164806 Fix handling of early edits in Conversation
The incoming messages were never updated, so of course their
edit_date wasn't either. This would cause the library to be
stuck until it timed out, because the event had already
arrived before we waited for it. As an example:

    await conv.send_message('foo')
    await sleep(1)  # bot has plenty of time to respond+edit
    await conv.get_edit()
2019-10-31 10:44:33 +01:00
YouTwitFace
4a8b19b0be Remove @ya from valid usernames (#1306) 2019-10-28 18:12:47 +01:00
Lonami Exo
baacecadc5 Document some errors, bump v1.10.7 2019-10-27 18:50:21 +01:00
Manuel1510
0a8103b6e8 Replace messages.getPeerDialogs with channels.getFullChannel (#1305) 2019-10-27 18:48:41 +01:00
Lonami Exo
5dcc30dcc6 Handle connection errors during auth key generation
This should help with spurious server-side disconnects during
auth_key generation, which happen most commonly on user DC
migrations.
2019-10-24 13:48:29 +02:00
Lonami Exo
08b78f0c47 Unify retry loops in mtprotosender._connect
Now the retry count is not twice its value.
2019-10-24 13:40:09 +02:00
Lonami Exo
3039915ce9 Factor out parts from mtprotosender._connect
This will help unifying the retry loop and reconnecting
if the server disconnects us during auth key generation
which will be done in a follow-up commit.
2019-10-24 13:36:32 +02:00
Lonami Exo
ca2537941c Fix sending albums in conversations 2019-10-22 20:35:08 +02:00
Lonami Exo
6206a1a524 Minor documentation update 2019-10-21 11:45:31 +02:00
YouTwitFace
b862f215c5 Ignore channel-only permissions in megagroups (#1292) 2019-10-11 18:04:41 +02:00
Lonami Exo
09f27f0dd7 Update Telegram's RSA keys 2019-10-03 20:52:25 +02:00
Tanner Collin
72dd36bc17 Allow edit_admin on yourself (#1285) 2019-09-29 09:19:44 +02:00
Lonami
07b0583069
Bump to v1.10.6 2019-09-28 09:44:01 +02:00
penn5
88d8424474 Fix kick_participant in channels (#1284)
Presumably some server-side change made insta-unbanning no longer work.
2019-09-28 09:38:41 +02:00
Lonami Exo
5e6ff67d01 Make allow_cache do nothing for now, bump v1.10.5 (#1272) 2019-09-27 15:12:17 +02:00
Lonami Exo
a360d74a4c Mention pomegranate under projects using the library 2019-09-27 10:41:01 +02:00
Lonami Exo
7de01a5f94 Fix resending code with empty phone hash (fix #1283) 2019-09-27 10:41:01 +02:00
Lonami
6da8d1a0ec
Less confusing error for getting marked ID of PeerChannel(0)
Fixes #1282.
2019-09-24 19:01:46 +02:00
Lonami Exo
d1ddfd09b6 Update missing links in the documentation summary 2019-09-24 16:19:56 +02:00
Andrebcd4
40aa46e72a Sleep automatically on slow mode error too (#1279) 2019-09-24 11:37:41 +02:00
Lonami Exo
4f6e5c5f5a Remove another debug print, bump v1.10.4 2019-09-21 18:21:09 +02:00
penn5
8d5a7c6ffb Remove debugging print (#1276) 2019-09-21 17:54:45 +02:00
davtur19
b76bed3a40 Update errors' description (#1266) 2019-09-16 19:40:29 +02:00
Lonami
75ca28df49
Parse t.me/@<user> URLs as valid usernames 2019-09-16 11:36:35 +02:00
Lonami Exo
c1774276c2 Fix handling of ChannelForbidden in input peer, bump v1.10.3 2019-09-12 22:30:47 +02:00
Lonami Exo
9c06f29aaf Don't cache entities with min flag set, bump v1.10.2
Since layer 102, there are two access_hash. One with the min flag,
and one without it. This was causing channel invalid errors.

access_hash with min flag set can only be used to fetch files such
as profile pictures.

access_hash with min flag unset can be used under all circumstances.

Previously, the library did not distinguish between these, so it was
caching the hash that could hardly be used for anything.

With this change, only the "full" access_hash is stored, which will
work for any methods.

See also: https://core.telegram.org/api/min
2019-09-12 19:19:46 +02:00
Lonami Exo
5c72e1286e Let delete_dialog work for bot accounts without erroring 2019-09-10 21:38:46 +02:00
Lonami Exo
0bf4c4ae75 Add v1.10.1 to .nix files 2019-09-10 21:38:46 +02:00
davtur19
95ba02a9d3 Add new known errors (#1265) 2019-09-10 21:09:43 +02:00
Lonami Exo
47956ddbca Fix events.Album and minor docs nit, bump to v1.10.1 2019-09-09 19:21:03 +02:00
Lonami Exo
b4046017a7 Bump nix expressions to 1.10 2019-09-08 12:01:17 +02:00
Lonami Exo
8ded667a6b Update to v1.10 2019-09-08 11:33:19 +02:00
Lonami Exo
6e9d799103 Actually fix message.document for webpages (57049d follow-up) 2019-09-08 11:06:54 +02:00
Lonami Exo
67183ff9e8 Improve method signatures in the docs 2019-09-08 10:56:35 +02:00
Lonami Exo
dab237e758 Support sending scheduled messages 2019-09-06 13:45:31 +02:00
Lonami Exo
9dd73cd494 Update to layer 105 2019-09-06 13:10:27 +02:00
Lonami Exo
57049de23a Fix message.document for webpages 2019-09-06 13:09:07 +02:00
Lonami Exo
d5faf5e8aa Support getting more than 100 messages by ID 2019-08-26 12:16:46 +02:00
painor
61bc8f7fa3 Fix-up #1259 missing import (#1261) 2019-08-25 21:29:17 +02:00
painor
bd7ab23a8f Support async progress_callback in upload_file (#1259) 2019-08-19 21:11:13 +02:00
Lonami Exo
42874de2b2 Fix start() and sign_up() flow for layer 104 2019-08-16 19:19:42 +02:00
Lonami Exo
00b0319397 Update to layer 104 2019-08-14 00:03:24 +02:00
Lonami Exo
f2a236eb57 Split scheme back into two files 2019-08-13 23:45:17 +02:00
Lonami Exo
e1905d0d7a Avoid using telethon.sync in the examples 2019-08-13 23:33:39 +02:00
Lonami Exo
61c0e63bbe Avoid unnecessary await in Conversation 2019-08-13 18:11:02 +02:00
Lonami Exo
e24dd3ad75 Prevent double-connect causing double-reads later
Which leads to "RuntimeError: readexactly() called while another
coroutine is already waiting for incoming data" being raised,
and causing everything to break or halt.
2019-08-11 19:05:11 +02:00
Lonami
48a70308b5
Update docstring for send_read_acknowledge 2019-08-11 10:23:45 +02:00
Lonami Exo
969a36c2a8 Update docs for silent parameter
Since it now also works in private chats to "not disturb friends"
(see https://telegram.org/blog/silent-messages-slow-mode).
2019-08-10 09:06:05 +02:00
Lonami Exo
f5de2cd9a0 Fix logging bug on disconnect 2019-08-08 09:32:18 +02:00
Lonami Exo
c0e4d6c8b6 Slightly simplify BinaryReader
There was no need for the BufferedReader, since everything
is already in memory. Further, the stream parameter was never
used, so it was also unnecessary. The check for None when
reading length was also unnecessary, since we could just pass
-1 to begin with.
2019-08-07 10:33:46 +02:00
Lonami Exo
45d82f2a85 Fix issues with to/from ID in private chats with multiple clients
This should address #1218.
2019-08-07 00:46:19 +02:00
Lonami Exo
b1eed82b7f Fix use of newer file IDs and add two new errors 2019-08-06 23:25:58 +02:00
Lonami Exo
b719a2a432 Remove unwanted html.unescape() call 2019-08-04 10:09:23 +02:00
Lonami Exo
8a933afc5d Support iterating over specific drafts more easily 2019-08-01 20:15:32 +02:00
Lonami Exo
d3221a508a Add kick_participant 2019-08-01 19:21:01 +02:00
Phil Jones
e1355ae5d8 Improve Quart example (#1229) 2019-08-01 19:15:04 +02:00
binares
2b277dd558 Fix (de)serialization of negative timestamps (#1241) 2019-08-01 18:47:38 +02:00
Lonami Exo
2ace4fde41 Fix Forward had its client set to None (#1247) 2019-07-31 11:04:08 +02:00
bb010g
13e9119573 Create Nix package expressions (#1246) 2019-07-30 15:31:53 +02:00
Lonami Exo
de85c34462 Handle connection error when fetching difference in updates 2019-07-23 21:12:08 +02:00
Lonami Exo
5a225d1668 Fix a dialog's message could be wrong in rare cases 2019-07-23 12:44:19 +02:00
Lonami Exo
eb44c6634b Add Dialog to auto cast to peer 2019-07-23 12:44:06 +02:00
Lonami Exo
5498d14e54 Fix default edit_permissions and clarify documentation 2019-07-23 10:14:31 +02:00
Lonami Exo
48d6f15850 Update to layer 103 (again) 2019-07-20 12:26:11 +02:00
Lonami Exo
ae620db0c5 Better document MESSAGE_ID_INVALID 2019-07-20 11:15:29 +02:00
Lonami Exo
cbcbda5276 Minor documentation and type hint updates 2019-07-17 12:37:16 +02:00
Lonami Exo
4bf85d9e8e Add new Button.auth (#1235) 2019-07-17 12:25:29 +02:00
Lonami Exo
649e9a7b0c Check for empty message after applying parse mode 2019-07-17 12:06:23 +02:00
Lonami Exo
cca50ef842 Improve flood wait log message 2019-07-17 12:04:53 +02:00
Lonami Exo
2ffd1e8e7c Clean-up usage of root directory in docs generation
Instead, make use of the current working directory.
This should ease most of it and remove noise, since
now root is just the current directory.
2019-07-17 10:11:52 +02:00
Lonami Exo
eb02eadb9f Fix TempWorkDir not actually working 2019-07-17 09:34:17 +02:00
Lonami Exo
ec093f90e7 Fix InputKeyboardButtonUrlAuth was not considered inline 2019-07-16 18:47:28 +02:00
Lonami Exo
de46745926 Fix "methods returning this type" not accounting for vectors 2019-07-15 14:58:27 +02:00
Lonami Exo
944fb10733 Add missing return self on action/download ctx managers 2019-07-13 21:21:29 +02:00
painor
ae1c1b3912 Improve permission-related errors (#1231) 2019-07-10 23:19:50 +02:00
painor
2adc746143 Fix CallbackQuery pattern= (#1230) 2019-07-10 17:37:36 +02:00
st7105
c4c263a85b Use flush() only if it exists (#1227) 2019-07-09 11:40:05 +02:00
Lonami Exo
0ced884aa3 Fix wrong friendly methods mappings 2019-07-06 19:45:20 +02:00
Lonami Exo
7a78aebb12 Update to v1.9.0 2019-07-06 19:38:18 +02:00
Lonami Exo
97e4d83593 Rename and clarify docs for edit_permissions 2019-07-06 19:31:33 +02:00
Lonami Exo
05b770a93f Fix directly nested markdown entities 2019-07-06 12:55:44 +02:00
Lonami Exo
8e36bb4c4d Link Python keywords with Python's documentation 2019-07-06 12:11:00 +02:00
painor
42d5c0fe6d Add pattern parameter to events.CallbackQuery (#1212) 2019-07-05 21:03:07 +02:00
Lonami Exo
2d0fc8356f Fix markdown parsing for pre blocks and entity after entity 2019-07-05 20:31:43 +02:00
Lonami Exo
be65c63f16 Add missing re-exports in telethon.sync 2019-07-05 16:51:08 +02:00
Lonami Exo
8a0a18255a Update to layer 103 2019-07-05 12:27:20 +02:00
Lonami Exo
61270e0ea6 Document edit_admin and edit_restrictions as friendly methods 2019-07-05 10:51:48 +02:00
painor
2d2afc5280 Add edit_admin and edit_restrictions (#1210) 2019-07-05 10:48:21 +02:00
Lonami
0d9e639f4f
Support asynchronous progress_callback's 2019-07-04 15:34:51 +02:00
Lonami Exo
3f19f6fd50 Return None in UserUpdate if information is unknown 2019-07-04 10:34:48 +02:00
Lonami Exo
99b15b916c Turn UserUpdate attributes into properties
This aids discoverability, makes them lazily-loaded,
and in general is more consistent and easier to extend.
2019-07-04 10:17:40 +02:00
Lonami Exo
7285b156f4 Create events.Album (#1216) 2019-06-30 16:34:34 +02:00
Lonami Exo
e8327da189 Fix some methods in Message were not checking for client 2019-06-30 16:34:34 +02:00
Lonami Exo
a7a7c4add2 Pass all Updates when building events 2019-06-30 16:34:34 +02:00
Lonami Exo
71979e7b23 Document some special method names 2019-06-30 16:34:34 +02:00
Lonami Exo
aa2b3daccc Factor out setting entities to events 2019-06-30 16:34:34 +02:00
Lonami Exo
8c771a842f Fix Python 3.5.2 type hinting (#1177) 2019-06-28 21:26:08 +02:00
Lonami Exo
7249d01709 Further clarify things in the documentation 2019-06-28 20:50:04 +02:00
Lonami Exo
9322c37a94 Document iteration order 2019-06-28 20:44:06 +02:00
Lonami Exo
84c4fcdec6 Fix entities weren't being passed to Draft 2019-06-28 20:34:30 +02:00
Lonami Exo
81e628b9f7 Fix explicit reverse=True with ids= not working 2019-06-28 20:27:36 +02:00
Lonami Exo
2a7d4317bd Add quart-based example (#1207) 2019-06-27 13:55:54 +02:00
Lonami Exo
4f1edeb750 Let File.ext work even with unknown mime types 2019-06-26 11:31:15 +02:00
Lonami Exo
80c9c5dad3 Avoid memory cycle in Forward 2019-06-26 11:16:17 +02:00
Lonami Exo
b6b4ea669d Remove messy subclassing in the TelegramClient
Since it was easy to cause MRO inconsistencies, and it's
not really needed now that self is type hinted as the client.
2019-06-24 17:48:46 +02:00
Lonami Exo
4e80e21ba1 Update markdown parser to support nested entities 2019-06-24 13:48:29 +02:00
Tulir Asokan
8b28f4ffbf Add support for unparsing nested entities into HTML (#1209) 2019-06-24 12:28:14 +02:00
Lonami Exo
962949008f Add new message entities to markdown/html parsers 2019-06-23 21:35:33 +02:00
Lonami Exo
3c68208c41 Update to layer 102 2019-06-23 21:23:40 +02:00
Lonami Exo
83789aaa42 Return None from ChatGetter when there isn't enough info 2019-06-19 11:46:03 +02:00
Lonami Exo
35ba9848d9 Fix get_extension missing even more photo cases 2019-06-16 11:15:52 +02:00
Lonami
86cdb7c1f8
Remove accidentally-committed debug print 2019-06-15 23:14:59 +02:00
Lonami Exo
bdedd2dc26 Redirect to exact match on pressing enter 2019-06-15 22:54:25 +02:00
Lonami Exo
d61bb2e87f Document MTProto Proxy usage (#1205) 2019-06-15 21:54:24 +02:00
Lonami Exo
8d28d1145a Actually fix invalid state in Conversation (1354bf6 followup) 2019-06-15 21:36:06 +02:00
Lonami Exo
40d32cee95 Assert that dispatched updates are Updates 2019-06-15 21:15:57 +02:00
Lonami Exo
5877459907 Create Message.mark_read() 2019-06-15 19:41:31 +02:00
Lonami Exo
1a056899d7 Fix caption when using send_file for albums / mixed documents 2019-06-15 16:59:16 +02:00
Lonami Exo
fd37e44854 Fix is_image not considering MessageMedia objects
This was causing albums with MessageMedia objects to fail.
2019-06-15 16:42:26 +02:00
Terrance
634d8a7898 Refactor libssl import path workarounds (#1202)
This amends the logic introduced for #1167 to only run when a plain
import of the library fails, thus avoiding the slow path traversal
needed to find the underlying library file.
2019-06-15 13:42:31 +02:00
Dmitry D. Chernov
93f4de8792 Update link to the GitHub repository of 'cryptg' package 2019-06-12 12:23:27 +10:00
Lonami Exo
51de0bd2da Update documentation with intersphinx and better summaries 2019-06-11 11:09:22 +02:00
Lonami Exo
31a26c0a0a Fix iter_dialogs missing dialogs once and for all 2019-06-11 10:04:36 +02:00
Lonami Exo
b8a38baaf6 Expose ignore_pinned in iter_dialogs 2019-06-11 09:55:13 +02:00
Lonami Exo
9752f66eab Minor updates to the documentation and errors 2019-06-07 15:48:34 +02:00
Lonami Exo
770c2c504d Add new methods to encode and decode waveforms 2019-06-04 21:36:38 +02:00
Lonami Exo
065719c8d8 conversation.cancel() now raises cancelled on future calls (#1183) 2019-06-03 19:44:43 +02:00
Lonami Exo
4c3e467d25 Add a method to cancel_all conversations (#1183) 2019-06-03 19:41:22 +02:00
Lonami Exo
690a40be77 Better behaviour for conversation.cancel() (#1183) 2019-06-03 19:29:08 +02:00
Lonami
9d6150da37
Fix downloading contacts to path (#1197) 2019-06-03 15:44:43 +02:00
Lonami Exo
e47f3ec1d6 Clarify some aspects of the documentation 2019-06-01 16:28:20 +02:00
Manuel1510
27360242b0 Add new known errors (#1196) 2019-06-01 15:49:12 +02:00
Lonami Exo
30a0e39060 Update to v1.8.0 2019-05-30 21:31:07 +02:00
Lonami Exo
5832ab2f31 Create new client.pin_message method 2019-05-30 17:15:50 +02:00
Lonami Exo
9ea686ab14 Create new client.iter_profile_photos method 2019-05-30 16:51:19 +02:00
Lonami Exo
0d64fd98f7 Create new client.delete_dialog method 2019-05-30 13:58:05 +02:00
Lonami Exo
e4158acd08 Update to layer 100 2019-05-30 13:23:47 +02:00
Lonami Exo
9f72bd8ca3 Fix manual raising of RPC errors 2019-05-27 18:18:38 +02:00
Lonami Exo
1354bf68a8 Factor out clearing items from pending in conversations
This should prevent bugs and ease reasoning, since
now everything is removed from a single place.
2019-05-27 14:23:42 +02:00
Lonami Exo
0b41454b01 Fix conversation setting result on cancelled futures
On timeout, they are cancelled. On a new message
arriving, we pop and set the result unconditionally.

    conv.send_message('Talk to me please')
    conv.get_response()
    try: conv.get_response(timeout=0.1)
    except asyncio.TimeoutError: pass
    conv.send_message('One more time...')
    conv.get_response()  # errors unless above is commented
2019-05-27 14:10:38 +02:00
Lonami Exo
e5485f3d54 Document raw methods with friendly variants 2019-05-26 21:15:43 +02:00
Lonami Exo
4ebf825c43 Clarify documentation on connection and receiving updates 2019-05-23 12:11:58 +02:00
Lonami Exo
6e5f90730e Fix reversed(client.iter_messages(offset_date=...)) 2019-05-22 12:20:02 +02:00
Lonami Exo
80f19bd1f0 Point to the new domains 2019-05-22 11:29:46 +02:00
Lonami Exo
c3d1d7a64c Rename client.archive as client.edit_folder 2019-05-22 11:20:56 +02:00
Lonami Exo
cf152403ee Use iter_download on download_file 2019-05-21 16:40:11 +02:00
Lonami Exo
0b0f8f4285 Create a method to iterate downloads 2019-05-21 16:16:32 +02:00
Lonami Exo
a43830d403 Fix updates introduction and add more projects using Telethon 2019-05-21 13:26:34 +02:00
Lonami Exo
58f3225fa6 Minor tweaks to the installation section of the docs 2019-05-21 10:13:25 +02:00
Lonami Exo
a9ff328e38 Fix iter_participants search on small group chats 2019-05-20 14:02:51 +02:00
Lonami Exo
465f38c1c6 Fix message.text behaviour with no parse mode 2019-05-20 12:00:52 +02:00
Lonami Exo
383ab9b0b2 Fix message.text not checking if parse_mode was set 2019-05-20 11:57:11 +02:00
Lonami Exo
7c1c040d50 Update docstrings to have consistent style 2019-05-20 11:57:11 +02:00
Hasan
4b74d16438 Fix file_id error checking (#1189) 2019-05-19 22:18:35 +02:00
Lonami Exo
da5c801b4d Add new section for people coming from Bot API 2019-05-19 12:40:52 +02:00
Lonami Exo
e5f1b2afa3 Update logging calls to use proper formatting 2019-05-17 12:30:13 +02:00
Lonami Exo
a4c2e45d6d Handle more key errors on forward message 2019-05-17 12:30:13 +02:00
Lonami
a71b095c1d Add RTFM issue template 2019-05-16 17:16:28 +02:00
Lonami Exo
bcfc3e7550 Remove invalid error inits and move bad msg error 2019-05-15 13:58:19 +02:00
Terrance
0946a7902f Fix super() initialisation call for EventCommon (#1182) 2019-05-12 23:29:01 +02:00
Lonami Exo
9730894a07 Call Chat/Sender Getter init methods 2019-05-12 14:00:12 +02:00
Lonami Exo
fefd6f0e6d Fix use of tg://join?invite= channels 2019-05-12 13:44:09 +02:00
Lonami Exo
5754ad589f Fix type hints for Python 3.5.2 (#1177) 2019-05-12 13:08:07 +02:00
Lonami Exo
5d41246e73 Support importing plugins in assistant when ran as module 2019-05-11 20:44:35 +02:00
Lonami Exo
560d4bed09 Move some parts of assistant to a different repo as plugins 2019-05-11 20:41:10 +02:00
Lonami Exo
4ca3517e22 Fix some type hints 2019-05-11 20:12:57 +02:00
Lonami Exo
c1be0bd2e8 Fix disconnection without a previous connection 2019-05-11 16:53:11 +02:00
Lonami
b31b239088
Remove the now deprecated issue template format 2019-05-11 15:33:28 +02:00
Lonami
564baffa17 Update issue templates 2019-05-11 15:33:00 +02:00
Lonami Exo
278f0e9e98 Don't raise errors during disconnect 2019-05-10 18:54:10 +02:00
Lonami Exo
313caf440e Add friendly methods for archiving dialogs 2019-05-10 18:12:16 +02:00
Lonami Exo
1828dca0b9 Support filtering by folder on iter_dialogs 2019-05-10 15:45:24 +02:00
Lonami Exo
e408550553 Update to layer 99 2019-05-09 21:21:50 +02:00
Lonami Exo
a7443612f6 Fix download of PhotoStrippedSize to bytes 2019-05-09 18:56:54 +02:00
Lonami Exo
aa1eec93be Use the friendly pigments theme 2019-05-09 14:22:35 +02:00
Lonami Exo
fbce902cf8 Fix minor documentation issue regarding pre tags 2019-05-09 14:13:43 +02:00
Lonami Exo
7f88238d8f Update external links to the documentation 2019-05-09 14:09:22 +02:00
Lonami Exo
0a3d6106f0 Completely overhaul the documentation 2019-05-09 12:50:09 +02:00
Lonami Exo
10251f9782 Create a new Message.file property (#1168) 2019-05-08 18:41:40 +02:00
Lonami Exo
cfd6d3ce04 Fix catch-up when no pts is known 2019-05-08 18:15:57 +02:00
Lonami Exo
d92d989569 Quote type hints
Otherwise, sphinx completely butchers the documentation.
2019-05-08 17:16:09 +02:00
Lonami Exo
c6691dc6a8 Update the reference with even more types and other docs 2019-05-07 21:25:55 +02:00
Lonami Exo
8bd9dd66ab Re-export the main modules and types from sync
For convenience. People can now easily replace:
from telethon import TelegramClient, events

with
from telethon.sync import TelegramClient, events
2019-05-07 10:47:25 +02:00
Lonami Exo
61613ab6ac Create a new page with a summary of the method reference 2019-05-06 11:38:26 +02:00
Lonami Exo
744f5f50fe Fix saving of StringSession 2019-05-06 08:55:24 +02:00
Lonami Exo
7d0efcf50f Save pts and date only if there is something to save 2019-05-05 22:13:02 +02:00
Lonami Exo
5ed7bf7815 Fix timeout error not being excepted on Button.click 2019-05-05 19:57:09 +02:00
Lonami Exo
b20dc3b804 Fix setup.py calling generate with the wrong arguments 2019-05-05 12:04:51 +02:00
Lonami Exo
19398d75be Add support for hexadecimal invite links 2019-05-05 11:56:04 +02:00
Lonami Exo
945d438696 Properly await all spawned background tasks 2019-05-04 21:02:07 +02:00
Lonami Exo
532bd1c916 Fetch difference only if it is worth it 2019-05-04 19:48:36 +02:00
Lonami Exo
f5e611e4d2 Fix SenderGetter was excepting the wrong type 2019-05-04 19:30:33 +02:00
Lonami Exo
716ab2f96d Fix getting difference for channels and for the first time 2019-05-04 19:29:47 +02:00
Lonami Exo
adc9b4c9f1 Fix SenderGetter should not define abstract methods
Or at least it shouldn't if subclasses are likely to not
implement them, which causes an error if left un-implemented.
2019-05-04 17:59:21 +02:00
Lonami Exo
84c197be60 Better clean command in setup.py 2019-05-04 17:51:14 +02:00
Lonami Exo
05fcbfd7b7 Add missing Union in hints.py 2019-05-04 12:39:48 +02:00
Lonami Exo
89c993f567 Add missing hints.py file for the previous commit 2019-05-04 10:21:21 +02:00
Lonami Exo
cd4b915522 Add type hints to all public methods in the client 2019-05-03 21:38:41 +02:00
Lonami Exo
c0e506e568 Add missing await on send_code_request 2019-05-03 21:38:41 +02:00
Lonami Exo
1e17ef1c98 Apply several lints 2019-05-03 21:38:41 +02:00
Lonami
52be689926
Only upgrade database if version < current
Should deal more gracefully when using new
session files in older versions of the library.
2019-05-02 23:20:39 +02:00
Lonami Exo
63ef7284f7 Use classes when type hinting primitives 2019-05-02 19:12:11 +02:00
Lonami Exo
cb56c54351 Use the entity cache in sender getter 2019-05-02 18:51:10 +02:00
Lonami Exo
1a00de6494 Add missing checks in the message for is client None 2019-05-02 18:44:28 +02:00
Lonami Exo
b58c0d3071 Stop relying on __doc__ in EntityCache
This breaks when running with python -OO (optimized) which
removes the documentation but is crucial. Instead and thanks
to fce5cfea, we can now rely on the type hints instead.
2019-05-02 10:20:49 +02:00
Lonami Exo
fce5cfea0e Update code generator to emit type hints on init methods 2019-05-02 10:19:15 +02:00
Lonami Exo
3a1496c205 Reuse code for _get_entity_pair
Less error-prone, improved the function flow for all callers,
and removed some duplicate work.
2019-05-01 17:52:32 +02:00
Lonami Exo
6d004601d0 Inline the old _load_entities code 2019-05-01 17:07:12 +02:00
Lonami Exo
e84c9847c5 Use sets instead of isinstance in StateCache too
Similar reasoning to the change for EntityCache, sets are faster
than attribute lookups (into types.) and isinstance (another global
lookup). Updating the state is also very common, so it should be
as fast as possible.
2019-05-01 16:37:54 +02:00
Lonami Exo
9a400748f7 Fix InlineBuilder only working with local files 2019-05-01 16:02:21 +02:00
Lonami Exo
22124b5ced Refactor code to fetch missing entities once again
This is another attempt at reducing CPU usage similar to:
    1b6b4a57d9

In addition it simplifies some of the code and opens up new
ideas for the state cache as well.
2019-05-01 14:02:27 +02:00
Lonami Exo
c12c65f728 Let utils.get_peer handle DialogPeer
This is important since some updates have a peer of
that type instead of just the normal Peer instance.
2019-05-01 12:49:36 +02:00
Lonami Exo
1dc6d226b7 Best-effort attempt at finding libssl (#1167) 2019-04-30 16:22:19 +02:00
Lonami Exo
1ead9757d3 Fix Conversation not allowing getting responses after ID 0 2019-04-29 08:54:10 +02:00
Lonami Exo
599a5ac3ff Fix using events.Raw after 1b6b4a5 2019-04-29 08:54:10 +02:00
Lonami
1805dc48ec
Fix-up 3ea1c9f no longer handling ssl being None 2019-04-28 14:13:29 +02:00
Lonami
3ea1c9f04b
Handle rare case when failing to load libssl (#1167) 2019-04-28 10:44:22 +02:00
Lonami Exo
1666976646 Fix-up stripped_photo_to_jpg from the previous commit 2019-04-25 20:37:48 +02:00
YoilyL
b0e96b2821 Fix stripped image downloads (#1165) 2019-04-25 20:31:52 +02:00
Lonami Exo
19664cd9cf Call self._writer.wait_closed() on disconnect
https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.wait_closed
2019-04-25 09:56:30 +02:00
Lonami Exo
01eb49d3a4 Update to v1.7.1 2019-04-24 12:46:48 +02:00
Lonami Exo
9fc719cb24 Update download_media with a new way to save thumbs (#1164) 2019-04-24 12:38:03 +02:00
Lonami Exo
21aec00e46 Fix downloading photos with download_file (#1164) 2019-04-24 12:37:19 +02:00
Lonami Exo
08f8aa3c52 Be deterministic when generating TLObjects 2019-04-24 09:40:39 +02:00
Lonami Exo
1b6b4a57d9 Attempt at reducing CPU usage after c902428
This attempt removes the forced `await` call, which could
be causing that extra usage. Some more boilerplate is needed.
2019-04-23 20:17:43 +02:00
Lonami Exo
56595e4a9c Treat all error codes as positive
This means that -500 errors will now behave like 500 errors
correctly so the -500 "No workers running" will properly be
caught and the library will retry requests by default.
2019-04-23 11:28:09 +02:00
Lonami Exo
665c844c9d Update to v1.7 2019-04-22 20:05:45 +02:00
Lonami Exo
bb23bc0fd2 Finish update to layer 98 2019-04-22 19:05:25 +02:00
Lonami Exo
013525797a Fix date_* attributes not being of type datetime 2019-04-22 17:07:08 +02:00
Lonami Exo
4e783728f9 Don't catch up on reconnect and fix typo
The feature is not ready yet.
2019-04-22 16:56:32 +02:00
Lonami Exo
8868ce14e8 Update to layer 98 2019-04-22 16:51:05 +02:00
Lonami Exo
a151d24951 Fix StateCache accessing None to_id and add logging (#1160) 2019-04-22 12:24:45 +02:00
Lonami Exo
fee0923dd1 Get difference for missing entities in channels too 2019-04-21 21:24:34 +02:00
Lonami Exo
c1880c9191 Fix pts from channels is different (#1160) 2019-04-21 13:56:14 +02:00
yegor
8edbfbdced Fix dd mode in MTProxies (#1157) 2019-04-19 22:09:22 +02:00
Lonami Exo
a7b4794585 Fix telethon.sync not syncifying takeout client 2019-04-19 13:58:37 +02:00
Lonami Exo
68bf9f76f6 Fix forward_messages for a single message when using IDs 2019-04-17 08:51:21 +02:00
Lonami
4c1555cc5f
Fix iter_dialogs missing many dialogs 2019-04-16 13:00:18 +02:00
bugchecker
0b6d766f0c Fix loading initial pts/date could be None (#1153) 2019-04-14 10:38:26 +02:00
Lonami Exo
6d83b16503 Fix CallbackQuery ignoring func 2019-04-13 16:42:53 +02:00
Lonami Exo
bd6c03e5f9 Rename grouped to as_album in forward_messages
Public comments regarding this change can be found at:
cf3a4bc658
2019-04-13 11:02:54 +02:00
Lonami Exo
20b8250037 Fix-up new sync __enter__ not handling the client directly 2019-04-13 10:55:51 +02:00
Lonami Exo
9090ede5db Reduce __enter__/__exit__ boilerplate for sync ctx managers 2019-04-13 10:53:33 +02:00
Lonami Exo
badefcec48 Add new action method to easily send chat actions like typing 2019-04-13 10:37:23 +02:00
Lonami Exo
fadc343821 Fix catch_up pts loading and remember pts per update 2019-04-13 09:12:59 +02:00
Lonami Exo
73742633bd Smarter grouping behaviour when forwarding messages 2019-04-11 15:18:15 +02:00
Lonami Exo
cf3a4bc658 Expose grouped parameter in forward_messages (default True) 2019-04-11 12:47:14 +02:00
Lonami Exo
9965cda968 Save pts and date in a tuple for immutability
This way it is easy and cheap to copy the two required values
to all incoming updates in case we need to getDifference since
the previous pts/date to fetch entities.

This is still a work in progress.
2019-04-10 21:10:34 +04:00
Lonami Exo
bec0fa414e Add missing files for the previous commit 2019-04-09 16:55:33 +04:00
Lonami Exo
b32d8307ec Fix sending albums not returning the sent messages (#1151) 2019-04-09 16:48:58 +04:00
Lonami Exo
9598e1877c Fix _get_response_message when integers are given #1151 2019-04-09 16:31:50 +04:00
Lonami Exo
2fb560624d Fix seeking on strings from c0828f5 when uploading files 2019-04-09 09:29:06 +04:00
Lonami Exo
68291c7b3d Add new errors to the list of known ones 2019-04-06 11:46:57 +04:00
Lonami Exo
29b21209bf Force fetch sender/chat if only min information was given 2019-04-05 21:28:31 +04:00
Lonami Exo
21a8a50ab9 Remove unwanted autocast in messages.discardEncryption 2019-04-05 10:31:43 +04:00
Lonami Exo
5e5fe5876a Fix mimetype for mp3 files
It was incorrectly audio/mp3, when audio/mpeg is the correct one.
This was causing sending mp3 voice notes to not work.
2019-04-04 10:10:54 +02:00
Lonami Exo
ff8349ff3f Prevent download_profile_photo from downloading arbitrary files
First of all, because it shouldn't be doing that. Second, it was
buggy and was passing the tuple returned by get_input_location to
download_file which doesn't accept tuples (instead it should be
passed the photo object so that download_file could return dc_id
and input file location itself).
2019-04-03 09:51:33 +02:00
Lonami Exo
22fcdeef7f Better get_input_entity code flow
Plenty of unnecessary exceptions were being raised and caught when
the input parameters were already correct. Furthermore, since
8abc7ade22, the in-disk cache was no
longer being used (so using usernames always reached out to memory).
2019-04-03 09:41:36 +02:00
Lonami
f95933c246
Fix downloading contacts (#1147) 2019-04-02 14:33:41 +02:00
Lonami Exo
41e4d0f788 Let forward_messages work with messages from different chats
Previously it would take the first chat it found and use the IDs
of all messages, even if they belonged to different chats, resulting
in unexpected messages to be forwarded.

Another solution would be to check that all the chats are the same,
but this solves the issue more nicely and makes it more powerful.
2019-04-02 10:46:37 +02:00
Lonami Exo
a9ab3e1211 Update forward_messages to use _get_response_message 2019-04-02 10:44:42 +02:00
Lonami Exo
c0828f590f Fix resize if needed not seeking back for image = bytes 2019-04-02 08:59:35 +02:00
Lonami Exo
85be48f42b Document misleading message.out value for events.MessageEdited 2019-04-02 08:39:19 +02:00
Lonami Exo
38900c5079 Fix CallbackQuery.edit for normal queries (0b4d649) 2019-04-02 08:37:24 +02:00
Lonami Exo
3398bee77a Handle disconnection errors more gracefully in background tasks 2019-04-01 08:46:07 +02:00
Lonami Exo
34a8140ff0 Fix MessageRead had blacklist_chat=None and not False
This was causing the checks against chats to fail. In addition
to that, before setting the attribute, it is now casted to bool
to prevent more issues like this in the future (or if users
use non-boolean values).
2019-03-31 12:15:48 +02:00
Lonami Exo
c95467ea3e Fix ._chat_peer could be None in Event.filter() 2019-03-31 12:15:48 +02:00
bugchecker
7225b7a40f Fix RequestIter.__anext__ without __ainit__ (#1142) 2019-03-29 18:32:00 +01:00
Lonami Exo
5377169db2 Call catch_up on reconnect (WIP for #1125) 2019-03-28 12:32:02 +01:00
Lonami Exo
ad963fd23e Don't clear pending_ack on disconnect
Upon reconnecting, we must make sure to send all `pending_ack`,
or Telegram may resend some responses (e.g. causing duplicated
updates).
2019-03-28 12:16:15 +01:00
Lonami Exo
a59f53d592 Load entities for new via_bot property and forward 2019-03-28 11:07:41 +01:00
Lonami Exo
39d9531483 Implement _load_entities for all events
Follow-up of c902428af1
This means that now input_chat (and even chat_id) and
similar can be safely used, without needing get_input
2019-03-28 10:47:15 +01:00
Lonami Exo
5554b414e1 Propagate the last error on reconnect, not always ConnectionError 2019-03-28 10:11:33 +01:00
Lonami Exo
7523869875 Except IOError and not ConnectionError
PySocks' errors do not subclass ConnectionError so the library
was unnecessarily logging them as unexpected, when any IOError
was actually expected.
2019-03-28 09:54:35 +01:00
Lonami Exo
c902428af1 getDifference if the event's chat was not found (WIP) 2019-03-27 16:21:17 +01:00
Lonami Exo
8abc7ade22 Use the new in-memory entity cache
This should avoid a disk access every time an input entity
is needed, which is very often. Another step for #1141.
2019-03-26 11:39:25 +01:00
Lonami Exo
4d35e8c80f Create a new in-memory cache for entities (#1141) 2019-03-26 11:27:52 +01:00
Lonami Exo
facf3ae582 Fix-up user_id for UserUpdate 2019-03-26 09:18:18 +01:00
Lonami Exo
0239852cc7 Fix UserUpdate in chats 2019-03-26 09:14:55 +01:00
Lonami Exo
9db9db1ade Fix UserUpdate not working for typing updates
The code to deal with the right chat action was there,
but the updates that could trigger that code path were
never checked.
2019-03-26 08:57:16 +01:00
Lonami Exo
e71638abf1 Document that now incoming private messages can be revoked 2019-03-25 08:46:44 +01:00
Lonami
3126533a33
Update to layer 97 2019-03-24 18:14:18 +01:00
Lonami Exo
0b4d64947b Fix CallbackQuery.edit for messages via inline queries 2019-03-23 19:25:45 +01:00
Lonami Exo
f6fe580eb7 Safer auto reconnect to prevent double connect 2019-03-22 19:01:40 +01:00
Lonami Exo
9eabca6987 Fix run_until_disconnected's call to disconnect 2019-03-22 18:20:10 +01:00
Lonami Exo
436fb64289 Prevent double autoreconnect like #1112 2019-03-22 16:21:18 +01:00
Lonami Exo
0662ead33b Properly document all optional dependencies 2019-03-21 16:55:32 +01:00
Lonami Exo
347db79979 run_until_disconnected() should disconnect() on finally 2019-03-21 12:25:19 +01:00
Lonami Exo
04ba2e1fc7 Revert disconnect() to be async again (#1133)
It's the only way to properly clean all background tasks,
which the library makes heavy use for in MTProto/Connection
send and receive loops.

Some parts of the code even relied on the fact that it was
asynchronous (it used to return a future so you could await
it and not be breaking).

It's automatically syncified to reduce the damage of being
a breaking change.
2019-03-21 12:21:00 +01:00
Lonami Exo
8f302bcdb0 Don't disconnect() on __del__ (#1133)
Destructors are not guaranteed to run. Despite having good
intentions (saving entities even if the user forgets), it
should be the user's responsability to cleanly close the
client under any circumstances.
2019-03-21 11:40:57 +01:00
Lonami Exo
2e4476a754 Workaround #1134 by early checking if proxy closes connection 2019-03-21 11:22:09 +01:00
Lonami Exo
f6c4ab6f41 Workaround #1124 (Telegram ignoring offset_date) 2019-03-18 17:36:06 +01:00
Lonami Exo
7c48857d0c Update docs for send_file/timeouts and add new known error 2019-03-18 17:34:48 +01:00
Lonami Exo
a5f5d6ef23 Minor doc updates, fixes and improvements
In particular, removed code which no longer worked, made light
theme easier on the eyes, added slight syntax highlightning,
and fixed search for exact matches.
2019-03-16 17:19:19 +01:00
Lonami
05e5becd78
Merge pull request #1126 from seriyps/mtproto-proxy-other-protocols
Implement different MTProto proxy protocols
2019-03-13 18:26:19 +01:00
Lonami Exo
598b9f25e1 Fix DialogsIter not passing the client to the built objects 2019-03-13 09:12:44 +01:00
Lonami Exo
9d5344e90d Fix-up file to media calls from edit (from 3d72c10) 2019-03-12 22:18:57 +01:00
Сергей Прохоров
43505e0aad
Use issubclass instead of direct class comparison 2019-03-12 20:25:33 +01:00
Сергей Прохоров
fef580c24b
Revert non-related change 2019-03-12 01:28:59 +01:00
Сергей Прохоров
4696dfc25e
Rework class hierarchy, try to DRY more 2019-03-12 01:12:55 +01:00
Lonami Exo
916b379c03 Work around message edits arriving too early in conversations 2019-03-11 13:03:10 +01:00
Lonami Exo
1b703e905c Don't set self._state when checking if logged in
This essentially made catch_up useless after .start().
cc #1125 since this affects catch_up.
2019-03-11 09:04:08 +01:00
Lonami Exo
8884015dae Clarify some docstrings 2019-03-10 13:29:34 +01:00
Сергей Прохоров
b873aa67cc
Implement different mtproto proxy protocols; refactor obfuscated2 2019-03-10 03:26:24 +01:00
Lonami Exo
baa8970bb6 Fix handling message payloads that are too large 2019-03-06 18:08:51 +01:00
Lonami Exo
3d72c10ea5 Reduce calls to utils.is_image 2019-03-06 09:38:17 +01:00
Lonami Exo
758556cd30 Fix upload_file not seeking streams back
This would cause issues in _cache_media since utils.is_image fails
in the second pass (it respects the stream's position, and the user
may rightfully pass a stream that should be read only from one pos).
2019-03-06 09:24:50 +01:00
Lonami Exo
fcfebf75a3 Prevent pillow from closing non-exclusive fps (#1121) 2019-03-06 09:14:06 +01:00
Lonami Exo
ae8f1fed05 Treat arguments with _until or _since in their name as dates 2019-03-06 08:38:21 +01:00
Lonami Exo
0f69455dc7 Handle hachoir metadata more gracefully, bump 1.6.2
Since bf11bbd _get_extension supports plenty more things,
which hachoir cannot deal with. Add some extra safety checks.
2019-03-04 08:58:32 +01:00
Lonami Exo
6799295115 Fix iter_participants in non-channels 2019-03-02 21:17:36 +01:00
Lonami Exo
4cc2a17765 Deal with usability in methods that hit flood wait 2019-03-02 19:40:39 +01:00
Lonami Exo
dd1ca16ded Update docs, usability and errors for all methods 2019-03-02 19:01:20 +01:00
Wirtos
c4d65f8bf4 ValueError fix for IOBase files (#1119) 2019-03-01 21:27:15 +01:00
Lonami Exo
df534585e9 Actually fix ids= not being a list, bump 1.6.1 2019-02-28 08:31:28 +01:00
Lonami Exo
2681dc09bb Fix iter_messages with ids= not being a list 2019-02-28 08:26:37 +01:00
Manuel1510
70e0d865a8 Fix RequestIter.__next__ propagating StopAsyncIteration (#1117) 2019-02-28 08:07:31 +01:00
Lonami Exo
8429f9bd3c Update to v1.6 2019-02-27 21:04:18 +01:00
Lonami Exo
934c733ccb Treat users "kicking themselves" as leaving (#1116) 2019-02-27 19:42:17 +01:00
Lonami Exo
05d174d4ce Update documentation, errors and add TODOs 2019-02-27 19:30:12 +01:00
Lonami Exo
f66d65d409 Add RequestIter._next__ for synchronous iteration 2019-02-27 16:13:11 +01:00
Lonami Exo
6b50152bb3 Use constants for chunk sizes, remove irrelevant TODO 2019-02-27 13:07:25 +01:00
Lonami Exo
d02d0e2d5e Handle negative limits gracefully in async generators
We rely on >= 0 for setting the batch size to use (which must
be valid), so it makes sense to make negative limits equal 0.

This is similar to how asyncio.sleep(negative) sleeps 0 seconds,
despite the fact that time.sleep(negative) fails.
2019-02-27 13:01:04 +01:00
Lonami Exo
d508e58d49 Remove the "private" _total parameter 2019-02-27 12:59:50 +01:00
Lonami Exo
bf71e49fcc Remove batch_size parameter from iter_messages
It was only useful for testing purposes, and no other methods
exposed this kind of parameter (but they still use it).
2019-02-27 12:57:54 +01:00
Lonami Exo
b9133567af Remove weird map chr range from aggressive iter_participants 2019-02-27 12:51:09 +01:00
Lonami
c99157ade2
Merge pull request #1115 from LonamiWebs/requestiter
Overhaul asynchronous generators
2019-02-27 12:49:12 +01:00
Lonami Exo
c73b8eda26 Simplify filling RequestIter's buffer 2019-02-27 11:25:35 +01:00
Lonami Exo
202ce1f494 Remove async_generator from dependencies 2019-02-27 11:13:29 +01:00
Lonami Exo
40ded93c7c Use RequestIter in chat methods 2019-02-27 11:12:05 +01:00
Lonami Exo
4f647847e7 Fix RequestIter not setting TotalList.total in collect() 2019-02-27 10:39:56 +01:00
Lonami Exo
968da5f72d Use RequestIter in the dialog methods 2019-02-27 10:38:50 +01:00
Lonami Exo
49d8a3fb33 Remove code to syncify async generator functions 2019-02-27 10:19:08 +01:00
Lonami Exo
5b8e6531fa Add method to collect RequestIter into TotalList 2019-02-27 10:15:32 +01:00
Lonami Exo
6d6c1917bc Implement iterator over message by IDs 2019-02-27 10:04:12 +01:00
Lonami Exo
60606b9994 Don't make iter_messages a coroutine function 2019-02-27 09:49:14 +01:00
Lonami Exo
35dc46ffb0 Fix searching messages in reverse 2019-02-27 09:48:47 +01:00
Lonami Exo
e3991fadd5 Fix updating offset 2019-02-27 09:37:12 +01:00
Lonami Exo
f765f73fa3 Fix setting batch size 2019-02-27 09:32:33 +01:00
Lonami Exo
e2f44ddbea Make iter_messages use a common message iterator 2019-02-27 09:31:15 +01:00
Lonami Exo
19f38d6733 Implement iter_messages with search 2019-02-26 21:04:46 +01:00
Lonami Exo
36eb1b1009 Create a new RequestIter ABC to deal with iter methods
This should make it easier to maintain these methods, increase
reusability, and get rid of the async_generator dependency.

In the future, people could use this to more easily deal with
raw API themselves.
2019-02-26 20:26:40 +01:00
Lonami Exo
1e4a12d2f7 Clean up iter_messages with reverse=True 2019-02-26 09:42:28 +01:00
Lonami Exo
5b098a909a Cleanup converting to input media in send album 2019-02-25 18:25:49 +01:00
Lonami Exo
8e36c0002b Fix sending albums with bot file IDs 2019-02-25 18:23:39 +01:00
Lonami Exo
70b08c4952 Get rid of broken JSON migration support (#1113) 2019-02-24 20:16:53 +01:00
Lonami Exo
0934f71c02 Update to layer 95 2019-02-21 12:40:47 +01:00
Lonami Exo
abadf3c789 Provide a blanket implementation for _init_conn 2019-02-21 10:43:31 +01:00
Lonami Exo
6de7329ce7 Fix Connection abstraction leak 2019-02-21 10:41:33 +01:00
Lonami Exo
96270bdc18 Fix sending PNGs without alpha channel, edit size typo 2019-02-19 17:48:27 +01:00
Lonami Exo
6db60627e6 Clearer error on send_file(chat, invalid type) 2019-02-19 16:41:51 +01:00
Lonami Exo
f540c4e089 Fix order of inline results not being preserved 2019-02-14 20:03:02 +01:00
Lonami Exo
628a16f287 Document new errors and limits for inline results 2019-02-14 19:45:25 +01:00
Lonami Exo
41bfb8ae52 Fix photo resizing not working for images with alpha channels 2019-02-14 12:12:17 +01:00
Lonami Exo
d1c755809d Except RpcMcgetFailError as call fail and sleep on server errors
This one in particular may happen when iterating over messages,
perhaps more likely to happen if the group was created in a
different data center.

A small sleep of a few seconds also greatly increases the
chances of the error going away.
2019-02-14 11:00:37 +01:00
nosanity
2e544270cd Fix login failure due to wrong argument name (#1109) 2019-02-14 16:46:57 +10:00
Lonami Exo
5bb2d9adf3 Document common usages for upload_file 2019-02-13 12:50:14 +01:00
Lonami Exo
559a40c7ea Fix get_input_media for InputFile
It was actually using FileLocation, which had the invalid expected
type for what was being returned. Instead it should have been this
other type.

In addition, more parameters are passed so that the method can have
all the information it needs to correctly cast the type.
2019-02-13 12:33:11 +01:00
Lonami Exo
99ad26bfa4 Lower the maximum amount of requests per container 2019-02-13 10:16:59 +01:00
Lonami Exo
5c85f830bd Further document automatic photo resizing 2019-02-13 09:58:02 +01:00
Lonami Exo
fd24f7087e Resize photos when sending files if needed 2019-02-13 09:50:00 +01:00
Lonami Exo
5772a5483c Check dc_id in resolve_bot_file_id 2019-02-13 09:16:34 +01:00
Lonami Exo
47d9de98ed Raise ImportError and not ValueError when sqlite3 is missing
Excepting ValueError when creating the SQLiteSession could hide
other errors (e.g. using a newer session file on an older version
of the library). Instead, the original error is raised, as if
sqlite3 was being imported within its __init__ method.
2019-02-13 08:52:54 +01:00
Dmitry D. Chernov
d25442345e Fix incorrect sending of DC id when connecting to MTProxy 2019-02-12 21:17:08 +10:00
Lonami Exo
4899788d92 Expose phone and phone_code_hash in sign_up 2019-02-12 11:44:36 +01:00
Lonami Exo
bf11bbd8a6 Support sending bytes/stream as photos (improved _get_extension)
Letting _get_extension work for photos even when the only information
available is a stream or a few bytes allows sending arbitrary bytes
as photos, if force_document was not set to True explicitly.
2019-02-12 11:33:06 +01:00
Lonami Exo
8f8ae9aee5 Update URLs in assistant and except message deletion errors 2019-02-12 10:48:02 +01:00
Lonami Exo
1007e19172 Fix get_peer_id not working with InputPeerSelf 2019-02-11 10:40:35 +01:00
Dmitry D. Chernov
3b4d00564d Detail docstring about the 'proxy' parameter in TelegramClient 2019-02-11 18:54:35 +10:00
Dmitry D. Chernov
45d0ba9e2f Initial implementation of MTProxy support (#1107) 2019-02-11 00:16:46 +01:00
Dmitry D. Chernov
45fdd098cc Docs: Removed mention of TcpClient from 'Project Structure' 2019-02-11 08:45:33 +10:00
Dmitry D. Chernov
8c428e8566 Fix broken connection establishment in ConnectionTcpObfuscated
This regression was introduced in ebde3be820.
2019-02-11 07:54:59 +10:00
Dmitry D. Chernov
c39cc06908 Fix incorrect check for reserved data prefix in ConnectionTcpObfuscated 2019-02-11 05:47:09 +10:00
Lonami Exo
de84bf08bf Support MessageMedia in pack_bot_file_id 2019-02-10 12:30:20 +01:00
Lonami Exo
e23308c0f9 Fix accessing conversation messages that arrived too early 2019-02-10 11:46:39 +01:00
Dmitry D. Chernov
9a98d41a2c Improve TakeoutClient proxy and takeout functionality (#1106) 2019-02-10 11:10:41 +01:00
Dmitry D. Chernov
274fa72a8c Correctly proxy __class__ in TakeoutClient for #1103 (#1105) 2019-02-07 16:51:16 +01:00
Lonami Exo
abd26af0ad Update misleading error message 2019-02-07 10:16:03 +01:00
Dmitry D. Chernov
fc2977fc0d Tiny style fixes 2019-02-07 06:55:34 +10:00
Dmitry D. Chernov
c8f16a4e89 Fix a couple of inconsistencies in the public interface (#1102)
* Create `_NOT_A_REQUEST` when needed. Currently, modifications    
  in the raised exception would be "global".    
* `retries` parameters were actually attempts. This has been fixed    
  to actually be the amount of retries, so 0 now means don't retry.    
* Helper function to deal with retries instead of using a range with    
  different styles every time.
2019-02-06 19:41:45 +01:00
Thorbijoern
c9e9b82eac Update resolve_bot_file_id to latest layer (#1100)
It is now possible to replace the dummy image with None.
2019-02-03 00:14:39 +01:00
Lonami Exo
b79c4510a6 Update to v1.5.5 2019-02-02 11:36:38 +01:00
Lonami Exo
c2c8a3caeb Saner timeouts for conversation (#1099) 2019-01-31 11:11:20 +01:00
Lonami Exo
8492300780 Fix search in private chats when from_user was not given 2019-01-31 11:11:20 +01:00
Lonami Exo
cf867954c3 Support any of /sS to focus docs search 2019-01-26 13:15:27 +01:00
Lonami Exo
c11d71c3cd Fix docs generation on Windows and search bar 2019-01-26 13:15:27 +01:00
Lonami Exo
c1ae7d67a9 Update readthedocs with the new ChatAdminRights 2019-01-24 14:12:35 +01:00
Lonami Exo
10cd61d2f9 Add missing timezone info in important places
Things like SQLAlchemy work correctly only for timezone-aware datetimes.
The returned TLObjects all have them, but those that are manually created
were missing them, so serializing the state into SQLAlchemy sessions failed.
2019-01-24 11:16:40 +01:00
Lonami Exo
86a8928278 Better error diagnostics and fix parsing errors.csv 2019-01-24 11:02:58 +01:00
Lonami Exo
d1fee27814 Register several known mimetypes (#1096) 2019-01-23 14:37:40 +01:00
Lonami Exo
33742c809a Document BANNED_RIGHTS_INVALID 2019-01-23 14:37:40 +01:00
Kirill
747caf1652 Support Path-like values for thumb in _file_to_media (#1095) 2019-01-22 18:52:53 +01:00
Lonami Exo
bb5c1f24c6 Update to layer 93 2019-01-21 20:09:13 +01:00
Lonami Exo
8f44364c6c Add supports_streaming to send_file and update docs/errors 2019-01-21 19:47:17 +01:00
Lonami Exo
b0883148f5 Document more RPC errors
These two occur when replying to an inline query with a photo and
InputBotInlineMessageMediaAuto empty message, and passing URLs to
PNGs that may not be accessible (i.e. 403 from pixiv).
2019-01-17 16:51:50 +01:00
Lonami Exo
09e58c4e53 Add more missing loggers parameters #1089 2019-01-15 11:09:08 +01:00
Lonami Exo
61593279c9 Update to v1.5.3 2019-01-14 14:26:14 +01:00
Lonami Exo
51f0bf5d84 Fully support email in edit_2fa 2019-01-14 13:57:07 +01:00
Lonami
21545c56bb
Add yet another missing file_reference 2019-01-13 20:40:51 +01:00
Lonami
be4f89740a
Fix resolve_bot_file_id for photos 2019-01-13 20:38:16 +01:00
Lonami Exo
5ac88f764d Fix and update links in the documentation 2019-01-12 13:06:14 +01:00
Lonami Exo
fbf7f75b00 Fix send_message with buttons not always returning ReplyMarkup 2019-01-12 12:53:21 +01:00
Lonami Exo
d31aaa5d0d Make Message.edit respect preview and buttons 2019-01-12 12:45:37 +01:00
Lonami Exo
ae4d4ba3ef Fix-up missing loggers from f271316 2019-01-12 12:15:29 +01:00
Tulir Asokan
f271316d7d Make logger fully configurable (#1087) 2019-01-11 15:52:30 +01:00
Manuel1510
eda4178333 Add hide_via to InlineResult.click (#1084) 2019-01-10 23:42:58 +01:00
Lonami Exo
ca2c8687c8 Expect UserEmpty on get_input_entity 2019-01-08 10:58:58 +01:00
Lonami Exo
d892a537a7 Support get_peer on participants and clarify some strings 2019-01-07 16:13:37 +01:00
Lonami Exo
00c8aa847d Tidy examples and update licenses 2019-01-07 15:48:25 +01:00
Lonami Exo
b57e3e3e0a Fix some open calls were not being made through pathlib
This was causing the documentation to fail to build under Python 3.5.
2019-01-06 21:36:32 +01:00
Lonami Exo
e565552ae9 Update to v1.5.2 2019-01-05 17:37:52 +01:00
Lonami Exo
a5b107e6c9 Update documentation with takeout, add new known errors 2019-01-05 17:31:26 +01:00
Lonami Exo
0a147d5b95 Add new takeout method 2019-01-05 17:18:40 +01:00
Lonami Exo
1544577757 Don't raise inside __del__ (#1073) 2019-01-04 11:26:57 +01:00
Lonami Exo
f99b4874c8 Support passing media objects to inline results (#1053) 2019-01-04 11:15:11 +01:00
Lonami Exo
4b9b77614f Support answering inline queries with empty text (#1053) 2019-01-04 11:13:13 +01:00
Lonami Exo
8c0250f775 Locally check dialog's offset date (#1043)
Telegram ignores the parameter if it's alone.
2019-01-03 22:35:07 +01:00
Lonami Exo
c51a17bf9a Actually make AdminLogEvent work
Ideally this would have been tested before release one commit ago.
2019-01-03 22:25:50 +01:00
Lonami Exo
77be6a2755 Update to v1.5.1 2019-01-03 19:43:48 +01:00
Lonami Exo
72927a4ec3 Remove full API examples that have a friendly counterpart 2019-01-03 19:23:28 +01:00
Lonami Exo
1b424b3fe7 Update documentation to reflect new methods 2019-01-03 19:20:52 +01:00
Lonami Exo
eda8d0dbc8 Support configuring the reply markup through buttons 2019-01-03 19:03:47 +01:00
Lonami Exo
2631144702 Add more properties to the client to make media more convenient 2019-01-03 18:34:29 +01:00
Lonami Exo
4ccabaf422 Add friendly method to get admin log (#952) 2019-01-03 13:09:59 +01:00
Lonami Exo
95cf873bad Don't mark the user as connected until successfull connection
The idea behind distinguishing between the user being connected and
the actual connection having been made was to support retries while
the user decided to connect, even if the connection wasn't made yet.

The problem is that _user_connected is used directly to tell whether
the sender is connected or not which will be wrong if an exception
occurs on the initial connection. Since the logic retry isn't used
there we can simply mark as connected once a successfull connection
is made.
2019-01-02 10:53:10 +01:00
Lonami Exo
229969192a Fix UnicodeDecodeError with malformed input on unparse text 2019-01-01 20:31:39 +01:00
Lonami Exo
619e4dc2d6 Fix get_peer for int which made ab557a8 useless 2018-12-28 17:54:15 +01:00
Lonami Exo
4ad9c9bf31 Add TLObject.to_json() for convenience 2018-12-27 20:15:35 +01:00
Lonami Exo
f1157b8fd1 Return downloaded bytes when file=bytes 2018-12-27 19:05:52 +01:00
Lonami Exo
342a4dd502 Update to v1.5 2018-12-25 17:55:15 +01:00
Lonami Exo
fc46e1ec20 Workaround file references by using empty byte strings for cache
They seem to work for now, so until there is a need to update it,
cache will just rely on empty byte strings for the file_reference.
2018-12-25 17:02:33 +01:00
Lonami Exo
b66c1e6084 Revert "Get rid of now broken cache"
This reverts commit f73ae42a03.
2018-12-25 16:50:11 +01:00
Lonami Exo
100cd807f6 Better GetAdminLogRequest example 2018-12-25 14:17:19 +01:00
Lonami Exo
a6c3c382b4 Except MessageIdsEmptyError when getting messages by ID
This error may occur even when the IDs are not actually empty.
2018-12-25 12:17:24 +01:00
Lonami Exo
f73ae42a03 Get rid of now broken cache 2018-12-25 11:44:10 +01:00
Lonami Exo
382106aafb Check g when password's prime is known to be good 2018-12-25 10:59:16 +01:00
Lonami Exo
18f457a41d Add missing file_reference to InputDocument 2018-12-25 01:04:45 +01:00
Aaron Gallagher
b0e0bc3701 Extend new_algo.salt1 to fix edit_2fa (#1072)
The salt1 that is sent to the server requires an additional 32 bytes
of random data. It's easy to miss this requirement from reading the 
tdesktop source, because this extension is done in a function called
`ValidateNewCloudPasswordAlgo`.

https://github.com/telegramdesktop/tdesktop/blob/2e5a0e056cdb40d61d487c6062bffe1a835f
6ddd/Telegram/SourceFiles/core/core_cloud_password.cpp#L210-L211
2018-12-24 21:16:09 +01:00
Lonami Exo
deb6ca0da0 Fast-path good known prime in 2fa 2018-12-24 20:17:31 +01:00
Lonami Exo
fb9796a293 Pre-create directory structure when generating docs
This reduces the amount of system calls
from thousands to a few dozens at most.
2018-12-24 18:16:18 +01:00
Lonami Exo
ffc0a8808b Revert docs search to use onkeyup or last keystroke is ignored 2018-12-24 18:04:13 +01:00
Lonami Exo
de74711e82 Implement click for KeyboardButtonGame 2018-12-24 17:56:30 +01:00
Lonami Exo
f16ed8235c Add new is_bot method to check if the logged-in user is a bot 2018-12-24 17:32:34 +01:00
Lonami Exo
ab557a8cef Use getUsers/getChannels with hash 0 on get_input_entity 2018-12-24 17:32:16 +01:00
Lonami Exo
46fea3fc93 Implement clearing password in edit_2fa 2018-12-24 16:30:45 +01:00
Lonami Exo
6823b6c691 Remove old code to get the hash of a password 2018-12-24 14:47:39 +01:00
Lonami Exo
d2ac7e5b0a Actually perform all checks in 2fa 2018-12-24 14:16:50 +01:00
Lonami Exo
a623006ea0 Update 2fa code to layer 91 2018-12-24 14:10:15 +01:00
Lonami Exo
24986bbea0 Update to layer 91 2018-12-23 11:03:14 +01:00
Lonami Exo
aefa429236 Make complete use of pathlib (fix up 8224e5a) 2018-12-21 18:18:18 +01:00
Lonami Exo
8224e5aabf Make use of pathlib nearly everywhere (breaks docs gen)
Python 3.6 introduced support for the os.PathLike interface,
which means Python 3.5 did not have it yet and attempting to
use it in os functions would fail. Instead we can use pathlib
for everything, but not all work is done yet.
2018-12-21 13:24:16 +01:00
Lonami Exo
b9d4eb5449 Default to dark theme to avoid screen flashing and fix typo 2018-12-21 10:56:40 +01:00
Lonami Exo
e852dccebf Prevent common pitfall when pulling new .tl files 2018-12-20 20:52:19 +01:00
Lonami Exo
2aa089f29c Prevent KeyError in forward_messages 2018-12-20 20:33:25 +01:00
Kyle2142
d3feaeedb2 Prevent caption=None from crashing (#1071) 2018-12-20 10:50:16 +01:00
Lonami Exo
7100b75598 Update to layer 89
This breaks edit_2fa
2018-12-18 16:37:07 +01:00
Lonami Exo
09f994c105 Except TypeError on empty access hash from 5018879 thoroughly 2018-12-18 08:39:36 +01:00
Lonami Exo
6839bfcc2d Redirect to first term when pressing enter on docs search 2018-12-16 10:40:31 +01:00
Lonami Exo
e71c556ca7 Use pathlib.Path in setup.py 2018-12-15 13:26:18 +01:00
Lonami Exo
da5c171346 Skip core types when parsing .tl files
This avoids the need to comment then every time the file changes.
2018-12-15 12:38:56 +01:00
Lonami Exo
5018879f84 Raise TypeError in get_input_peer if access_hash is None
When auto-casting to input peers if the access hash is now None,
the ID will be used to look-up a cached access hash which will
solve some common pitfalls (using full entities when only the
input variant should and could be used).
2018-12-15 12:23:14 +01:00
Lonami Exo
7ee7b43547 Be explicit that phone numbers only work if in your contacts 2018-12-15 12:04:36 +01:00
Lonami Exo
932d3bd033 Improve autogenerated examples with more cases and synonyms 2018-12-15 11:47:52 +01:00
Lonami Exo
34879a4b35 Use decodeURI when loading GET parameters 2018-12-15 11:36:54 +01:00
Lonami Exo
027d08cec7 Slightly smarter search generated docs 2018-12-15 11:36:54 +01:00
Lonami
52b179dba8
Never check channel constructor when generating objects 2018-12-14 17:31:15 +01:00
Sister Midnight
f3013c6817 Fix ConnectionHttp SSL socket wrap (#1064) 2018-12-10 14:43:48 +01:00
Lonami Exo
c48d41d99d Expect ConnectionError in the send loop 2018-12-06 16:27:09 +01:00
Lonami Exo
f9fc433c0f Better catch_up behaviour when invalid states are present 2018-12-06 16:07:11 +01:00
Lonami Exo
40730e7862 Fix aaee092 to correct entities' offsets on stripping 2018-12-06 12:33:15 +01:00
Lonami Exo
424079aa12 Fix pts initialized as -1 not being considered 2018-12-04 21:07:18 +01:00
Lonami Exo
0b89d9d3f9 Update to v1.4.3 2018-12-04 14:09:17 +01:00
Lonami Exo
50b77d881d Clarify who can use methods in the documentation 2018-12-03 16:17:37 +01:00
Lonami Exo
e4cfd964d5 Use an enum for methods' usability 2018-12-03 15:56:38 +01:00
Lonami Exo
4901447ad6 Remove now unused code to fetch errors from remote host 2018-12-03 15:53:08 +01:00
Lonami Exo
01848de7e2 Move error capture names to errors.csv 2018-12-03 11:08:20 +01:00
Lonami Exo
4d8f078ad9 Fix _document_by_attribute calls using cond 2018-12-03 08:49:24 +01:00
Lonami Exo
b8912ea0aa Add two MEDIA_*_INVALID to known errors 2018-11-30 21:54:00 +01:00
Lonami Exo
8eca29be25 Make use of the new method info when generating docs 2018-11-30 21:48:15 +01:00
Lonami Exo
f05109f186 Update code generator to parse CSV files 2018-11-30 21:39:13 +01:00
Lonami Exo
f646863149 Replace error and method data with CSV files 2018-11-30 21:38:36 +01:00
Lonami Exo
ed1bcb509f Use InputMessageReplyTo in get_reply_message
This lets bots access to messages other bots sent through replies.
2018-11-29 14:21:49 +01:00
Lonami Exo
9a6f4d35f2 Fix crash on get_messages(ids=InputMessageReplyTo) 2018-11-29 14:19:56 +01:00
Lonami Exo
10b9b4b969 Handle TimeoutError on automatic reconnect 2018-11-29 13:11:34 +01:00
Lonami Exo
4a8a85d7a6 Refetch ChatAction service message to get input users 2018-11-28 18:59:49 +01:00
Lonami Exo
16dd47f5ec Update to v1.4.2 2018-11-24 20:53:28 +01:00
Lonami Exo
d2e995ef95 Change code to recv and handle disconnections 2018-11-24 20:53:28 +01:00
Lonami Exo
09b16f96fc Better behaviour for send_read_acknowledge 2018-11-24 20:53:28 +01:00
painor
7e168cb4fb Remove invalid mentions from message entities (#1049) 2018-11-24 19:39:05 +01:00
udf
5b2cfffedc make utils.get_peer_id raise with message about InputPeerSelf 2018-11-23 16:39:31 +01:00
udf
9362f03fd5 fix utils.get_peer not raising in some cases 2018-11-23 16:39:31 +01:00
Lonami Exo
d214c78bd0 Add missing @property decorator 2018-11-19 12:06:18 +01:00
Lonami Exo
aaee092a46 Locally strip outgoing message text respecting entities 2018-11-19 10:15:56 +01:00
Lonami Exo
d854babf22 Make HTML parser yield the correct offset and lengths 2018-11-19 09:20:13 +01:00
Lonami Exo
d6ec883cd9 Ignore iter_messages from_user unless it's a User 2018-11-19 08:55:49 +01:00
Lonami Exo
8ed3ddba31 Clarify iter_participants filter parameter in the docs 2018-11-19 08:38:31 +01:00
Lonami Exo
207d5ebdcb Avoid packing more than 1024 messages in a single container 2018-11-19 08:29:44 +01:00
Lonami Exo
f90dd76f4c New assistant command and check if hastebin is down 2018-11-15 17:54:51 +01:00
Lonami Exo
0f7c2b891a Fix get entity by exact name since 2ca8ddb 2018-11-15 17:23:47 +01:00
Lonami Exo
924b59d735 Actually fix cancelling custom conversations from f6bc80b 2018-11-12 08:21:18 +01:00
Lonami Exo
dfab8f5e20 Avoid caching event loop in telethon.sync
This would cause issues when creating and setting a different
event loop from a different thread since it would use the old
loop and not the new one, solved by explicitly getting the
loop every time, although this has a slight performance hit.
2018-11-10 11:34:26 +01:00
Lonami Exo
f046d1f0a3 Fix deadlock on unexpected disconnections 2018-11-10 11:23:19 +01:00
Lonami Exo
f6bc80bc6b Fix cancellation of custom conversation events from d474458 2018-11-08 10:08:41 +01:00
Lonami Exo
cdbd1f6193 Fix valid auth_key never being saved after switching DC 2018-11-03 18:53:26 +01:00
Lonami Exo
0fc97a3e8c Update to v1.4 2018-11-03 12:46:19 +01:00
Lonami Exo
8e6b98669a Get rid of full_sync 2018-11-03 12:46:19 +01:00
Lonami Exo
c70943bb0e Add CHAT_RESTRICTED to known errors 2018-11-03 12:46:19 +01:00
K900
20a8081e8e Fix file cache when using MemorySession (#1033) 2018-11-01 10:29:23 +01:00
Lonami Exo
67c5572d7b Possibly fix possible deadlock 2018-10-28 11:53:28 +01:00
painor
45999001be Added retry_delay parameter for auto-reconnection (#1031) 2018-10-28 10:55:58 +01:00
Lonami Exo
15546fd957 Update client package documentation missing some subclasses 2018-10-28 10:21:03 +01:00
Lonami Exo
24970a875a Fix raising PasswordHashInvalid missing request arg too 2018-10-27 09:40:30 +02:00
Lonami Exo
2863eb8cc9 Fix raising local FloodWait missing request argument 2018-10-27 09:39:00 +02:00
Lonami Exo
5beaf46e09 Add extra checks to the #haste command 2018-10-26 10:49:02 +02:00
Lonami Exo
b0e587c03d Except expected ConnectionError in the receive loop 2018-10-26 10:36:33 +02:00
Lonami Exo
0d8b15a109 Add #haste command to assistant and fix typo 2018-10-26 10:25:29 +02:00
Lonami Exo
091180b32d Except CancelledError in MTProtoSender send/recv loop
This way, the tasks should not end with the cancelled exception
which should get rid of the warning if any.
2018-10-25 15:54:25 +02:00
Kyle2142
eacfa226fd Add Conversation.cancel() (#1030) 2018-10-25 15:37:36 +02:00
Lonami Exo
2c61c50671 Save session on connect and fix some typos
The session is saved upon connecting with the hope to make
sure that the authorization key gets saved to disk before
the next run.
2018-10-24 16:39:59 +02:00
Lonami Exo
4562ba9ccf Fix auth_key not actually being saved
This would make the user have to login every time.
2018-10-22 20:58:07 +02:00
Lonami Exo
7dece209a0 Cancel tasks on reconnect instead of awaiting them
This prevents us from locking forever on any task that doesn't
rely on cancellation tokens, in this case, Connection.recv()'s
_recv_queue.get() would never complete after the server closed
the connection.

Additionally, working with cancellation tokens in asyncio is
somewhat annoying to do.

Last but not least removing the Connection._disconnected future
avoids the need to use its state (if an exception was set it
should be retrieved) to prevent asyncio from complaining, which
it was before.
2018-10-21 16:20:05 +02:00
Lonami Exo
f2e77f4030 Fix using the wrong logger in Connection 2018-10-21 16:10:09 +02:00
Lonami Exo
740a715acd Handle empty code when starting 2018-10-21 16:05:31 +02:00
Lonami Exo
0755421fc2 Fix update loop should not disconnect causing race condition
When starting the client, on phone migrations, it would
disconnect twice right after connecting which would
trigger a not-connected error.
2018-10-21 16:02:13 +02:00
Ivan Tung
c248a102dc Fix-up 99129da error generation if error.has_captures (#1025) 2018-10-21 15:22:46 +02:00
Lonami Exo
396ec908dd Fix _borrow_exported_sender connection 2018-10-20 17:11:40 +02:00
Lonami Exo
f51b56558d Document AUTH_KEY_DUPLICATED, better assistant UX 2018-10-19 19:32:17 +02:00
Lonami Exo
39929db2b8 Patch __enter__/__exit__ specially in full_sync 2018-10-19 18:04:53 +02:00
Lonami Exo
a2bae8374e Fix .get_entity('me') not working from c31635c (#1024) 2018-10-19 16:57:46 +02:00
Lonami Exo
0686ec4440 Implement after_msg_id back 2018-10-19 16:53:50 +02:00
Lonami Exo
9dc4009152 Handle the right errors 2018-10-19 14:41:50 +02:00
Lonami Exo
b93b01cb02 Fix manual BadMessageError instantiation 2018-10-19 14:02:20 +02:00
Lonami Exo
6b280dc3bd Clarify MTProtoSender._disconnected 2018-10-19 14:01:03 +02:00
Lonami Exo
945b34b103 Set MTProtoSender.auth_key on its creation 2018-10-19 13:56:11 +02:00
Lonami Exo
8563b9560d Fix auth_key not being set 2018-10-19 13:35:29 +02:00
Lonami Exo
939854a0dd Fix-up e2fe3eb (packer was OK, encryption was not made) 2018-10-19 13:33:24 +02:00
Lonami Exo
f5bc952309 Don't set the disconnected flag twice 2018-10-19 13:25:00 +02:00
Lonami Exo
e2fe3eb503 Use new broken MessagePacker 2018-10-19 13:24:52 +02:00
Lonami Exo
83f60deef9 Allow updating AuthKey.key
This will preserve references to the AuthKey more cleanly.
2018-10-19 12:40:36 +02:00
Lonami Exo
6d30a38316 Let Connection._disconnected be a proper Future
This means that awaiting on disconnect will properly raise errors,
allowing to differentiate clean disconnects from faulty ones.
2018-10-19 10:46:34 +02:00
Lonami Exo
542d0f539b Revisit connection and properly detail the class' intent 2018-10-19 10:35:22 +02:00
Lonami Exo
e5fc9d8674 Fix several broken links in the docs 2018-10-17 11:50:11 +02:00
Lonami Exo
a9a2401e44 Fix CallbackQuery using string regex 2018-10-17 11:44:08 +02:00
Lonami Exo
419fe6dca3 Raise RuntimeError on with blocks that need async 2018-10-17 11:30:07 +02:00
Lonami Exo
9cbc088b76 Make disconnect synchronous
This also fixes a bug when auto-reconnecting in MTProtoSender.
2018-10-16 11:56:17 +02:00
Lonami Exo
477fbd8dc7 Generate simple examples for the docs 2018-10-16 09:29:48 +02:00
Lonami Exo
8f04ec820f Don't add functions as possible argument types 2018-10-15 21:12:10 +02:00
Lonami Exo
bb180a1db8 Split generator/tlobject into separate files 2018-10-15 19:29:32 +02:00
Lonami Exo
e3c4bd46fb Reference argument types to objects when parsing .tl 2018-10-15 19:23:09 +02:00
Lonami Exo
b219da6b87 Fix Dialog.is_group for types.ChatForbidden 2018-10-15 18:43:53 +02:00
Lonami Exo
34cefed9ff Except BotMethodInvalidError on getContacts 2018-10-15 18:42:35 +02:00
Lonami Exo
99129daeee Show the request that caused RPC errors in their messages 2018-10-15 18:35:51 +02:00
Lonami Exo
ee4c952290 Add descriptions for new RPC errors 2018-10-15 17:57:16 +02:00
Lonami Exo
0094eb391e Support exclusive conversations by default 2018-10-12 22:17:07 +02:00
Lonami Exo
cf6686ff42 Revert async sessions from 3dd8b7c (breaks sync properties)
Such as ChatGetter.input_chat and SenderGetter.input_sender
which rely on the fact that access to the session file will
be synchronous.
2018-10-12 22:00:02 +02:00
Lonami Exo
e677a6bb05 Fix is_user_authorized after log_out 2018-10-12 19:57:16 +02:00
Lonami Exo
6e77f583f1 Detect arbitrary negative HTTP error codes 2018-10-12 19:47:40 +02:00
Lonami Exo
cca7055fcf Add inline mode to the assistant example 2018-10-12 13:33:55 +02:00
Lonami Exo
74f7ae525f Document InlineBuilder 2018-10-12 12:38:46 +02:00
Lonami Exo
9ee415749d Fix InlineBuilder.photo/document and clearer errors 2018-10-12 11:50:36 +02:00
Lonami Exo
32c884d543 Revert d392939 and use empty tuple as a sentinel value 2018-10-08 11:33:56 +02:00
Lonami Exo
27345d5749 Work around importing sync and using full_sync 2018-10-08 11:13:24 +02:00
Lonami Exo
26c773392e Explain run_until_disconnected in the docs 2018-10-07 11:18:40 +02:00
Lonami Exo
2d94a104d1 Syncify events too 2018-10-06 21:08:38 +02:00
Lonami Exo
ded24db3dd Fix proxy support by removing conflicting arguments 2018-10-06 20:56:09 +02:00
Lonami Exo
8c14259728 Update documentation with new sections 2018-10-06 20:20:11 +02:00
Lonami Exo
fb9660afe0 Add missing awaits 2018-10-06 12:39:07 +02:00
Lonami Exo
67be6418b6 Remove callback parameter from custom.Button
Its behaviour was strange. Removing and adding an event
handler every time a message is sent is not a good idea
and it would just do more harm than good.
2018-10-05 20:54:50 +02:00
josephbiko
3dd8b7c6d1 Support async def in sessions (#1013) 2018-10-05 20:25:49 +02:00
Tulir Asokan
653f3c043d Add full_sync module (#1016) 2018-10-05 19:59:56 +02:00
Lonami Exo
730cf31921 Delete all (broken) tests 2018-10-05 19:36:01 +02:00
Lonami Exo
d392939018 Create a default module to use as a sentinel value
This looks better in the documentation than utils.Default,
cleans the utils with specific stuff like this, and users
may use it more easily.
2018-10-05 14:20:50 +02:00
Lonami Exo
0fcc2e5e52 Add autocast for InputDocument and InputChatPhoto 2018-10-05 14:11:47 +02:00
Lonami Exo
aa3f26263c Fix timeout not accepting timedelta 2018-10-05 14:06:15 +02:00
Lonami Exo
dedbf29ca4 Fix Conversation.wait_event not resolving them (from d474458) 2018-10-05 13:42:07 +02:00
Lonami Exo
dc77136453 Don't expect responses from ack, log send errors, remove TODOs 2018-10-05 13:26:44 +02:00
Lonami Exo
ef60ade647 Rewrite container packing to support arbitrary sizes 2018-10-05 13:26:44 +02:00
Lonami Exo
7e7bbcf4c0 Remove irrelevant TODOs and add more logging 2018-10-05 13:26:44 +02:00
Lonami Exo
a5d4e97922 Fix alternative connection modes 2018-10-05 13:26:44 +02:00
Lonami Exo
ebde3be820 Add support for proxy again 2018-10-05 13:26:44 +02:00
Lonami Exo
db83709c6b Support connection timeout 2018-10-05 13:26:44 +02:00
Lonami Exo
0cc8bca098 Delete TcpClient 2018-10-05 13:26:44 +02:00
Lonami Exo
1b9d6aac06 Gzip only content related data 2018-10-05 13:26:44 +02:00
Lonami Exo
37b9922f64 Handle cancellation on the receive loop 2018-10-05 13:26:44 +02:00
Lonami Exo
e319fa3aa9 Handle IncompleteReadError and InvalidChecksumError 2018-10-05 13:26:44 +02:00
Lonami Exo
3fd7c33127 Fix automatic reconnect (e.g. on bad auth key)
This took more time than it should have to debug.
2018-10-05 13:26:44 +02:00
Lonami Exo
bc1fd9039d Handle receiving errors 2018-10-05 13:26:44 +02:00
Lonami Exo
cf7e5d5592 Set auth_key on connection 2018-10-05 13:26:44 +02:00
Lonami Exo
21ffa2f26b Fix DC migration and seqno 2018-10-05 13:26:44 +02:00
Lonami Exo
ac567ebf1d Handle bad salt/msg not resending containers 2018-10-05 13:26:44 +02:00
Lonami Exo
3b1142aaca Add back auth key generation process 2018-10-05 13:26:44 +02:00
Lonami Exo
5edc2216c7 Handle initial connection if network is down correctly 2018-10-05 13:26:44 +02:00
Lonami Exo
2d275989cb Properly handle cancellation in _ReadyQueue 2018-10-05 13:26:44 +02:00
Lonami Exo
105bd52eee Abstract the send queue off MTProtoSender 2018-10-05 13:26:44 +02:00
Lonami Exo
b02ebcb69b Stop waiting for send items on disconnection 2018-10-05 13:26:44 +02:00
Lonami Exo
470fb9f5df Make use of the MTProtoLayer in MTProtoSender 2018-10-05 13:26:44 +02:00
Lonami Exo
9402b4a26d Create a new layer to lift encryption off the MTProtoSender 2018-10-05 13:26:44 +02:00
Lonami Exo
5daad2aaab Actually use the new connection class 2018-10-05 13:26:44 +02:00
Lonami Exo
daf94e416b Fix connection never receiving and missing clone method 2018-10-05 13:26:44 +02:00
Lonami Exo
2fd51b8582 Let all connection modes implement the new Connection 2018-10-05 13:26:44 +02:00
Lonami Exo
096424ea78 Create a new Connection class to work through queues 2018-10-05 13:26:44 +02:00
painor
340f5614b5 Add name mention formatting to HTML and Markdown (#1019) 2018-10-04 15:56:32 +02:00
Manuel1510
2468b32fc5 Implement next_offset and allow empty results in answer() (#1017) 2018-10-04 09:12:12 +02:00
nailerNAS
c2966297f1 Add delete method to custom.Dialog (#1014) 2018-09-28 15:47:24 +02:00
255 changed files with 29298 additions and 11979 deletions

8
.coveragerc Normal file
View File

@ -0,0 +1,8 @@
[run]
branch = true
parallel = true
source =
telethon
[report]
precision = 2

View File

@ -1,15 +0,0 @@
DON'T ASK QUESTIONS HERE.
This place is for issues with the library, so please make sure that:
1. The error is in the library's code, and not in your own.
2. You have already searched for your error on existing issues.
3. You are testing with upstream code (pip install -U https://github.com/LonamiWebs/Telethon/archive/master.zip).
If you have a question, ask in the official Telegram group https://t.me/TelethonChat or https://stackoverflow.com. Enhancement ideas are currently welcome, too.
If you paste code, please put it between three backticks (`):
```python
code here
```
Once you have read and understood this, delete all this text and detail whatever issue you are posting.

96
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,96 @@
name: Bug Report
description: Create a report about a bug inside the library.
body:
- type: textarea
id: reproducing-example
attributes:
label: Code that causes the issue
description: Provide a code example that reproduces the problem. Try to keep it short without other dependencies.
placeholder: |
```python
from telethon.sync import TelegramClient
...
```
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: Explain what you should expect to happen. Include reproduction steps.
placeholder: |
"I was doing... I was expecting the following to happen..."
validations:
required: true
- type: textarea
id: actual-behavior
attributes:
label: Actual behavior
description: Explain what actually happens.
placeholder: |
"This happened instead..."
validations:
required: true
- type: textarea
id: traceback
attributes:
label: Traceback
description: |
The traceback, if the problem is a crash.
placeholder: |
```
Traceback (most recent call last):
File "code.py", line 1, in <code>
```
- type: input
id: telethon-version
attributes:
label: Telethon version
description: The output of `python -c "import telethon; print(telethon.__version__)"`.
placeholder: "1.x"
validations:
required: true
- type: input
id: python-version
attributes:
label: Python version
description: The output of `python --version`.
placeholder: "3.x"
validations:
required: true
- type: input
id: os
attributes:
label: Operating system (including distribution name and version)
placeholder: Windows 11, macOS 13.4, Ubuntu 23.04...
validations:
required: true
- type: textarea
id: other-details
attributes:
label: Other details
placeholder: |
Additional details and attachments. Is it a server? Network condition?
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
options:
- label: The error is in the library's code, and not in my own.
required: true
- label: I have searched for this issue before posting it and there isn't an open duplicate.
required: true
- label: I ran `pip install -U https://github.com/LonamiWebs/Telethon/archive/v1.zip` and triggered the bug in the latest version.
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Ask questions in StackOverflow
url: https://stackoverflow.com/questions/ask?tags=telethon
about: Questions are not bugs. Please ask them in StackOverflow instead. Questions in the bug tracker will be closed
- name: Find about updates and our Telegram groups
url: https://t.me/s/TelethonUpdates
about: Be notified of updates, chat with other people about the library or ask questions in these groups

View File

@ -0,0 +1,22 @@
name: Documentation Issue
description: Report a problem with the documentation.
labels: [documentation]
body:
- type: textarea
id: description
attributes:
label: Description
description: Describe the problem in detail.
placeholder: This part is unclear...
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
options:
- label: This is a documentation problem, not a question or a bug report.
required: true
- label: I have searched for this issue before posting it and there isn't a duplicate.
required: true

View File

@ -0,0 +1,22 @@
name: Feature Request
description: Suggest ideas, changes or other enhancements for the library.
labels: [enhancement]
body:
- type: textarea
id: feature-description
attributes:
label: Describe your suggested feature
description: Please describe your idea. Would you like another friendly method? Renaming them to something more appropriate? Changing the way something works?
placeholder: "It should work like this..."
validations:
required: true
- type: checkboxes
id: checklist
attributes:
label: Checklist
description: Read this carefully, we will close and ignore your issue if you skimmed through this.
options:
- label: I have searched for this issue before posting it and there isn't a duplicate.
required: true

5
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,5 @@
<!--
Thanks for the PR! Please keep in mind that v1 is *feature frozen*.
New features very likely won't be merged, although fixes can be sent.
All new development should happen in v2. Thanks!
-->

28
.github/workflows.disabled/python.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Python Library
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.5", "3.6", "3.7", "3.8"]
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up env
run: |
python -m pip install --upgrade pip
pip install tox
- name: Lint with flake8
run: |
tox -e flake
- name: Test with pytest
run: |
# use "py", which is the default python version
tox -e py

115
.gitignore vendored Executable file → Normal file
View File

@ -1,108 +1,23 @@
# Docs
_build/
docs/
# Generated code
telethon/tl/functions/
telethon/tl/types/
telethon/tl/patched/
telethon/tl/alltlobjects.py
telethon/errors/rpcerrorlist.py
/telethon/tl/functions/
/telethon/tl/types/
/telethon/tl/alltlobjects.py
/telethon/errors/rpcerrorlist.py
# User session
*.session
usermedia/
/usermedia/
# Quick tests should live in this file
example.py
# Byte-compiled / optimized / DLL files
# Builds and testing
__pycache__/
*.py[cod]
*$py.class
/dist/
/build/
/*.egg-info/
/readthedocs/_build/
/.tox/
# C extensions
*.so
# API reference docs
/docs/
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
.venv/
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
# File used to manually test new changes, contains sensitive data
/example.py

18
.readthedocs.yaml Normal file
View File

@ -0,0 +1,18 @@
# https://docs.readthedocs.io/en/stable/config-file/v2.html
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
sphinx:
configuration: readthedocs/conf.py
formats:
- pdf
- epub
python:
install:
- requirements: readthedocs/requirements.txt

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 LonamiWebs
Copyright (c) 2016-Present LonamiWebs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,4 +0,0 @@
include LICENSE
include README.rst
recursive-include telethon *

View File

@ -4,16 +4,16 @@ Telethon
⭐️ Thanks **everyone** who has starred the project, it means a lot!
|logo| **Telethon** is an `asyncio
<https://docs.python.org/3/library/asyncio.html>`_ **Python 3** library
to interact with Telegram's API.
|logo| **Telethon** is an asyncio_ **Python 3**
MTProto_ library to interact with Telegram_'s API
as a user or through a bot account (bot API alternative).
**If you're upgrading from Telethon pre-1.0 to 1.0, please make sure to read**
`this section of the documentation
<https://telethon.readthedocs.io/en/latest/extra/basic/asyncio-magic.html>`_,
or ``pip install telethon-sync`` which is compatible with `synchronous code
<https://github.com/LonamiWebs/Telethon/tree/sync>`_. Don't forget to remove
the asynchronous version (``pip uninstall telethon``) if you do install sync.
.. important::
If you have code using Telethon before its 1.0 version, you must
read `Compatibility and Convenience`_ to learn how to migrate.
As with any third-party library for Telegram, be careful not to
break `Telegram's ToS`_ or `Telegram can ban the account`_.
What is this?
-------------
@ -27,7 +27,7 @@ heavy job for you, so you can focus on developing an application.
Installing
----------
.. code:: sh
.. code-block:: sh
pip3 install telethon
@ -35,9 +35,9 @@ Installing
Creating a client
-----------------
.. code:: python
.. code-block:: python
from telethon import TelegramClient, sync
from telethon import TelegramClient, events, sync
# These example values won't work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development.
@ -51,7 +51,7 @@ Creating a client
Doing stuff
-----------
.. code:: python
.. code-block:: python
print(client.get_me().stringify())
@ -62,14 +62,25 @@ Doing stuff
messages = client.get_messages('username')
messages[0].download_media()
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
async def handler(event):
await event.respond('Hey!')
Next steps
----------
Do you like how Telethon looks? Check out `Read The Docs
<http://telethon.rtfd.io/>`_ for a more in-depth explanation,
with examples, troubleshooting issues, and more useful information.
Do you like how Telethon looks? Check out `Read The Docs`_ for a more
in-depth explanation, with examples, troubleshooting issues, and more
useful information.
.. _asyncio: https://docs.python.org/3/library/asyncio.html
.. _MTProto: https://core.telegram.org/mtproto
.. _Telegram: https://telegram.org
.. _Compatibility and Convenience: https://docs.telethon.dev/en/stable/misc/compatibility-and-convenience.html
.. _Telegram's ToS: https://core.telegram.org/api/terms
.. _Telegram can ban the account: https://docs.telethon.dev/en/stable/quick-references/faq.html#my-account-was-deleted-limited-when-using-the-library
.. _Read The Docs: https://docs.telethon.dev
.. |logo| image:: logo.svg
:width: 24pt

3
dev-requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pytest
pytest-cov
pytest-asyncio

View File

@ -1,3 +1,5 @@
cryptg
pysocks
hachoir3
python-socks[asyncio]
hachoir
pillow

36
pyproject.toml Normal file
View File

@ -0,0 +1,36 @@
# https://snarky.ca/what-the-heck-is-pyproject-toml/
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
# Need to use legacy format for the time being
# https://tox.readthedocs.io/en/3.20.0/example/basic.html#pyproject-toml-tox-legacy-ini
[tool.tox]
legacy_tox_ini = """
[tox]
envlist = py35,py36,py37,py38
# run with tox -e py
[testenv]
deps =
-rrequirements.txt
-roptional-requirements.txt
-rdev-requirements.txt
commands =
# NOTE: you can run any command line tool here - not just tests
pytest {posargs}
# run with tox -e flake
[testenv:flake]
deps =
-rrequirements.txt
-roptional-requirements.txt
-rdev-requirements.txt
flake8
commands =
# stop the build if there are Python syntax errors or undefined names
flake8 telethon/ telethon_generator/ tests/ --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 telethon/ telethon_generator/ tests/ --count --exit-zero --exclude telethon/tl/,telethon/errors/rpcerrorlist.py --max-complexity=10 --max-line-length=127 --statistics
"""

View File

@ -0,0 +1,96 @@
.. _installation:
============
Installation
============
Telethon is a Python library, which means you need to download and install
Python from https://www.python.org/downloads/ if you haven't already. Once
you have Python installed, `upgrade pip`__ and run:
.. code-block:: sh
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade telethon
…to install or upgrade the library to the latest version.
.. __: https://pythonspeed.com/articles/upgrade-pip/
Installing Development Versions
===============================
If you want the *latest* unreleased changes,
you can run the following command instead:
.. code-block:: sh
python3 -m pip install --upgrade https://github.com/LonamiWebs/Telethon/archive/v1.zip
.. note::
The development version may have bugs and is not recommended for production
use. However, when you are `reporting a library bug`__, you should try if the
bug still occurs in this version.
.. __: https://github.com/LonamiWebs/Telethon/issues/
Verification
============
To verify that the library is installed correctly, run the following command:
.. code-block:: sh
python3 -c "import telethon; print(telethon.__version__)"
The version number of the library should show in the output.
Optional Dependencies
=====================
If cryptg_ is installed, **the library will work a lot faster**, since
encryption and decryption will be made in C instead of Python. If your
code deals with a lot of updates or you are downloading/uploading a lot
of files, you will notice a considerable speed-up (from a hundred kilobytes
per second to several megabytes per second, if your connection allows it).
If it's not installed, pyaes_ will be used (which is pure Python, so it's
much slower).
If pillow_ is installed, large images will be automatically resized when
sending photos to prevent Telegram from failing with "invalid image".
Official clients also do this.
If aiohttp_ is installed, the library will be able to download
:tl:`WebDocument` media files (otherwise you will get an error).
If hachoir_ is installed, it will be used to extract metadata from files
when sending documents. Telegram uses this information to show the song's
performer, artist, title, duration, and for videos too (including size).
Otherwise, they will default to empty values, and you can set the attributes
manually.
.. note::
Some of the modules may require additional dependencies before being
installed through ``pip``. If you have an ``apt``-based system, consider
installing the most commonly missing dependencies (with the right ``pip``):
.. code-block:: sh
apt update
apt install clang lib{jpeg-turbo,webp}-dev python{,-dev} zlib-dev
pip install -U --user setuptools
pip install -U --user telethon cryptg pillow
Thanks to `@bb010g`_ for writing down this nice list.
.. _cryptg: https://github.com/cher-nov/cryptg
.. _pyaes: https://github.com/ricmoo/pyaes
.. _pillow: https://python-pillow.org
.. _aiohttp: https://docs.aiohttp.org
.. _hachoir: https://hachoir.readthedocs.io
.. _@bb010g: https://static.bb010g.com

View File

@ -0,0 +1,46 @@
==========
Next Steps
==========
These basic first steps should have gotten you started with the library.
By now, you should know how to call friendly methods and how to work with
the returned objects, how things work inside event handlers, etc.
Next, we will see a quick reference summary of *all* the methods and
properties that you will need when using the library. If you follow
the links there, you will expand the documentation for the method
and property, with more examples on how to use them.
Therefore, **you can find an example on every method** of the client
to learn how to use it, as well as a description of all the arguments.
After that, we will go in-depth with some other important concepts
that are worth learning and understanding.
From now on, you can keep pressing the "Next" button if you want,
or use the menu on the left, since some pages are quite lengthy.
A note on developing applications
=================================
If you're using the library to make an actual application (and not just
automate things), you should make sure to `comply with the ToS`__:
[…] when logging in as an existing user, apps are supposed to call
[:tl:`GetTermsOfServiceUpdate`] to check for any updates to the Terms of
Service; this call should be repeated after ``expires`` seconds have
elapsed. If an update to the Terms Of Service is available, clients are
supposed to show a consent popup; if accepted, clients should call
[:tl:`AcceptTermsOfService`], providing the ``termsOfService id`` JSON
object; in case of denial, clients are to delete the account using
[:tl:`DeleteAccount`], providing Decline ToS update as deletion reason.
.. __: https://core.telegram.org/api/config#terms-of-service
However, if you use the library to automate or enhance your Telegram
experience, it's very likely that you are using other applications doing this
check for you (so you wouldn't run the risk of violating the ToS).
The library itself will not automatically perform this check or accept the ToS
because it should require user action (the only exception is during sign-up).

View File

@ -0,0 +1,111 @@
===========
Quick-Start
===========
Let's see a longer example to learn some of the methods that the library
has to offer. These are known as "friendly methods", and you should always
use these if possible.
.. code-block:: python
from telethon import TelegramClient
# Remember to use your own values from my.telegram.org!
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('anon', api_id, api_hash)
async def main():
# Getting information about yourself
me = await client.get_me()
# "me" is a user object. You can pretty-print
# any Telegram object with the "stringify" method:
print(me.stringify())
# When you print something, you see a representation of it.
# You can access all attributes of Telegram objects with
# the dot operator. For example, to get the username:
username = me.username
print(username)
print(me.phone)
# You can print all the dialogs/conversations that you are part of:
async for dialog in client.iter_dialogs():
print(dialog.name, 'has ID', dialog.id)
# You can send messages to yourself...
await client.send_message('me', 'Hello, myself!')
# ...to some chat ID
await client.send_message(-100123456, 'Hello, group!')
# ...to your contacts
await client.send_message('+34600123123', 'Hello, friend!')
# ...or even to any username
await client.send_message('username', 'Testing Telethon!')
# You can, of course, use markdown in your messages:
message = await client.send_message(
'me',
'This message has **bold**, `code`, __italics__ and '
'a [nice website](https://example.com)!',
link_preview=False
)
# Sending a message returns the sent message object, which you can use
print(message.raw_text)
# You can reply to messages directly if you have a message object
await message.reply('Cool!')
# Or send files, songs, documents, albums...
await client.send_file('me', '/home/me/Pictures/holidays.jpg')
# You can print the message history of any chat:
async for message in client.iter_messages('me'):
print(message.id, message.text)
# You can download media from messages, too!
# The method will return the path where the file was saved.
if message.photo:
path = await message.download_media()
print('File saved to', path) # printed after download is done
with client:
client.loop.run_until_complete(main())
Here, we show how to sign in, get information about yourself, send
messages, files, getting chats, printing messages, and downloading
files.
You should make sure that you understand what the code shown here
does, take note on how methods are called and used and so on before
proceeding. We will see all the available methods later on.
.. important::
Note that Telethon is an asynchronous library, and as such, you should
get used to it and learn a bit of basic `asyncio`. This will help a lot.
As a quick start, this means you generally want to write all your code
inside some ``async def`` like so:
.. code-block:: python
client = ...
async def do_something(me):
...
async def main():
# Most of your code should go here.
# You can of course make and use your own async def (do_something).
# They only need to be async if they need to await things.
me = await client.get_me()
await do_something(me)
with client:
client.loop.run_until_complete(main())
After you understand this, you may use the ``telethon.sync`` hack if you
want do so (see :ref:`compatibility-and-convenience`), but note you may
run into other issues (iPython, Anaconda, etc. have some issues with it).

View File

@ -0,0 +1,229 @@
.. _signing-in:
==========
Signing In
==========
Before working with Telegram's API, you need to get your own API ID and hash:
1. `Login to your Telegram account <https://my.telegram.org/>`_ with the
phone number of the developer account to use.
2. Click under API Development tools.
3. A *Create new application* window will appear. Fill in your application
details. There is no need to enter any *URL*, and only the first two
fields (*App title* and *Short name*) can currently be changed later.
4. Click on *Create application* at the end. Remember that your
**API hash is secret** and Telegram won't let you revoke it.
Don't post it anywhere!
.. note::
This API ID and hash is the one used by *your application*, not your
phone number. You can use this API ID and hash with *any* phone number
or even for bot accounts.
Editing the Code
================
This is a little introduction for those new to Python programming in general.
We will write our code inside ``hello.py``, so you can use any text
editor that you like. To run the code, use ``python3 hello.py`` from
the terminal.
.. important::
Don't call your script ``telethon.py``! Python will try to import
the client from there and it will fail with an error such as
"ImportError: cannot import name 'TelegramClient' ...".
Signing In
==========
We can finally write some code to log into our account!
.. code-block:: python
from telethon import TelegramClient
# Use your own values from my.telegram.org
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
# The first parameter is the .session file name (absolute paths allowed)
with TelegramClient('anon', api_id, api_hash) as client:
client.loop.run_until_complete(client.send_message('me', 'Hello, myself!'))
In the first line, we import the class name so we can create an instance
of the client. Then, we define variables to store our API ID and hash
conveniently.
At last, we create a new `TelegramClient <telethon.client.telegramclient.TelegramClient>`
instance and call it ``client``. We can now use the client variable
for anything that we want, such as sending a message to ourselves.
.. note::
Since Telethon is an asynchronous library, you need to ``await``
coroutine functions to have them run (or otherwise, run the loop
until they are complete). In this tiny example, we don't bother
making an ``async def main()``.
See :ref:`mastering-asyncio` to find out more.
Using a ``with`` block is the preferred way to use the library. It will
automatically `start() <telethon.client.auth.AuthMethods.start>` the client,
logging or signing up if necessary.
If the ``.session`` file already existed, it will not login
again, so be aware of this if you move or rename the file!
Signing In as a Bot Account
===========================
You can also use Telethon for your bots (normal bot accounts, not users).
You will still need an API ID and hash, but the process is very similar:
.. code-block:: python
from telethon.sync import TelegramClient
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
bot_token = '12345:0123456789abcdef0123456789abcdef'
# We have to manually call "start" if we want an explicit bot token
bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token)
# But then we can use the client instance as usual
with bot:
...
To get a bot account, you need to talk
with `@BotFather <https://t.me/BotFather>`_.
Signing In behind a Proxy
=========================
If you need to use a proxy to access Telegram,
you will need to either:
* For Python >= 3.6 : `install python-socks[asyncio]`__
* For Python <= 3.5 : `install PySocks`__
and then change
.. code-block:: python
TelegramClient('anon', api_id, api_hash)
with
.. code-block:: python
TelegramClient('anon', api_id, api_hash, proxy=("socks5", '127.0.0.1', 4444))
(of course, replacing the protocol, IP and port with the protocol, IP and port of the proxy).
The ``proxy=`` argument should be a dict (or tuple, for backwards compatibility),
consisting of parameters described `in PySocks usage`__.
The allowed values for the argument ``proxy_type`` are:
* For Python <= 3.5:
* ``socks.SOCKS5`` or ``'socks5'``
* ``socks.SOCKS4`` or ``'socks4'``
* ``socks.HTTP`` or ``'http'``
* For Python >= 3.6:
* All of the above
* ``python_socks.ProxyType.SOCKS5``
* ``python_socks.ProxyType.SOCKS4``
* ``python_socks.ProxyType.HTTP``
Example:
.. code-block:: python
proxy = {
'proxy_type': 'socks5', # (mandatory) protocol to use (see above)
'addr': '1.1.1.1', # (mandatory) proxy IP address
'port': 5555, # (mandatory) proxy port number
'username': 'foo', # (optional) username if the proxy requires auth
'password': 'bar', # (optional) password if the proxy requires auth
'rdns': True # (optional) whether to use remote or local resolve, default remote
}
For backwards compatibility with ``PySocks`` the following format
is possible (but discouraged):
.. code-block:: python
proxy = (socks.SOCKS5, '1.1.1.1', 5555, True, 'foo', 'bar')
.. __: https://github.com/romis2012/python-socks#installation
.. __: https://github.com/Anorov/PySocks#installation
.. __: https://github.com/Anorov/PySocks#usage-1
Using MTProto Proxies
=====================
MTProto Proxies are Telegram's alternative to normal proxies,
and work a bit differently. The following protocols are available:
* ``ConnectionTcpMTProxyAbridged``
* ``ConnectionTcpMTProxyIntermediate``
* ``ConnectionTcpMTProxyRandomizedIntermediate`` (preferred)
For now, you need to manually specify these special connection modes
if you want to use a MTProto Proxy. Your code would look like this:
.. code-block:: python
from telethon import TelegramClient, connection
# we need to change the connection ^^^^^^^^^^
client = TelegramClient(
'anon',
api_id,
api_hash,
# Use one of the available connection modes.
# Normally, this one works with most proxies.
connection=connection.ConnectionTcpMTProxyRandomizedIntermediate,
# Then, pass the proxy details as a tuple:
# (host name, port, proxy secret)
#
# If the proxy has no secret, the secret must be:
# '00000000000000000000000000000000'
proxy=('mtproxy.example.com', 2002, 'secret')
)
In future updates, we may make it easier to use MTProto Proxies
(such as avoiding the need to manually pass ``connection=``).
In short, the same code above but without comments to make it clearer:
.. code-block:: python
from telethon import TelegramClient, connection
client = TelegramClient(
'anon', api_id, api_hash,
connection=connection.ConnectionTcpMTProxyRandomizedIntermediate,
proxy=('mtproxy.example.com', 2002, 'secret')
)

View File

@ -0,0 +1,159 @@
=======
Updates
=======
Updates are an important topic in a messaging platform like Telegram.
After all, you want to be notified when a new message arrives, when
a member joins, when someone starts typing, etc.
For that, you can use **events**.
.. important::
It is strongly advised to enable logging when working with events,
since exceptions in event handlers are hidden by default. Please
add the following snippet to the very top of your file:
.. code-block:: python
import logging
logging.basicConfig(format='[%(levelname) %(asctime)s] %(name)s: %(message)s',
level=logging.WARNING)
Getting Started
===============
Let's start things with an example to automate replies:
.. code-block:: python
from telethon import TelegramClient, events
client = TelegramClient('anon', api_id, api_hash)
@client.on(events.NewMessage)
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('hi!')
client.start()
client.run_until_disconnected()
This code isn't much, but there might be some things unclear.
Let's break it down:
.. code-block:: python
from telethon import TelegramClient, events
client = TelegramClient('anon', api_id, api_hash)
This is normal creation (of course, pass session name, API ID and hash).
Nothing we don't know already.
.. code-block:: python
@client.on(events.NewMessage)
This Python decorator will attach itself to the ``my_event_handler``
definition, and basically means that *on* a `NewMessage
<telethon.events.newmessage.NewMessage>` *event*,
the callback function you're about to define will be called:
.. code-block:: python
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('hi!')
If a `NewMessage
<telethon.events.newmessage.NewMessage>` event occurs,
and ``'hello'`` is in the text of the message, we `reply()
<telethon.tl.custom.message.Message.reply>` to the event
with a ``'hi!'`` message.
.. note::
Event handlers **must** be ``async def``. After all,
Telethon is an asynchronous library based on `asyncio`,
which is a safer and often faster approach to threads.
You **must** ``await`` all method calls that use
network requests, which is most of them.
More Examples
=============
Replying to messages with hello is fun, but, can we do more?
.. code-block:: python
@client.on(events.NewMessage(outgoing=True, pattern=r'\.save'))
async def handler(event):
if event.is_reply:
replied = await event.get_reply_message()
sender = replied.sender
await client.download_profile_photo(sender)
await event.respond('Saved your photo {}'.format(sender.username))
We could also get replies. This event filters outgoing messages
(only those that we send will trigger the method), then we filter
by the regex ``r'\.save'``, which will match messages starting
with ``".save"``.
Inside the method, we check whether the event is replying to another message
or not. If it is, we get the reply message and the sender of that message,
and download their profile photo.
Let's delete messages which contain "heck". We don't allow swearing here.
.. code-block:: python
@client.on(events.NewMessage(pattern=r'(?i).*heck'))
async def handler(event):
await event.delete()
With the ``r'(?i).*heck'`` regex, we match case-insensitive
"heck" anywhere in the message. Regex is very powerful and you
can learn more at https://regexone.com/.
So far, we have only seen the `NewMessage
<telethon.events.newmessage.NewMessage>`, but there are many more
which will be covered later. This is only a small introduction to updates.
Entities
========
When you need the user or chat where an event occurred, you **must** use
the following methods:
.. code-block:: python
async def handler(event):
# Good
chat = await event.get_chat()
sender = await event.get_sender()
chat_id = event.chat_id
sender_id = event.sender_id
# BAD. Don't do this
chat = event.chat
sender = event.sender
chat_id = event.chat.id
sender_id = event.sender.id
Events are like messages, but don't have all the information a message has!
When you manually get a message, it will have all the information it needs.
When you receive an update about a message, it **won't** have all the
information, so you have to **use the methods**, not the properties.
Make sure you understand the code seen here before continuing!
As a rule of thumb, remember that new message events behave just
like message objects, so you can do with them everything you can
do with a message object.

View File

@ -0,0 +1,368 @@
.. _mastering-asyncio:
=================
Mastering asyncio
=================
.. contents::
What's asyncio?
===============
`asyncio` is a Python 3's built-in library. This means it's already installed if
you have Python 3. Since Python 3.5, it is convenient to work with asynchronous
code. Before (Python 3.4) we didn't have ``async`` or ``await``, but now we do.
`asyncio` stands for *Asynchronous Input Output*. This is a very powerful
concept to use whenever you work IO. Interacting with the web or external
APIs such as Telegram's makes a lot of sense this way.
Why asyncio?
============
Asynchronous IO makes a lot of sense in a library like Telethon.
You send a request to the server (such as "get some message"), and
thanks to `asyncio`, your code won't block while a response arrives.
The alternative would be to spawn a thread for each update so that
other code can run while the response arrives. That is *a lot* more
expensive.
The code will also run faster, because instead of switching back and
forth between the OS and your script, your script can handle it all.
Avoiding switching saves quite a bit of time, in Python or any other
language that supports asynchronous IO. It will also be cheaper,
because tasks are smaller than threads, which are smaller than processes.
What are asyncio basics?
========================
The code samples below assume that you have Python 3.7 or greater installed.
.. code-block:: python
# First we need the asyncio library
import asyncio
# We also need something to run
async def main():
for char in 'Hello, world!\n':
print(char, end='', flush=True)
await asyncio.sleep(0.2)
# Then, we can create a new asyncio loop and use it to run our coroutine.
# The creation and tear-down of the loop is hidden away from us.
asyncio.run(main())
What does telethon.sync do?
===========================
The moment you import any of these:
.. code-block:: python
from telethon import sync, ...
# or
from telethon.sync import ...
# or
import telethon.sync
The ``sync`` module rewrites most ``async def``
methods in Telethon to something similar to this:
.. code-block:: python
def new_method():
result = original_method()
if loop.is_running():
# the loop is already running, return the await-able to the user
return result
else:
# the loop is not running yet, so we can run it for the user
return loop.run_until_complete(result)
That means you can do this:
.. code-block:: python
print(client.get_me().username)
Instead of this:
.. code-block:: python
me = client.loop.run_until_complete(client.get_me())
print(me.username)
# or, using asyncio's default loop (it's the same)
import asyncio
loop = asyncio.get_running_loop() # == client.loop
me = loop.run_until_complete(client.get_me())
print(me.username)
As you can see, it's a lot of boilerplate and noise having to type
``run_until_complete`` all the time, so you can let the magic module
to rewrite it for you. But notice the comment above: it won't run
the loop if it's already running, because it can't. That means this:
.. code-block:: python
async def main():
# 3. the loop is running here
print(
client.get_me() # 4. this will return a coroutine!
.username # 5. this fails, coroutines don't have usernames
)
loop.run_until_complete( # 2. run the loop and the ``main()`` coroutine
main() # 1. calling ``async def`` "returns" a coroutine
)
Will fail. So if you're inside an ``async def``, then the loop is
running, and if the loop is running, you must ``await`` things yourself:
.. code-block:: python
async def main():
print((await client.get_me()).username)
loop.run_until_complete(main())
What are async, await and coroutines?
=====================================
The ``async`` keyword lets you define asynchronous functions,
also known as coroutines, and also iterate over asynchronous
loops or use ``async with``:
.. code-block:: python
import asyncio
async def main():
# ^ this declares the main() coroutine function
async with client:
# ^ this is an asynchronous with block
async for message in client.iter_messages(chat):
# ^ this is a for loop over an asynchronous generator
print(message.sender.username)
asyncio.run(main())
# ^ this will create a new asyncio loop behind the scenes and tear it down
# once the function returns. It will run the loop untiil main finishes.
# You should only use this function if there is no other loop running.
The ``await`` keyword blocks the *current* task, and the loop can run
other tasks. Tasks can be thought of as "threads", since many can run
concurrently:
.. code-block:: python
import asyncio
async def hello(delay):
await asyncio.sleep(delay) # await tells the loop this task is "busy"
print('hello') # eventually the loop resumes the code here
async def world(delay):
# the loop decides this method should run first
await asyncio.sleep(delay) # await tells the loop this task is "busy"
print('world') # eventually the loop finishes all tasks
async def main():
asyncio.create_task(world(2)) # create the world task, passing 2 as delay
asyncio.create_task(hello(delay=1)) # another task, but with delay 1
await asyncio.sleep(3) # wait for three seconds before exiting
try:
# create a new temporary asyncio loop and use it to run main
asyncio.run(main())
except KeyboardInterrupt:
pass
The same example, but without the comment noise:
.. code-block:: python
import asyncio
async def hello(delay):
await asyncio.sleep(delay)
print('hello')
async def world(delay):
await asyncio.sleep(delay)
print('world')
async def main():
asyncio.create_task(world(2))
asyncio.create_task(hello(delay=1))
await asyncio.sleep(3)
try:
asyncio.run(main())
except KeyboardInterrupt:
pass
Can I use threads?
==================
Yes, you can, but you must understand that the loops themselves are
not thread safe. and you must be sure to know what is happening. The
easiest and cleanest option is to use `asyncio.run` to create and manage
the new event loop for you:
.. code-block:: python
import asyncio
import threading
async def actual_work():
client = TelegramClient(..., loop=loop)
... # can use `await` here
def go():
asyncio.run(actual_work())
threading.Thread(target=go).start()
Generally, **you don't need threads** unless you know what you're doing.
Just create another task, as shown above. If you're using the Telethon
with a library that uses threads, you must be careful to use `threading.Lock`
whenever you use the client, or enable the compatible mode. For that, see
:ref:`compatibility-and-convenience`.
You may have seen this error:
.. code-block:: text
RuntimeError: There is no current event loop in thread 'Thread-1'.
It just means you didn't create a loop for that thread. Please refer to
the ``asyncio`` documentation to correctly learn how to set the event loop
for non-main threads.
client.run_until_disconnected() blocks!
=======================================
All of what `client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` does is
run the `asyncio`'s event loop until the client is disconnected. That means
*the loop is running*. And if the loop is running, it will run all the tasks
in it. So if you want to run *other* code, create tasks for it:
.. code-block:: python
from datetime import datetime
async def clock():
while True:
print('The time:', datetime.now())
await asyncio.sleep(1)
loop.create_task(clock())
...
client.run_until_disconnected()
This creates a task for a clock that prints the time every second.
You don't need to use `client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` either!
You just need to make the loop is running, somehow. `loop.run_forever()
<asyncio.loop.run_forever()>` and `loop.run_until_complete()
<asyncio.loop.run_until_complete>` can also be used to run
the loop, and Telethon will be happy with any approach.
Of course, there are better tools to run code hourly or daily, see below.
What else can asyncio do?
=========================
Asynchronous IO is a really powerful tool, as we've seen. There are plenty
of other useful libraries that also use `asyncio` and that you can integrate
with Telethon.
* `aiohttp <https://github.com/aio-libs/aiohttp>`_ is like the infamous
`requests <https://github.com/requests/requests/>`_ but asynchronous.
* `quart <https://gitlab.com/pgjones/quart>`_ is an asynchronous alternative
to `Flask <http://flask.pocoo.org/>`_.
* `aiocron <https://github.com/gawel/aiocron>`_ lets you schedule things
to run things at a desired time, or run some tasks hourly, daily, etc.
And of course, `asyncio <https://docs.python.org/3/library/asyncio.html>`_
itself! It has a lot of methods that let you do nice things. For example,
you can run requests in parallel:
.. code-block:: python
async def main():
last, sent, download_path = await asyncio.gather(
client.get_messages('telegram', 10),
client.send_message('me', 'Using asyncio!'),
client.download_profile_photo('telegram')
)
loop.run_until_complete(main())
This code will get the 10 last messages from `@telegram
<https://t.me/telegram>`_, send one to the chat with yourself, and also
download the profile photo of the channel. `asyncio` will run all these
three tasks at the same time. You can run all the tasks you want this way.
A different way would be:
.. code-block:: python
loop.create_task(client.get_messages('telegram', 10))
loop.create_task(client.send_message('me', 'Using asyncio!'))
loop.create_task(client.download_profile_photo('telegram'))
They will run in the background as long as the loop is running too.
You can also `start an asyncio server
<https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server>`_
in the main script, and from another script, `connect to it
<https://docs.python.org/3/library/asyncio-stream.html#asyncio.open_connection>`_
to achieve `Inter-Process Communication
<https://en.wikipedia.org/wiki/Inter-process_communication>`_.
You can get as creative as you want. You can program anything you want.
When you use a library, you're not limited to use only its methods. You can
combine all the libraries you want. People seem to forget this simple fact!
Why does client.start() work outside async?
===========================================
Because it's so common that it's really convenient to offer said
functionality by default. This means you can set up all your event
handlers and start the client without worrying about loops at all.
Using the client in a ``with`` block, `start
<telethon.client.auth.AuthMethods.start>`, `run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>`, and
`disconnect <telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`
all support this.
Where can I read more?
======================
`Check out my blog post
<https://lonami.dev/blog/asyncio/>`_ about `asyncio`, which
has some more examples and pictures to help you understand what happens
when the loop runs.

View File

@ -0,0 +1,336 @@
.. _botapi:
=======================
HTTP Bot API vs MTProto
=======================
Telethon is more than just another viable alternative when developing bots
for Telegram. If you haven't decided which wrapper library for bots to use
yet, using Telethon from the beginning may save you some headaches later.
.. contents::
What is Bot API?
================
The `Telegram Bot API`_, also known as HTTP Bot API and from now on referred
to as simply "Bot API" is Telegram's official way for developers to control
their own Telegram bots. Quoting their main page:
The Bot API is an HTTP-based interface created for developers keen on
building bots for Telegram.
To learn how to create and set up a bot, please consult our
`Introduction to Bots`_ and `Bot FAQ`_.
Bot API is simply an HTTP endpoint which translates your requests to it into
MTProto calls through tdlib_, their bot backend.
Configuration of your bot, such as its available commands and auto-completion,
is configured through `@BotFather <https://t.me/BotFather>`_.
What is MTProto?
================
MTProto_ is Telegram's own protocol to communicate with their API when you
connect to their servers.
Telethon is an alternative MTProto-based backend written entirely in Python
and much easier to setup and use.
Both official applications and third-party clients (like your own
applications) logged in as either user or bots **can use MTProto** to
communicate directly with Telegram's API (which is not the HTTP bot API).
When we talk about MTProto, we often mean "MTProto-based clients".
Advantages of MTProto over Bot API
==================================
MTProto clients (like Telethon) connect directly to Telegram's servers,
which means there is no HTTP connection, no "polling" or "web hooks". This
means **less overhead**, since the protocol used between you and the server
is much more compact than HTTP requests with responses in wasteful JSON.
Since there is a direct connection to Telegram's servers, even if their
Bot API endpoint is down, you can still have connection to Telegram directly.
Using a MTProto client, you are also not limited to the public API that
they expose, and instead, **you have full control** of what your bot can do.
Telethon offers you all the power with often **much easier usage** than any
of the available Python Bot API wrappers.
If your application ever needs user features because bots cannot do certain
things, you will be able to easily login as a user and even keep your bot
without having to learn a new library.
If less overhead and full control didn't convince you to use Telethon yet,
check out the wiki page `MTProto vs HTTP Bot API`_ with a more exhaustive
and up-to-date list of differences.
Migrating from Bot API to Telethon
==================================
It doesn't matter if you wrote your bot with requests_ and you were
making API requests manually, or if you used a wrapper library like
python-telegram-bot_ or pyTelegramBotAPI_. It's never too late to
migrate to Telethon!
If you were using an asynchronous library like aiohttp_ or a wrapper like
aiogram_ or dumbot_, it will be even easier, because Telethon is also an
asynchronous library.
Next, we will see some examples from the most popular libraries.
Migrating from python-telegram-bot
----------------------------------
Let's take their `echobot.py`_ example and shorten it a bit:
.. code-block:: python
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
def start(update, context):
"""Send a message when the command /start is issued."""
update.message.reply_text('Hi!')
def echo(update, context):
"""Echo the user message."""
update.message.reply_text(update.message.text)
def main():
"""Start the bot."""
updater = Updater("TOKEN")
dp = updater.dispatcher
dp.add_handler(CommandHandler("start", start))
dp.add_handler(MessageHandler(Filters.text & ~Filters.command, echo))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
After using Telethon:
.. code-block:: python
from telethon import TelegramClient, events
bot = TelegramClient('bot', 11111, 'a1b2c3d4').start(bot_token='TOKEN')
@bot.on(events.NewMessage(pattern='/start'))
async def start(event):
"""Send a message when the command /start is issued."""
await event.respond('Hi!')
raise events.StopPropagation
@bot.on(events.NewMessage)
async def echo(event):
"""Echo the user message."""
await event.respond(event.text)
def main():
"""Start the bot."""
bot.run_until_disconnected()
if __name__ == '__main__':
main()
Key differences:
* The recommended way to do it imports fewer things.
* All handlers trigger by default, so we need ``events.StopPropagation``.
* Adding handlers, responding and running is a lot less verbose.
* Telethon needs ``async def`` and ``await``.
* The ``bot`` isn't hidden away by ``Updater`` or ``Dispatcher``.
Migrating from pyTelegramBotAPI
-------------------------------
Let's show another echobot from their README:
.. code-block:: python
import telebot
bot = telebot.TeleBot("TOKEN")
@bot.message_handler(commands=['start'])
def send_welcome(message):
bot.reply_to(message, "Howdy, how are you doing?")
@bot.message_handler(func=lambda m: True)
def echo_all(message):
bot.reply_to(message, message.text)
bot.polling()
Now we rewrite it to use Telethon:
.. code-block:: python
from telethon import TelegramClient, events
bot = TelegramClient('bot', 11111, 'a1b2c3d4').start(bot_token='TOKEN')
@bot.on(events.NewMessage(pattern='/start'))
async def send_welcome(event):
await event.reply('Howdy, how are you doing?')
@bot.on(events.NewMessage)
async def echo_all(event):
await event.reply(event.text)
bot.run_until_disconnected()
Key differences:
* Instead of doing ``bot.reply_to(message)``, we can do ``event.reply``.
Note that the ``event`` behaves just like their ``message``.
* Telethon also supports ``func=lambda m: True``, but it's not necessary.
Migrating from aiogram
----------------------
From their GitHub:
.. code-block:: python
from aiogram import Bot, Dispatcher, executor, types
API_TOKEN = 'BOT TOKEN HERE'
# Initialize bot and dispatcher
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
"""
This handler will be called when client send `/start` command.
"""
await message.reply("Hi!\nI'm EchoBot!\nPowered by aiogram.")
@dp.message_handler(regexp='(^cat[s]?$|puss)')
async def cats(message: types.Message):
with open('data/cats.jpg', 'rb') as photo:
await bot.send_photo(message.chat.id, photo, caption='Cats is here 😺',
reply_to_message_id=message.message_id)
@dp.message_handler()
async def echo(message: types.Message):
await bot.send_message(message.chat.id, message.text)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
After rewrite:
.. code-block:: python
from telethon import TelegramClient, events
# Initialize bot and... just the bot!
bot = TelegramClient('bot', 11111, 'a1b2c3d4').start(bot_token='TOKEN')
@bot.on(events.NewMessage(pattern='/start'))
async def send_welcome(event):
await event.reply('Howdy, how are you doing?')
@bot.on(events.NewMessage(pattern='(^cat[s]?$|puss)'))
async def cats(event):
await event.reply('Cats is here 😺', file='data/cats.jpg')
@bot.on(events.NewMessage)
async def echo_all(event):
await event.reply(event.text)
if __name__ == '__main__':
bot.run_until_disconnected()
Key differences:
* Telethon offers convenience methods to avoid retyping
``bot.send_photo(message.chat.id, ...)`` all the time,
and instead let you type ``event.reply``.
* Sending files is **a lot** easier. The methods for sending
photos, documents, audios, etc. are all the same!
Migrating from dumbot
---------------------
Showcasing their subclassing example:
.. code-block:: python
from dumbot import Bot
class Subbot(Bot):
async def init(self):
self.me = await self.getMe()
async def on_update(self, update):
await self.sendMessage(
chat_id=update.message.chat.id,
text='i am {}'.format(self.me.username)
)
Subbot(token).run()
After rewriting:
.. code-block:: python
from telethon import TelegramClient, events
class Subbot(TelegramClient):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)
self.add_event_handler(self.on_update, events.NewMessage)
async def connect():
await super().connect()
self.me = await self.get_me()
async def on_update(event):
await event.reply('i am {}'.format(self.me.username))
bot = Subbot('bot', 11111, 'a1b2c3d4').start(bot_token='TOKEN')
bot.run_until_disconnected()
Key differences:
* Telethon method names are ``snake_case``.
* dumbot does not offer friendly methods like ``update.reply``.
* Telethon does not have an implicit ``on_update`` handler, so
we need to manually register one.
.. _Telegram Bot API: https://core.telegram.org/bots/api
.. _Introduction to Bots: https://core.telegram.org/bots
.. _Bot FAQ: https://core.telegram.org/bots/faq
.. _tdlib: https://core.telegram.org/tdlib
.. _MTProto: https://core.telegram.org/mtproto
.. _MTProto vs HTTP Bot API: https://github.com/LonamiWebs/Telethon/wiki/MTProto-vs-HTTP-Bot-API
.. _requests: https://pypi.org/project/requests/
.. _python-telegram-bot: https://python-telegram-bot.readthedocs.io
.. _pyTelegramBotAPI: https://github.com/eternnoir/pyTelegramBotAPI
.. _aiohttp: https://docs.aiohttp.org/en/stable
.. _aiogram: https://aiogram.readthedocs.io
.. _dumbot: https://github.com/Lonami/dumbot
.. _echobot.py: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py

View File

@ -0,0 +1,169 @@
.. _chats-channels:
=================
Chats vs Channels
=================
Telegram's raw API can get very confusing sometimes, in particular when it
comes to talking about "chats", "channels", "groups", "megagroups", and all
those concepts.
This section will try to explain what each of these concepts are.
Chats
=====
A ``Chat`` can be used to talk about either the common "subclass" that both
chats and channels share, or the concrete :tl:`Chat` type.
Technically, both :tl:`Chat` and :tl:`Channel` are a form of the `Chat type`_.
**Most of the time**, the term :tl:`Chat` is used to talk about *small group
chats*. When you create a group through an official application, this is the
type that you get. Official applications refer to these as "Group".
Both the bot API and Telethon will add a minus sign (negate) the real chat ID
so that you can tell at a glance, with just a number, the entity type.
For example, if you create a chat with :tl:`CreateChatRequest`, the real chat
ID might be something like `123`. If you try printing it from a
`message.chat_id` you will see `-123`. This ID helps Telethon know you're
talking about a :tl:`Chat`.
Channels
========
Official applications create a *broadcast* channel when you create a new
channel (used to broadcast messages, only administrators can post messages).
Official applications implicitly *migrate* an *existing* :tl:`Chat` to a
*megagroup* :tl:`Channel` when you perform certain actions (exceed user limit,
add a public username, set certain permissions, etc.).
A ``Channel`` can be created directly with :tl:`CreateChannelRequest`, as
either a ``megagroup`` or ``broadcast``.
Official applications use the term "channel" **only** for broadcast channels.
The API refers to the different types of :tl:`Channel` with certain attributes:
* A **broadcast channel** is a :tl:`Channel` with the ``channel.broadcast``
attribute set to `True`.
* A **megagroup channel** is a :tl:`Channel` with the ``channel.megagroup``
attribute set to `True`. Official applications refer to this as "supergroup".
* A **gigagroup channel** is a :tl:`Channel` with the ``channel.gigagroup``
attribute set to `True`. Official applications refer to this as "broadcast
groups", and is used when a megagroup becomes very large and administrators
want to transform it into something where only they can post messages.
Both the bot API and Telethon will "concatenate" ``-100`` to the real chat ID
so that you can tell at a glance, with just a number, the entity type.
For example, if you create a new broadcast channel, the real channel ID might
be something like `456`. If you try printing it from a `message.chat_id` you
will see `-1000000000456`. This ID helps Telethon know you're talking about a
:tl:`Channel`.
Converting IDs
==============
You can convert between the "marked" identifiers (prefixed with a minus sign)
and the real ones with ``utils.resolve_id``. It will return a tuple with the
real ID, and the peer type (the class):
.. code-block:: python
from telethon import utils
real_id, peer_type = utils.resolve_id(-1000000000456)
print(real_id) # 456
print(peer_type) # <class 'telethon.tl.types.PeerChannel'>
peer = peer_type(real_id)
print(peer) # PeerChannel(channel_id=456)
The reverse operation can be done with ``utils.get_peer_id``:
.. code-block:: python
print(utils.get_peer_id(types.PeerChannel(456))) # -1000000000456
Note that this function can also work with other types, like :tl:`Chat` or
:tl:`Channel` instances.
If you need to convert other types like usernames which might need to perform
API calls to find out the identifier, you can use ``client.get_peer_id``:
.. code-block:: python
print(await client.get_peer_id('me')) # your id
If there is no "mark" (no minus sign), Telethon will assume your identifier
refers to a :tl:`User`. If this is **not** the case, you can manually fix it:
.. code-block:: python
from telethon import types
await client.send_message(types.PeerChannel(456), 'hello')
# ^^^^^^^^^^^^^^^^^ explicit peer type
A note on raw API
=================
Certain methods only work on a :tl:`Chat`, and some others only work on a
:tl:`Channel` (and these may only work in broadcast, or megagroup). Your code
likely knows what it's working with, so it shouldn't be too much of an issue.
If you need to find the :tl:`Channel` from a :tl:`Chat` that migrated to it,
access the `migrated_to` property:
.. code-block:: python
# chat is a Chat
channel = await client.get_entity(chat.migrated_to)
# channel is now a Channel
Channels do not have a "migrated_from", but a :tl:`ChannelFull` does. You can
use :tl:`GetFullChannelRequest` to obtain this:
.. code-block:: python
from telethon import functions
full = await client(functions.channels.GetFullChannelRequest(your_channel))
full_channel = full.full_chat
# full_channel is a ChannelFull
print(full_channel.migrated_from_chat_id)
This way, you can also access the linked discussion megagroup of a broadcast channel:
.. code-block:: python
print(full_channel.linked_chat_id) # prints ID of linked discussion group or None
You do not need to use ``client.get_entity`` to access the
``migrated_from_chat_id`` :tl:`Chat` or the ``linked_chat_id`` :tl:`Channel`.
They are in the ``full.chats`` attribute:
.. code-block:: python
if full_channel.migrated_from_chat_id:
migrated_from_chat = next(c for c in full.chats if c.id == full_channel.migrated_from_chat_id)
print(migrated_from_chat.title)
if full_channel.linked_chat_id:
linked_group = next(c for c in full.chats if c.id == full_channel.linked_chat_id)
print(linked_group.username)
.. _Chat type: https://tl.telethon.dev/types/chat.html

View File

@ -0,0 +1,313 @@
.. _entities:
========
Entities
========
The library widely uses the concept of "entities". An entity will refer
to any :tl:`User`, :tl:`Chat` or :tl:`Channel` object that the API may return
in response to certain methods, such as :tl:`GetUsersRequest`.
.. note::
When something "entity-like" is required, it means that you need to
provide something that can be turned into an entity. These things include,
but are not limited to, usernames, exact titles, IDs, :tl:`Peer` objects,
or even entire :tl:`User`, :tl:`Chat` and :tl:`Channel` objects and even
phone numbers **from people you have in your contact list**.
To "encounter" an ID, you would have to "find it" like you would in the
normal app. If the peer is in your dialogs, you would need to
`client.get_dialogs() <telethon.client.dialogs.DialogMethods.get_dialogs>`.
If the peer is someone in a group, you would similarly
`client.get_participants(group) <telethon.client.chats.ChatMethods.get_participants>`.
Once you have encountered an ID, the library will (by default) have saved
their ``access_hash`` for you, which is needed to invoke most methods.
This is why sometimes you might encounter this error when working with
the library. You should ``except ValueError`` and run code that you know
should work to find the entity.
.. contents::
What is an Entity?
==================
A lot of methods and requests require *entities* to work. For example,
you send a message to an *entity*, get the username of an *entity*, and
so on.
There are a lot of things that work as entities: usernames, phone numbers,
chat links, invite links, IDs, and the types themselves. That is, you can
use any of those when you see an "entity" is needed.
.. note::
Remember that the phone number must be in your contact list before you
can use it.
You should use, **from better to worse**:
1. Input entities. For example, `event.input_chat
<telethon.tl.custom.chatgetter.ChatGetter.input_chat>`,
`message.input_sender
<telethon.tl.custom.sendergetter.SenderGetter.input_sender>`,
or caching an entity you will use a lot with
``entity = await client.get_input_entity(...)``.
2. Entities. For example, if you had to get someone's
username, you can just use ``user`` or ``channel``.
It will work. Only use this option if you already have the entity!
3. IDs. This will always look the entity up from the
cache (the ``*.session`` file caches seen entities).
4. Usernames, phone numbers and links. The cache will be
used too (unless you force a `client.get_entity()
<telethon.client.users.UserMethods.get_entity>`),
but may make a request if the username, phone or link
has not been found yet.
In recent versions of the library, the following two are equivalent:
.. code-block:: python
async def handler(event):
await client.send_message(event.sender_id, 'Hi')
await client.send_message(event.input_sender, 'Hi')
If you need to be 99% sure that the code will work (sometimes it's
simply impossible for the library to find the input entity), or if
you will reuse the chat a lot, consider using the following instead:
.. code-block:: python
async def handler(event):
# This method may make a network request to find the input sender.
# Properties can't make network requests, so we need a method.
sender = await event.get_input_sender()
await client.send_message(sender, 'Hi')
await client.send_message(sender, 'Hi')
Getting Entities
================
Through the use of the :ref:`sessions`, the library will automatically
remember the ID and hash pair, along with some extra information, so
you're able to just do this:
.. code-block:: python
# (These examples assume you are inside an "async def")
#
# Dialogs are the "conversations you have open".
# This method returns a list of Dialog, which
# has the .entity attribute and other information.
#
# This part is IMPORTANT, because it fills the entity cache.
dialogs = await client.get_dialogs()
# All of these work and do the same.
username = await client.get_entity('username')
username = await client.get_entity('t.me/username')
username = await client.get_entity('https://telegram.dog/username')
# Other kind of entities.
channel = await client.get_entity('telegram.me/joinchat/AAAAAEkk2WdoDrB4-Q8-gg')
contact = await client.get_entity('+34xxxxxxxxx')
friend = await client.get_entity(friend_id)
# Getting entities through their ID (User, Chat or Channel)
entity = await client.get_entity(some_id)
# You can be more explicit about the type for said ID by wrapping
# it inside a Peer instance. This is recommended but not necessary.
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
my_user = await client.get_entity(PeerUser(some_id))
my_chat = await client.get_entity(PeerChat(some_id))
my_channel = await client.get_entity(PeerChannel(some_id))
.. note::
You **don't** need to get the entity before using it! Just let the
library do its job. Use a phone from your contacts, username, ID or
input entity (preferred but not necessary), whatever you already have.
All methods in the :ref:`telethon-client` call `.get_input_entity()
<telethon.client.users.UserMethods.get_input_entity>` prior
to sending the request to save you from the hassle of doing so manually.
That way, convenience calls such as `client.send_message('username', 'hi!')
<telethon.client.messages.MessageMethods.send_message>`
become possible.
Every entity the library encounters (in any response to any call) will by
default be cached in the ``.session`` file (an SQLite database), to avoid
performing unnecessary API calls. If the entity cannot be found, additonal
calls like :tl:`ResolveUsernameRequest` or :tl:`GetContactsRequest` may be
made to obtain the required information.
Entities vs. Input Entities
===========================
.. note::
This section is informative, but worth reading. The library
will transparently handle all of these details for you.
On top of the normal types, the API also make use of what they call their
``Input*`` versions of objects. The input version of an entity (e.g.
:tl:`InputPeerUser`, :tl:`InputChat`, etc.) only contains the minimum
information that's required from Telegram to be able to identify
who you're referring to: a :tl:`Peer`'s **ID** and **hash**. They
are named like this because they are input parameters in the requests.
Entities' ID are the same for all user and bot accounts, however, the access
hash is **different for each account**, so trying to reuse the access hash
from one account in another will **not** work.
Sometimes, Telegram only needs to indicate the type of the entity along
with their ID. For this purpose, :tl:`Peer` versions of the entities also
exist, which just have the ID. You cannot get the hash out of them since
you should not be needing it. The library probably has cached it before.
Peers are enough to identify an entity, but they are not enough to make
a request with them. You need to know their hash before you can
"use them", and to know the hash you need to "encounter" them, let it
be in your dialogs, participants, message forwards, etc.
.. note::
You *can* use peers with the library. Behind the scenes, they are
replaced with the input variant. Peers "aren't enough" on their own
but the library will do some more work to use the right type.
As we just mentioned, API calls don't need to know the whole information
about the entities, only their ID and hash. For this reason, another method,
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
is available. This will always use the cache while possible, making zero API
calls most of the time. When a request is made, if you provided the full
entity, e.g. an :tl:`User`, the library will convert it to the required
:tl:`InputPeer` automatically for you.
**You should always favour**
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
**over**
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
for this reason! Calling the latter will always make an API call to get
the most recent information about said entity, but invoking requests don't
need this information, just the :tl:`InputPeer`. Only use
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
if you need to get actual information, like the username, name, title, etc.
of the entity.
To further simplify the workflow, since the version ``0.16.2`` of the
library, the raw requests you make to the API are also able to call
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
wherever needed, so you can even do things like:
.. code-block:: python
await client(SendMessageRequest('username', 'hello'))
The library will call the ``.resolve()`` method of the request, which will
resolve ``'username'`` with the appropriated :tl:`InputPeer`. Don't worry if
you don't get this yet, but remember some of the details here are important.
Full Entities
=============
In addition to :tl:`PeerUser`, :tl:`InputPeerUser`, :tl:`User` (and its
variants for chats and channels), there is also the concept of :tl:`UserFull`.
This full variant has additional information such as whether the user is
blocked, its notification settings, the bio or about of the user, etc.
There is also :tl:`messages.ChatFull` which is the equivalent of full entities
for chats and channels, with also the about section of the channel. Note that
the ``users`` field only contains bots for the channel (so that clients can
suggest commands to use).
You can get both of these by invoking :tl:`GetFullUser`, :tl:`GetFullChat`
and :tl:`GetFullChannel` respectively.
Accessing Entities
==================
Although it's explicitly noted in the documentation that messages
*subclass* `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`
and `SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>`,
some people still don't get inheritance.
When the documentation says "Bases: `telethon.tl.custom.chatgetter.ChatGetter`"
it means that the class you're looking at, *also* can act as the class it
bases. In this case, `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`
knows how to get the *chat* where a thing belongs to.
So, a `Message <telethon.tl.custom.message.Message>` is a
`ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`.
That means you can do this:
.. code-block:: python
message.is_private
message.chat_id
await message.get_chat()
# ...etc
`SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>` is similar:
.. code-block:: python
message.user_id
await message.get_input_sender()
message.user
# ...etc
Quite a few things implement them, so it makes sense to reuse the code.
For example, all events (except raw updates) implement `ChatGetter
<telethon.tl.custom.chatgetter.ChatGetter>` since all events occur
in some chat.
Summary
=======
TL;DR; If you're here because of *"Could not find the input entity for"*,
you must ask yourself "how did I find this entity through official
applications"? Now do the same with the library. Use what applies:
.. code-block:: python
# (These examples assume you are inside an "async def")
async with client:
# Does it have a username? Use it!
entity = await client.get_entity(username)
# Do you have a conversation open with them? Get dialogs.
await client.get_dialogs()
# Are they participant of some group? Get them.
await client.get_participants('username')
# Is the entity the original sender of a forwarded message? Get it.
await client.get_messages('username', 100)
# NOW you can use the ID, anywhere!
await client.send_message(123456, 'Hi!')
entity = await client.get_entity(123456)
print(entity)
Once the library has "seen" the entity, you can use their **integer** ID.
You can't use entities from IDs the library hasn't seen. You must make the
library see them *at least once* and disconnect properly. You know where
the entities are and you must tell the library. It won't guess for you.

View File

@ -0,0 +1,155 @@
.. _rpc-errors:
==========
RPC Errors
==========
RPC stands for Remote Procedure Call, and when the library raises
a ``RPCError``, it's because you have invoked some of the API
methods incorrectly (wrong parameters, wrong permissions, or even
something went wrong on Telegram's server).
You should import the errors from ``telethon.errors`` like so:
.. code-block:: python
from telethon import errors
try:
async with client.takeout() as takeout:
...
except errors.TakeoutInitDelayError as e:
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ here we except TAKEOUT_INIT_DELAY
print('Must wait', e.seconds, 'before takeout')
There isn't any official list of all possible RPC errors, so the
`list of known errors`_ is provided on a best-effort basis. When new methods
are available, the list may be lacking since we simply don't know what errors
can raise from them.
Once we do find out about a new error and what causes it, the list is
updated, so if you see an error without a specific class, do report it
(and what method caused it)!.
This list is used to generate documentation for the `raw API page`_.
For example, if we want to know what errors can occur from
`messages.sendMessage`_ we can simply navigate to its raw API page
and find it has 24 known RPC errors at the time of writing.
Base Errors
===========
All the "base" errors are listed in :ref:`telethon-errors`.
Any other more specific error will be a subclass of these.
If the library isn't aware of a specific error just yet, it will instead
raise one of these superclasses. This means you may find stuff like this:
.. code-block:: text
telethon.errors.rpcbaseerrors.BadRequestError: RPCError 400: MESSAGE_POLL_CLOSED (caused by SendVoteRequest)
If you do, make sure to open an issue or send a pull request to update the
`list of known errors`_.
Common Errors
=============
These are some of the errors you may normally need to deal with:
- ``FloodWaitError`` (420), the same request was repeated many times.
Must wait ``.seconds`` (you can access this attribute). For example:
.. code-block:: python
...
from telethon import errors
try:
messages = await client.get_messages(chat)
print(messages[0].text)
except errors.FloodWaitError as e:
print('Have to sleep', e.seconds, 'seconds')
time.sleep(e.seconds)
- ``SessionPasswordNeededError``, if you have setup two-steps
verification on Telegram and are trying to sign in.
- ``FilePartMissingError``, if you have tried to upload an empty file.
- ``ChatAdminRequiredError``, you don't have permissions to perform
said operation on a chat or channel. Try avoiding filters, i.e. when
searching messages.
The generic classes for different error codes are:
- ``InvalidDCError`` (303), the request must be repeated on another DC.
- ``BadRequestError`` (400), the request contained errors.
- ``UnauthorizedError`` (401), the user is not authorized yet.
- ``ForbiddenError`` (403), privacy violation error.
- ``NotFoundError`` (404), make sure you're invoking ``Request``\ 's!
If the error is not recognised, it will only be an ``RPCError``.
You can refer to all errors from Python through the ``telethon.errors``
module. If you don't know what attributes they have, try printing their
dir (like ``print(dir(e))``).
Attributes
==========
Some of the errors carry additional data in them. When they look like
``EMAIL_UNCONFIRMED_X``, the ``_X`` value will be accessible from the
error instance. The current list of errors that do this is the following:
- ``EmailUnconfirmedError`` has ``.code_length``.
- ``FileMigrateError`` has ``.new_dc``.
- ``FilePartMissingError`` has ``.which``.
- ``FloodTestPhoneWaitError`` has ``.seconds``.
- ``FloodWaitError`` has ``.seconds``.
- ``InterdcCallErrorError`` has ``.dc``.
- ``InterdcCallRichErrorError`` has ``.dc``.
- ``NetworkMigrateError`` has ``.new_dc``.
- ``PhoneMigrateError`` has ``.new_dc``.
- ``SlowModeWaitError`` has ``.seconds``.
- ``TakeoutInitDelayError`` has ``.seconds``.
- ``UserMigrateError`` has ``.new_dc``.
Avoiding Limits
===============
Don't spam. You won't get ``FloodWaitError`` or your account banned or
deleted if you use the library *for legit use cases*. Make cool tools.
Don't spam! Nobody knows the exact limits for all requests since they
depend on a lot of factors, so don't bother asking.
Still, if you do have a legit use case and still get those errors, the
library will automatically sleep when they are smaller than 60 seconds
by default. You can set different "auto-sleep" thresholds:
.. code-block:: python
client.flood_sleep_threshold = 0 # Don't auto-sleep
client.flood_sleep_threshold = 24 * 60 * 60 # Sleep always
You can also except it and act as you prefer:
.. code-block:: python
from telethon.errors import FloodWaitError
try:
...
except FloodWaitError as e:
print('Flood waited for', e.seconds)
quit(1)
VoIP numbers are very limited, and some countries are more limited too.
.. _list of known errors: https://github.com/LonamiWebs/Telethon/blob/v1/telethon_generator/data/errors.csv
.. _raw API page: https://tl.telethon.dev/
.. _messages.sendMessage: https://tl.telethon.dev/methods/messages/send_message.html

View File

@ -0,0 +1,420 @@
.. _full-api:
============
The Full API
============
.. important::
While you have access to this, you should always use the friendly
methods listed on :ref:`client-ref` unless you have a better reason
not to, like a method not existing or you wanting more control.
.. contents::
Introduction
============
The :ref:`telethon-client` doesn't offer a method for every single request
the Telegram API supports. However, it's very simple to *call* or *invoke*
any request defined in Telegram's API.
This section will teach you how to use what Telethon calls the `TL reference`_.
The linked page contains a list and a way to search through *all* types
generated from the definition of Telegram's API (in ``.tl`` file format,
hence the name). These types include requests and constructors.
.. note::
The reason to keep both https://tl.telethon.dev and this
documentation alive is that the former allows instant search results
as you type, and a "Copy import" button. If you like namespaces, you
can also do ``from telethon.tl import types, functions``. Both work.
Telegram makes these ``.tl`` files public, which other implementations, such
as Telethon, can also use to generate code. These files are versioned under
what's called "layers". ``.tl`` files consist of thousands of definitions,
and newer layers often add, change, or remove them. Each definition refers
to either a Remote Procedure Call (RPC) function, or a type (which the
`TL reference`_ calls "constructors", as they construct particular type
instances).
As such, the `TL reference`_ is a good place to go to learn about all possible
requests, types, and what they look like. If you're curious about what's been
changed between layers, you can refer to the `TL diff`_ site.
Navigating the TL reference
===========================
Functions
---------
"Functions" is the term used for the Remote Procedure Calls (RPC) that can be
sent to Telegram to ask it to perform something (e.g. "send message"). These
requests have an associated return type. These can be invoked ("called"):
.. code-block:: python
client = TelegramClient(...)
function_instance = SomeRequest(...)
# Invoke the request
returned_type = await client(function_instance)
Whenever you find the type for a function in the `TL reference`_, the page
will contain the following information:
* What type of account can use the method. This information is regenerated
from time to time (by attempting to invoke the function under both account
types and finding out where it fails). Some requests can only be used by
bot accounts, others by user accounts, and others by both.
* The TL definition. This helps you get a feel for the what the function
looks like. This is not Python code. It just contains the definition in
a concise manner.
* "Copy import" button. Does what it says: it will copy the necessary Python
code to import the function to your system's clipboard for easy access.
* Returns. The returned type. When you invoke the function, this is what the
result will be. It also includes which of the constructors can be returned
inline, to save you a click.
* Parameters. The parameters accepted by the function, including their type,
whether they expect a list, and whether they're optional.
* Known RPC errors. A best-effort list of known errors the request may cause.
This list is not complete and may be out of date, but should provide an
overview of what could go wrong.
* Example. Autogenerated example, showcasing how you may want to call it.
Bear in mind that this is *autogenerated*. It may be spitting out non-sense.
The goal of this example is not to show you everything you can do with the
request, only to give you a feel for what it looks like to use it.
It is very important to click through the links and navigate to get the full
picture. A specific page will show you what the specific function returns and
needs as input parameters. But it may reference other types, so you need to
navigate to those to learn what those contain or need.
Types
-----
"Types" as understood by TL are not actually generated in Telethon.
They would be the "abstract base class" of the constructors, but since Python
is duck-typed, there is hardly any need to generate mostly unnecessary code.
The page for a type contains:
* Constructors. Every type will have one or more constructors. These
constructors *are* generated and can be immported and used.
* Requests returning this type. A helpful way to find out "what requests can
return this?". This is how you may learn what request you need to use to
obtain a particular instance of a type.
* Requests accepting this type as input. A helpful way to find out "what
requests can use this type as one of their input parameters?". This is how
you may learn where a type is used.
* Other types containing this type. A helpful way to find out "where else
does this type appear?". This is how you can walk back through nested
objects.
Constructors
------------
Constructors are used to create instances of a particular type, and are also
returned when invoking requests. You will have to create instances yourself
when invoking requests that need a particular type as input.
The page for a constructor contains:
* Belongs to. The parent type. This is a link back to the types page for the
specific constructor. It also contains the sibling constructors inline, to
save you a click.
* Members. Both the input parameters *and* fields the constructor contains.
Using the TL reference
======================
After you've found a request you want to send, a good start would be to simply
copy and paste the autogenerated example into your script. Then you can simply
tweak it to your needs.
If you want to do it from scratch, first, make sure to import the request into
your code (either using the "Copy import" button near the top, or by manually
spelling out the package under ``telethon.tl.functions.*``).
Then, start reading the parameters one by one. If the parameter cannot be
omitted, you **will** need to specify it, so make sure to spell it out as
an input parameter when constructing the request instance. Let's look at
`PingRequest`_ for example. First, we copy the import:
.. code-block:: python
from telethon.tl.functions import PingRequest
Then, we look at the parameters:
ping_id - long
A single parameter, and it's a long (a integer number with a large range of
values). It doesn't say it can be omitted, so we must provide it, like so:
.. code-block:: python
PingRequest(
ping_id=48641868471
)
(In this case, the ping ID is a random number. You often have to guess what
the parameter needs just by looking at the name.)
Now that we have our request, we can invoke it:
.. code-block:: python
response = await client(PingRequest(
ping_id=48641868471
))
To find out what ``response`` looks like, we can do as the autogenerated
example suggests and "stringify" the result as a pretty-printed string:
.. code-block:: python
print(result.stringify())
This will print out the following:
.. code-block:: python
Pong(
msg_id=781875678118,
ping_id=48641868471
)
Which is a very easy way to get a feel for a response. You should nearly
always print the stringified result, at least once, when trying out requests,
to get a feel for what the response may look like.
But of course, you don't need to do that. Without writing any code, you could
have navigated through the "Returns" link to learn ``PingRequest`` returns a
``Pong``, which only has one constructor, and the constructor has two members,
``msg_id`` and ``ping_id``.
If you wanted to create your own ``Pong``, you would use both members as input
parameters:
.. code-block:: python
my_pong = Pong(
msg_id=781875678118,
ping_id=48641868471
)
(Yes, constructing object instances can use the same code that ``.stringify``
would return!)
And if you wanted to access the ``msg_id`` member, you would simply access it
like any other attribute access in Python:
.. code-block:: python
print(response.msg_id)
Example walkthrough
===================
Say `client.send_message()
<telethon.client.messages.MessageMethods.send_message>` didn't exist,
we could `use the search`_ to look for "message". There we would find
:tl:`SendMessageRequest`, which we can work with.
Every request is a Python class, and has the parameters needed for you
to invoke it. You can also call ``help(request)`` for information on
what input parameters it takes. Remember to "Copy import to the
clipboard", or your script won't be aware of this class! Now we have:
.. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest
If you're going to use a lot of these, you may do:
.. code-block:: python
from telethon.tl import types, functions
# We now have access to 'functions.messages.SendMessageRequest'
We see that this request must take at least two parameters, a ``peer``
of type :tl:`InputPeer`, and a ``message`` which is just a Python
`str`\ ing.
How can we retrieve this :tl:`InputPeer`? We have two options. We manually
construct one, for instance:
.. code-block:: python
from telethon.tl.types import InputPeerUser
peer = InputPeerUser(user_id, user_hash)
Or we call `client.get_input_entity()
<telethon.client.users.UserMethods.get_input_entity>`:
.. code-block:: python
import telethon
async def main():
peer = await client.get_input_entity('someone')
client.loop.run_until_complete(main())
.. note::
Remember that ``await`` must occur inside an ``async def``.
Every full API example assumes you already know and do this.
When you're going to invoke an API method, most require you to pass an
:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
is more straightforward (and often immediate, if you've seen the user before,
know their ID, etc.). If you also **need** to have information about the whole
user, use `client.get_entity() <telethon.client.users.UserMethods.get_entity>`
instead:
.. code-block:: python
entity = await client.get_entity('someone')
In the later case, when you use the entity, the library will cast it to
its "input" version for you. If you already have the complete user and
want to cache its input version so the library doesn't have to do this
every time its used, simply call `telethon.utils.get_input_peer`:
.. code-block:: python
from telethon import utils
peer = utils.get_input_peer(entity)
.. note::
Since ``v0.16.2`` this is further simplified. The ``Request`` itself
will call `client.get_input_entity
<telethon.client.users.UserMethods.get_input_entity>` for you when
required, but it's good to remember what's happening.
After this small parenthesis about `client.get_entity
<telethon.client.users.UserMethods.get_entity>` versus
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`,
we have everything we need. To invoke our
request we do:
.. code-block:: python
result = await client(SendMessageRequest(peer, 'Hello there!'))
Message sent! Of course, this is only an example. There are over 250
methods available as of layer 80, and you can use every single of them
as you wish. Remember to use the right types! To sum up:
.. code-block:: python
result = await client(SendMessageRequest(
await client.get_input_entity('username'), 'Hello there!'
))
This can further be simplified to:
.. code-block:: python
result = await client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = await client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
.. note::
Note that some requests have a "hash" parameter. This is **not**
your ``api_hash``! It likely isn't your self-user ``.access_hash`` either.
It's a special hash used by Telegram to only send a difference of new data
that you don't already have with that request, so you can leave it to 0,
and it should work (which means no hash is known yet).
For those requests having a "limit" parameter, you can often set it to
zero to signify "return default amount". This won't work for all of them
though, for instance, in "messages.search" it will actually return 0 items.
Requests in Parallel
====================
The library will automatically merge outgoing requests into a single
*container*. Telegram's API supports sending multiple requests in a
single container, which is faster because it has less overhead and
the server can run them without waiting for others. You can also
force using a container manually:
.. code-block:: python
async def main():
# Letting the library do it behind the scenes
await asyncio.wait([
client.send_message('me', 'Hello'),
client.send_message('me', ','),
client.send_message('me', 'World'),
client.send_message('me', '.')
])
# Manually invoking many requests at once
await client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ', '),
SendMessageRequest('me', 'World'),
SendMessageRequest('me', '.')
])
Note that you cannot guarantee the order in which they are run.
Try running the above code more than one time. You will see the
order in which the messages arrive is different.
If you use the raw API (the first option), you can use ``ordered``
to tell the server that it should run the requests sequentially.
This will still be faster than going one by one, since the server
knows all requests directly:
.. code-block:: python
await client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ', '),
SendMessageRequest('me', 'World'),
SendMessageRequest('me', '.')
], ordered=True)
If any of the requests fails with a Telegram error (not connection
errors or any other unexpected events), the library will raise
`telethon.errors.common.MultiError`. You can ``except`` this
and still access the successful results:
.. code-block:: python
from telethon.errors import MultiError
try:
await client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ''),
SendMessageRequest('me', 'World')
], ordered=True)
except MultiError as e:
# The first and third requests worked.
first = e.results[0]
third = e.results[2]
# The second request failed.
second = e.exceptions[1]
.. _TL reference: https://tl.telethon.dev
.. _TL diff: https://diff.telethon.dev
.. _PingRequest: https://tl.telethon.dev/methods/ping.html
.. _use the search: https://tl.telethon.dev/?q=message&redirect=no

View File

@ -4,6 +4,14 @@
Session Files
==============
.. contents::
They are an important part for the library to be efficient, such as caching
and handling your authorization key (or you would have to login every time!).
What are Sessions?
==================
The first parameter you pass to the constructor of the
:ref:`TelegramClient <telethon-client>` is
the ``session``, and defaults to be the session name (or full path). That is,
@ -38,10 +46,14 @@ by setting ``client.session.save_entities = False``.
Different Session Storage
*************************
=========================
If you don't want to use the default SQLite session storage, you can also use
one of the other implementations or implement your own storage.
If you don't want to use the default SQLite session storage, you can also
use one of the other implementations or implement your own storage.
While it's often not the case, it's possible that SQLite is slow enough to
be noticeable, in which case you can also use a different storage. Note that
this is rare and most people won't have this issue, but it's worth a mention.
To use a custom session storage, simply pass the custom session instance to
:ref:`TelegramClient <telethon-client>` instead of
@ -49,13 +61,15 @@ the session name.
Telethon contains three implementations of the abstract ``Session`` class:
* ``MemorySession``: stores session data within memory.
* ``SQLiteSession``: stores sessions within on-disk SQLite databases. Default.
* ``StringSession``: stores session data within memory,
.. currentmodule:: telethon.sessions
* `MemorySession <memory.MemorySession>`: stores session data within memory.
* `SQLiteSession <sqlite.SQLiteSession>`: stores sessions within on-disk SQLite databases. Default.
* `StringSession <string.StringSession>`: stores session data within memory,
but can be saved as a string.
You can import these ``from telethon.sessions``. For example, using the
``StringSession`` is done as follows:
`StringSession <string.StringSession>` is done as follows:
.. code-block:: python
@ -86,25 +100,30 @@ There are other community-maintained implementations available:
* `Redis <https://github.com/ezdev128/telethon-session-redis>`_:
stores all sessions in a single Redis data store.
* `MongoDB <https://github.com/watzon/telethon-session-mongo>`_:
stores the current session in a MongoDB database.
Creating your Own Storage
*************************
=========================
The easiest way to create your own storage implementation is to use
``MemorySession`` as the base and check out how ``SQLiteSession`` or
one of the community-maintained implementations work. You can find the
relevant Python files under the ``sessions`` directory in Telethon.
`MemorySession <memory.MemorySession>` as the base and check out how
`SQLiteSession <sqlite.SQLiteSession>` or one of the community-maintained
implementations work. You can find the relevant Python files under the
``sessions/`` directory in the Telethon's repository.
After you have made your own implementation, you can add it to the
community-maintained session implementation list above with a pull request.
String Sessions
***************
===============
``StringSession`` are a convenient way to embed your login credentials
directly into your code for extremely easy portability, since all they
take is a string to be able to login without asking for your phone and
code (or faster start if you're using a bot token).
`StringSession <string.StringSession>` are a convenient way to embed your
login credentials directly into your code for extremely easy portability,
since all they take is a string to be able to login without asking for your
phone and code (or faster start if you're using a bot token).
The easiest way to generate a string session is as follows:
@ -124,7 +143,7 @@ output (likely your terminal).
.. warning::
**Keep this string safe!** Anyone with this string can use it
to login into your account and do anything they want to to do.
to login into your account and do anything they want to.
This is similar to leaking your ``*.session`` files online,
but it is easier to leak a string than it is to leak a file.
@ -138,7 +157,7 @@ you can save it in a variable directly:
string = '1aaNk8EX-YRfwoRsebUkugFvht6DUPi_Q25UOCzOAqzc...'
with TelegramClient(StringSession(string), api_id, api_hash) as client:
client.send_message('me', 'Hi')
client.loop.run_until_complete(client.send_message('me', 'Hi'))
These strings are really convenient for using in places like Heroku since

View File

@ -0,0 +1,88 @@
======================
String-based Debugging
======================
Debugging is *really* important. Telegram's API is really big and there
are a lot of things that you should know. Such as, what attributes or fields
does a result have? Well, the easiest thing to do is printing it:
.. code-block:: python
entity = await client.get_entity('username')
print(entity)
That will show a huge **string** similar to the following:
.. code-block:: python
Channel(id=1066197625, title='Telegram Usernames', photo=ChatPhotoEmpty(), date=datetime.datetime(2016, 12, 16, 15, 15, 43, tzinfo=datetime.timezone.utc), version=0, creator=False, left=True, broadcast=True, verified=True, megagroup=False, restricted=False, signatures=False, min=False, scam=False, has_link=False, has_geo=False, slowmode_enabled=False, access_hash=-6309373984955162244, username='username', restriction_reason=[], admin_rights=None, banned_rights=None, default_banned_rights=None, participants_count=None)
That's a lot of text. But as you can see, all the properties are there.
So if you want the title you **don't use regex** or anything like
splitting ``str(entity)`` to get what you want. You just access the
attribute you need:
.. code-block:: python
title = entity.title
Can we get better than the shown string, though? Yes!
.. code-block:: python
print(entity.stringify())
Will show a much better representation:
.. code-block:: python
Channel(
id=1066197625,
title='Telegram Usernames',
photo=ChatPhotoEmpty(
),
date=datetime.datetime(2016, 12, 16, 15, 15, 43, tzinfo=datetime.timezone.utc),
version=0,
creator=False,
left=True,
broadcast=True,
verified=True,
megagroup=False,
restricted=False,
signatures=False,
min=False,
scam=False,
has_link=False,
has_geo=False,
slowmode_enabled=False,
access_hash=-6309373984955162244,
username='username',
restriction_reason=[
],
admin_rights=None,
banned_rights=None,
default_banned_rights=None,
participants_count=None
)
Now it's easy to see how we could get, for example,
the ``year`` value. It's inside ``date``:
.. code-block:: python
channel_year = entity.date.year
You don't need to print everything to see what all the possible values
can be. You can just search in http://tl.telethon.dev/.
Remember that you can use Python's `isinstance
<https://docs.python.org/3/library/functions.html#isinstance>`_
to check the type of something. For example:
.. code-block:: python
from telethon import types
if isinstance(entity.photo, types.ChatPhotoEmpty):
print('Channel has no photo')

View File

@ -0,0 +1,228 @@
================
Updates in Depth
================
Properties vs. Methods
======================
The event shown above acts just like a `custom.Message
<telethon.tl.custom.message.Message>`, which means you
can access all the properties it has, like ``.sender``.
**However** events are different to other methods in the client, like
`client.get_messages <telethon.client.messages.MessageMethods.get_messages>`.
Events *may not* send information about the sender or chat, which means it
can be `None`, but all the methods defined in the client always have this
information so it doesn't need to be re-fetched. For this reason, you have
``get_`` methods, which will make a network call if necessary.
In short, you should do this:
.. code-block:: python
@client.on(events.NewMessage)
async def handler(event):
# event.input_chat may be None, use event.get_input_chat()
chat = await event.get_input_chat()
sender = await event.get_sender()
buttons = await event.get_buttons()
async def main():
async for message in client.iter_messages('me', 10):
# Methods from the client always have these properties ready
chat = message.input_chat
sender = message.sender
buttons = message.buttons
Notice, properties (`message.sender
<telethon.tl.custom.message.Message.sender>`) don't need an ``await``, but
methods (`message.get_sender
<telethon.tl.custom.message.Message.get_sender>`) **do** need an ``await``,
and you should use methods in events for these properties that may need network.
Events Without the client
=========================
The code of your application starts getting big, so you decide to
separate the handlers into different files. But how can you access
the client from these files? You don't need to! Just `events.register
<telethon.events.register>` them:
.. code-block:: python
# handlers/welcome.py
from telethon import events
@events.register(events.NewMessage('(?i)hello'))
async def handler(event):
client = event.client
await event.respond('Hey!')
await client.send_message('me', 'I said hello to someone')
Registering events is a way of saying "this method is an event handler".
You can use `telethon.events.is_handler` to check if any method is a handler.
You can think of them as a different approach to Flask's blueprints.
It's important to note that this does **not** add the handler to any client!
You never specified the client on which the handler should be used. You only
declared that it is a handler, and its type.
To actually use the handler, you need to `client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>` to the
client (or clients) where they should be added to:
.. code-block:: python
# main.py
from telethon import TelegramClient
import handlers.welcome
with TelegramClient(...) as client:
client.add_event_handler(handlers.welcome.handler)
client.run_until_disconnected()
This also means that you can register an event handler once and
then add it to many clients without re-declaring the event.
Events Without Decorators
=========================
If for any reason you don't want to use `telethon.events.register`,
you can explicitly pass the event handler to use to the mentioned
`client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>`:
.. code-block:: python
from telethon import TelegramClient, events
async def handler(event):
...
with TelegramClient(...) as client:
client.add_event_handler(handler, events.NewMessage)
client.run_until_disconnected()
Similarly, you also have `client.remove_event_handler
<telethon.client.updates.UpdateMethods.remove_event_handler>`
and `client.list_event_handlers
<telethon.client.updates.UpdateMethods.list_event_handlers>`.
The ``event`` argument is optional in all three methods and defaults to
`events.Raw <telethon.events.raw.Raw>` for adding, and `None` when
removing (so all callbacks would be removed).
.. note::
The ``event`` type is ignored in `client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>`
if you have used `telethon.events.register` on the ``callback``
before, since that's the point of using such method at all.
Stopping Propagation of Updates
===============================
There might be cases when an event handler is supposed to be used solitary and
it makes no sense to process any other handlers in the chain. For this case,
it is possible to raise a `telethon.events.StopPropagation` exception which
will cause the propagation of the update through your handlers to stop:
.. code-block:: python
from telethon.events import StopPropagation
@client.on(events.NewMessage)
async def _(event):
# ... some conditions
await event.delete()
# Other handlers won't have an event to work with
raise StopPropagation
@client.on(events.NewMessage)
async def _(event):
# Will never be reached, because it is the second handler
# in the chain.
pass
Remember to check :ref:`telethon-events` if you're looking for
the methods reference.
Understanding asyncio
=====================
With `asyncio`, the library has several tasks running in the background.
One task is used for sending requests, another task is used to receive them,
and a third one is used to handle updates.
To handle updates, you must keep your script running. You can do this in
several ways. For instance, if you are *not* running `asyncio`'s event
loop, you should use `client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>`:
.. code-block:: python
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
...
client.run_until_disconnected()
Behind the scenes, this method is ``await``'ing on the `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>` property,
so the code above and the following are equivalent:
.. code-block:: python
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
async def main():
await client.disconnected
asyncio.run(main())
You could also run `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>`
until it completed.
But if you don't want to ``await``, then you should know what you want
to be doing instead! What matters is that you shouldn't let your script
die. If you don't care about updates, you don't need any of this.
Notice that unlike `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>`,
`client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>` will
handle ``KeyboardInterrupt`` for you. This method is special and can
also be ran while the loop is running, so you can do this:
.. code-block:: python
async def main():
await client.run_until_disconnected()
loop.run_until_complete(main())
Sequential Updates
==================
If you need to process updates sequentially (i.e. not in parallel),
you should set ``sequential_updates=True`` when creating the client:
.. code-block:: python
with TelegramClient(..., sequential_updates=True) as client:
...

View File

@ -20,14 +20,13 @@
import re
import os
import sys
sys.path.insert(0, os.path.abspath(os.curdir))
sys.path.insert(0, os.path.abspath(os.pardir))
root = os.path.abspath(os.path.join(__file__, os.path.pardir, os.path.pardir))
tl_ref_url = 'https://lonamiwebs.github.io/Telethon'
tl_ref_url = 'https://tl.telethon.dev'
# -- General configuration ------------------------------------------------
@ -40,9 +39,15 @@ tl_ref_url = 'https://lonamiwebs.github.io/Telethon'
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
'sphinx.ext.intersphinx',
'custom_roles'
]
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None)
}
# Change the default role so we can avoid prefixing everything with :obj:
default_role = "py:obj"
@ -60,7 +65,7 @@ master_doc = 'index'
# General information about the project.
project = 'Telethon'
copyright = '2017, Lonami'
copyright = '2017 - 2019, Lonami'
author = 'Lonami'
# The version info for the project you're documenting, acts as replacement for
@ -80,7 +85,7 @@ release = version
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@ -88,12 +93,30 @@ language = None
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = 'friendly'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
def skip(app, what, name, obj, would_skip, options):
if name.endswith('__'):
# We want to show special methods names, except some which add clutter
return name in {
'__init__',
'__abstractmethods__',
'__module__',
'__doc__',
'__dict__'
}
return would_skip
def setup(app):
app.connect("autodoc-skip-member", skip)
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
@ -114,7 +137,7 @@ html_theme_options = {
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
@ -186,5 +209,3 @@ texinfo_documents = [
'Miscellaneous'),
]

View File

@ -28,6 +28,7 @@ def make_link_node(rawtext, app, name, options):
return node
# noinspection PyUnusedLocal
def tl_role(name, rawtext, text, lineno, inliner, options=None, content=None):
"""
Link to the TL reference.
@ -45,8 +46,6 @@ def tl_role(name, rawtext, text, lineno, inliner, options=None, content=None):
"""
if options is None:
options = {}
if content is None:
content = []
# TODO Report error on type not found?
# Usage:
@ -63,7 +62,6 @@ def setup(app):
:param app: Sphinx application context.
"""
app.info('Initializing TL reference plugin')
app.add_role('tl', tl_role)
app.add_config_value('tl_ref_url', None, 'env')
return

View File

@ -4,7 +4,7 @@ Project Structure
Main interface
**************
==============
The library itself is under the ``telethon/`` directory. The
``__init__.py`` file there exposes the main ``TelegramClient``, a class
@ -33,13 +33,8 @@ The sender makes use of a ``Connection`` class which knows the format in
which outgoing messages should be sent (how to encode their length and
their body, if they're further encrypted).
For now, all connection modes make use of the ``extensions/tcpclient``,
a C#-like ``TcpClient`` to ease working with sockets in Python. All the
``TcpClient`` know is how to connect through TCP and writing/reading
from the socket with optional cancel.
Auto-generated code
*******************
===================
The files under ``telethon_generator/`` are used to generate the code
that gets placed under ``telethon/tl/``. The parsers take in files in
@ -50,3 +45,7 @@ an index so that they can be imported easily.
Custom documentation can also be generated to easily navigate through
the vast amount of items offered by the API.
If you clone the repository, you will have to run ``python setup.py gen``
in order to generate the code. Installing the library runs the generator
too, but the mentioned command will just generate code.

View File

@ -0,0 +1,13 @@
===============================
Telegram API in Other Languages
===============================
Telethon was made for **Python**, and it has inspired other libraries such as
`gramjs <https://github.com/gram-js/gramjs>`__ (JavaScript) and `grammers
<https://github.com/Lonami/grammers>`__ (Rust). But there is a lot more beyond
those, made independently by different developers.
If you're looking for something like Telethon but in a different programming
language, head over to `Telegram API in Other Languages in the official wiki
<https://github.com/LonamiWebs/Telethon/wiki/Telegram-API-in-Other-Languages>`__
for a (mostly) up-to-date list.

View File

@ -12,7 +12,7 @@ To run Telethon on a test server, use the following code:
You can check your ``'test ip'`` on https://my.telegram.org.
You should set ``None`` session so to ensure you're generating a new
You should set `None` session so to ensure you're generating a new
authorization key for it (it would fail if you used a session where you
had previously connected to another data center).
@ -35,3 +35,7 @@ times, in this case, ``22222`` so we can hardcode that:
client.start(
phone='9996621234', code_callback=lambda: '22222'
)
Note that Telegram has changed the length of login codes multiple times in the
past, so if ``dc_id`` repeated five times does not work, try repeating it six
times.

View File

@ -0,0 +1,87 @@
=====
Tests
=====
Telethon uses `Pytest <https://pytest.org/>`__, for testing, `Tox
<https://tox.readthedocs.io/en/latest/>`__ for environment setup, and
`pytest-asyncio <https://pypi.org/project/pytest-asyncio/>`__ and `pytest-cov
<https://pytest-cov.readthedocs.io/en/latest/>`__ for asyncio and
`coverage <https://coverage.readthedocs.io/>`__ integration.
While reading the full documentation for these is probably a good idea, there
is a lot to read, so a brief summary of these tools is provided below for
convienience.
Brief Introduction to Pytest
============================
`Pytest <https://pytest.org/>`__ is a tool for discovering and running python
tests, as well as allowing modular reuse of test setup code using fixtures.
Most Pytest tests will look something like this::
from module import my_thing, my_other_thing
def test_my_thing(fixture):
assert my_thing(fixture) == 42
@pytest.mark.asyncio
async def test_my_thing(event_loop):
assert await my_other_thing(loop=event_loop) == 42
Note here:
1. The test imports one specific function. The role of unit tests is to test
that the implementation of some unit, like a function or class, works.
It's role is not so much to test that components interact well with each
other. I/O, such as connecting to remote servers, should be avoided. This
helps with quickly identifying the source of an error, finding silent
breakage, and makes it easier to cover all possible code paths.
System or integration tests can also be useful, but are currently out of
scope of Telethon's automated testing.
2. A function ``test_my_thing`` is declared. Pytest searches for files
starting with ``test_``, classes starting with ``Test`` and executes any
functions or methods starting with ``test_`` it finds.
3. The function is declared with a parameter ``fixture``. Fixtures are used to
request things required to run the test, such as temporary directories,
free TCP ports, Connections, etc. Fixtures are declared by simply adding
the fixture name as parameter. A full list of available fixtures can be
found with the ``pytest --fixtures`` command.
4. The test uses a simple ``assert`` to test some condition is valid. Pytest
uses some magic to ensure that the errors from this are readable and easy
to debug.
5. The ``pytest.mark.asyncio`` fixture is provided by ``pytest-asyncio``. It
starts a loop and executes a test function as coroutine. This should be
used for testing asyncio code. It also declares the ``event_loop``
fixture, which will request an ``asyncio`` event loop.
Brief Introduction to Tox
=========================
`Tox <https://tox.readthedocs.io/en/latest/>`__ is a tool for automated setup
of virtual environments for testing. While the tests can be run directly by
just running ``pytest``, this only tests one specific python version in your
existing environment, which will not catch e.g. undeclared dependencies, or
version incompatabilities.
Tox environments are declared in the ``tox.ini`` file. The default
environments, declared at the top, can be simply run with ``tox``. The option
``tox -e py36,flake`` can be used to request specific environments to be run.
Brief Introduction to Pytest-cov
================================
Coverage is a useful metric for testing. It measures the lines of code and
branches that are exercised by the tests. The higher the coverage, the more
likely it is that any coding errors will be caught by the tests.
A brief coverage report can be generated with the ``--cov`` option to ``tox``,
which will be passed on to ``pytest``. Additionally, the very useful HTML
report can be generated with ``--cov --cov-report=html``, which contains a
browsable copy of the source code, annotated with coverage information for each
line.

View File

@ -0,0 +1,128 @@
===============================
Working with Chats and Channels
===============================
.. note::
These examples assume you have read :ref:`full-api`.
.. contents::
Joining a chat or channel
=========================
Note that :tl:`Chat` are normal groups, and :tl:`Channel` are a
special form of :tl:`Chat`, which can also be super-groups if
their ``megagroup`` member is `True`.
Joining a public channel
========================
Once you have the :ref:`entity <entities>` of the channel you want to join
to, you can make use of the :tl:`JoinChannelRequest` to join such channel:
.. code-block:: python
from telethon.tl.functions.channels import JoinChannelRequest
await client(JoinChannelRequest(channel))
# In the same way, you can also leave such channel
from telethon.tl.functions.channels import LeaveChannelRequest
await client(LeaveChannelRequest(input_channel))
For more on channels, check the `channels namespace`__.
__ https://tl.telethon.dev/methods/channels/index.html
Joining a private chat or channel
=================================
If all you have is a link like this one:
``https://t.me/joinchat/AAAAAFFszQPyPEZ7wgxLtd``, you already have
enough information to join! The part after the
``https://t.me/joinchat/``, this is, ``AAAAAFFszQPyPEZ7wgxLtd`` on this
example, is the ``hash`` of the chat or channel. Now you can use
:tl:`ImportChatInviteRequest` as follows:
.. code-block:: python
from telethon.tl.functions.messages import ImportChatInviteRequest
updates = await client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg'))
Adding someone else to such chat or channel
===========================================
If you don't want to add yourself, maybe because you're already in,
you can always add someone else with the :tl:`AddChatUserRequest`, which
use is very straightforward, or :tl:`InviteToChannelRequest` for channels:
.. code-block:: python
# For normal chats
from telethon.tl.functions.messages import AddChatUserRequest
# Note that ``user_to_add`` is NOT the name of the parameter.
# It's the user you want to add (``user_id=user_to_add``).
await client(AddChatUserRequest(
chat_id,
user_to_add,
fwd_limit=10 # Allow the user to see the 10 last messages
))
# For channels (which includes megagroups)
from telethon.tl.functions.channels import InviteToChannelRequest
await client(InviteToChannelRequest(
channel,
[users_to_add]
))
Note that this method will only really work for friends or bot accounts.
Trying to mass-add users with this approach will not work, and can put both
your account and group to risk, possibly being flagged as spam and limited.
Checking a link without joining
===============================
If you don't need to join but rather check whether it's a group or a
channel, you can use the :tl:`CheckChatInviteRequest`, which takes in
the hash of said channel or group.
Increasing View Count in a Channel
==================================
It has been asked `quite`__ `a few`__ `times`__ (really, `many`__), and
while I don't understand why so many people ask this, the solution is to
use :tl:`GetMessagesViewsRequest`, setting ``increment=True``:
.. code-block:: python
# Obtain `channel' through dialogs or through client.get_entity() or anyhow.
# Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list.
await client(GetMessagesViewsRequest(
peer=channel,
id=msg_ids,
increment=True
))
Note that you can only do this **once or twice a day** per account,
running this in a loop will obviously not increase the views forever
unless you wait a day between each iteration. If you run it any sooner
than that, the views simply won't be increased.
__ https://github.com/LonamiWebs/Telethon/issues/233
__ https://github.com/LonamiWebs/Telethon/issues/305
__ https://github.com/LonamiWebs/Telethon/issues/409
__ https://github.com/LonamiWebs/Telethon/issues/447

View File

@ -5,11 +5,13 @@ Users
.. note::
These examples assume you have read :ref:`accessing-the-full-api`.
These examples assume you have read :ref:`full-api`.
.. contents::
Retrieving full information
***************************
===========================
If you need to retrieve the bio, biography or about information for a user
you should use :tl:`GetFullUser`:
@ -19,18 +21,18 @@ you should use :tl:`GetFullUser`:
from telethon.tl.functions.users import GetFullUserRequest
full = client(GetFullUserRequest(user))
full = await client(GetFullUserRequest(user))
# or even
full = client(GetFullUserRequest('username'))
full = await client(GetFullUserRequest('username'))
bio = full.about
bio = full.full_user.about
See :tl:`UserFull` to know what other fields you can access.
Updating your name and/or bio
*****************************
=============================
The first name, last name and bio (about) can all be changed with the same
request. Omitted fields won't change after invoking :tl:`UpdateProfile`:
@ -39,13 +41,13 @@ request. Omitted fields won't change after invoking :tl:`UpdateProfile`:
from telethon.tl.functions.account import UpdateProfileRequest
client(UpdateProfileRequest(
await client(UpdateProfileRequest(
about='This is a test from Telethon'
))
Updating your username
**********************
======================
You need to use :tl:`account.UpdateUsername`:
@ -53,11 +55,11 @@ You need to use :tl:`account.UpdateUsername`:
from telethon.tl.functions.account import UpdateUsernameRequest
client(UpdateUsernameRequest('new_username'))
await client(UpdateUsernameRequest('new_username'))
Updating your profile photo
***************************
===========================
The easiest way is to upload a new file and use that as the profile photo
through :tl:`UploadProfilePhoto`:
@ -67,6 +69,6 @@ through :tl:`UploadProfilePhoto`:
from telethon.tl.functions.photos import UploadProfilePhotoRequest
client(UploadProfilePhotoRequest(
client.upload_file('/path/to/some/file')
)))
await client(UploadProfilePhotoRequest(
await client.upload_file('/path/to/some/file')
))

View File

@ -0,0 +1,17 @@
=================
A Word of Warning
=================
Full API is **not** how you are intended to use the library. You **should**
always prefer the :ref:`client-ref`. However, not everything is implemented
as a friendly method, so full API is your last resort.
If you select a method in :ref:`client-ref`, you will most likely find an
example for that method. This is how you are intended to use the library.
Full API **will** break between different minor versions of the library,
since Telegram changes very often. The friendly methods will be kept
compatible between major versions.
If you need to see real-world examples, please refer to the
`wiki page of projects using Telethon <https://github.com/LonamiWebs/Telethon/wiki/Projects-using-Telethon>`__.

View File

@ -0,0 +1,13 @@
=====================
Working with messages
=====================
.. note::
These examples assume you have read :ref:`full-api`.
This section has been `moved to the wiki`_, where it can be easily edited as new
features arrive and the API changes. Please refer to the linked page to learn how
to send spoilers, custom emoji, stickers, react to messages, and more things.
.. _moved to the wiki: https://github.com/LonamiWebs/Telethon/wiki/Sending-more-than-just-messages

View File

@ -1,231 +0,0 @@
.. _accessing-the-full-api:
======================
Accessing the Full API
======================
.. important::
While you have access to this, you should always use the friendly
methods listed on :ref:`telethon-client` unless you have a better
reason not to, like a method not existing or you wanting more control.
The :ref:`TelegramClient <telethon-client>` doesn't offer a method for
every single request the Telegram API supports. However, it's very simple to
*call* or *invoke* any request. Whenever you need something, don't forget to
`check the documentation`__ and look for the `method you need`__. There you
can go through a sorted list of everything you can do.
.. note::
The reason to keep both https://lonamiwebs.github.io/Telethon and this
documentation alive is that the former allows instant search results
as you type, and a "Copy import" button. If you like namespaces, you
can also do ``from telethon.tl import types, functions``. Both work.
.. important::
All the examples in this documentation assume that you have
``from telethon import sync`` or ``import telethon.sync``
for the sake of simplicity and that you understand what
it does (see :ref:`asyncio-magic` for more). Simply add
either line at the beginning of your project and it will work.
You should also refer to the documentation to see what the objects
(constructors) Telegram returns look like. Every constructor inherits
from a common type, and that's the reason for this distinction.
Say `client.send_message
<telethon.client.messages.MessageMethods.send_message>` didn't exist,
we could use the `search`__ to look for "message". There we would find
:tl:`SendMessageRequest`, which we can work with.
Every request is a Python class, and has the parameters needed for you
to invoke it. You can also call ``help(request)`` for information on
what input parameters it takes. Remember to "Copy import to the
clipboard", or your script won't be aware of this class! Now we have:
.. code-block:: python
from telethon.tl.functions.messages import SendMessageRequest
If you're going to use a lot of these, you may do:
.. code-block:: python
from telethon.tl import types, functions
# We now have access to 'functions.messages.SendMessageRequest'
We see that this request must take at least two parameters, a ``peer``
of type :tl:`InputPeer`, and a ``message`` which is just a Python
``str``\ ing.
How can we retrieve this :tl:`InputPeer`? We have two options. We manually
construct one, for instance:
.. code-block:: python
from telethon.tl.types import InputPeerUser
peer = InputPeerUser(user_id, user_hash)
Or we call `client.get_input_entity
<telethon.client.users.UserMethods.get_input_entity>`:
.. code-block:: python
import telethon.sync
peer = client.get_input_entity('someone')
When you're going to invoke an API method, most require you to pass an
:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using
`client.get_input_entity <telethon.client.users.UserMethods.get_input_entity>`
is more straightforward (and often immediate, if you've seen the user before,
know their ID, etc.). If you also **need** to have information about the whole
user, use `client.get_entity <telethon.client.users.UserMethods.get_entity>`
instead:
.. code-block:: python
entity = client.get_entity('someone')
In the later case, when you use the entity, the library will cast it to
its "input" version for you. If you already have the complete user and
want to cache its input version so the library doesn't have to do this
every time its used, simply call `telethon.utils.get_input_peer`:
.. code-block:: python
from telethon import utils
peer = utils.get_input_peer(entity)
.. note::
Since ``v0.16.2`` this is further simplified. The ``Request`` itself
will call `client.get_input_entity <
telethon.client.users.UserMethods.get_input_entity>` for you when required,
but it's good to remember what's happening.
After this small parenthesis about `client.get_entity
<telethon.client.users.UserMethods.get_entity>` versus
`client.get_input_entity <telethon.client.users.UserMethods.get_input_entity>`,
we have everything we need. To invoke our
request we do:
.. code-block:: python
result = client(SendMessageRequest(peer, 'Hello there!'))
# __call__ is an alias for client.invoke(request). Both will work
Message sent! Of course, this is only an example. There are over 250
methods available as of layer 80, and you can use every single of them
as you wish. Remember to use the right types! To sum up:
.. code-block:: python
result = client(SendMessageRequest(
client.get_input_entity('username'), 'Hello there!'
))
This can further be simplified to:
.. code-block:: python
result = client(SendMessageRequest('username', 'Hello there!'))
# Or even
result = client(SendMessageRequest(PeerChannel(id), 'Hello there!'))
.. note::
Note that some requests have a "hash" parameter. This is **not**
your ``api_hash``! It likely isn't your self-user ``.access_hash`` either.
It's a special hash used by Telegram to only send a difference of new data
that you don't already have with that request, so you can leave it to 0,
and it should work (which means no hash is known yet).
For those requests having a "limit" parameter, you can often set it to
zero to signify "return default amount". This won't work for all of them
though, for instance, in "messages.search" it will actually return 0 items.
Requests in Parallel
********************
The library will automatically merge outgoing requests into a single
*container*. Telegram's API supports sending multiple requests in a
single container, which is faster because it has less overhead and
the server can run them without waiting for others. You can also
force using a container manually:
.. code-block:: python
async def main():
# Letting the library do it behind the scenes
await asyncio.wait([
client.send_message('me', 'Hello'),
client.send_message('me', ','),
client.send_message('me', 'World'),
client.send_message('me', '.')
])
# Manually invoking many requests at once
await client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ', '),
SendMessageRequest('me', 'World'),
SendMessageRequest('me', '.')
])
Note that you cannot guarantee the order in which they are run.
Try running the above code more than one time. You will see the
order in which the messages arrive is different.
If you use the raw API (the first option), you can use ``ordered``
to tell the server that it should run the requests sequentially.
This will still be faster than going one by one, since the server
knows all requests directly:
.. code-block:: python
client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ', '),
SendMessageRequest('me', 'World'),
SendMessageRequest('me', '.')
], ordered=True)
If any of the requests fails with a Telegram error (not connection
errors or any other unexpected events), the library will raise
`telethon.errors.common.MultiError`. You can ``except`` this
and still access the successful results:
.. code-block:: python
from telethon.errors import MultiError
try:
client([
SendMessageRequest('me', 'Hello'),
SendMessageRequest('me', ''),
SendMessageRequest('me', 'World')
], ordered=True)
except MultiError as e:
# The first and third requests worked.
first = e.results[0]
third = e.results[2]
# The second request failed.
second = e.exceptions[1]
__ https://lonamiwebs.github.io/Telethon
__ https://lonamiwebs.github.io/Telethon/methods/index.html
__ https://lonamiwebs.github.io/Telethon/?q=message&redirect=no

View File

@ -1,338 +0,0 @@
.. _mastering-telethon:
==================
Mastering Telethon
==================
You've come far! In this section you will learn best practices, as well
as how to fix some silly (yet common) errors you may have found. Let's
start with a simple one.
Asyncio madness
***************
We promise ``asyncio`` is worth learning. Take your time to learn it.
It's a powerful tool that enables you to use this powerful library.
You need to be comfortable with it if you want to master Telethon.
.. code-block:: text
AttributeError: 'coroutine' object has no attribute 'id'
You probably had a previous version, upgraded, and expected everything
to work. Remember, just add this line:
.. code-block:: python
import telethon.sync
If you're inside an event handler you need to ``await`` **everything** that
*makes a network request*. Getting users, sending messages, and nearly
everything in the library needs access to the network, so they need to
be awaited:
.. code-block:: python
@client.on(events.NewMessage)
async def handler(event):
print((await event.get_sender()).username)
You may want to read https://lonamiwebs.github.io/blog/asyncio/ to help
you understand ``asyncio`` better. I'm open for `feedback
<https://t.me/LonamiWebs>`_ regarding that blog post
Entities
********
A lot of methods and requests require *entities* to work. For example,
you send a message to an *entity*, get the username of an *entity*, and
so on. There is an entire section on this at :ref:`entities` due to their
importance.
There are a lot of things that work as entities: usernames, phone numbers,
chat links, invite links, IDs, and the types themselves. That is, you can
use any of those when you see an "entity" is needed.
You should use, **from better to worse**:
1. Input entities. For example, `event.input_chat
<telethon.tl.custom.chatgetter.ChatGetter.input_chat>`,
`message.input_sender
<telethon.tl.custom.sendergetter.SenderGetter.input_sender>`,
or caching an entity you will use a lot with
``entity = await client.get_input_entity(...)``.
2. Entities. For example, if you had to get someone's
username, you can just use ``user`` or ``channel``.
It will work. Only use this option if you already have the entity!
3. IDs. This will always look the entity up from the
cache (the ``*.session`` file caches seen entities).
4. Usernames, phone numbers and links. The cache will be
used too (unless you force a `client.get_entity()
<telethon.client.users.UserMethods.get_entity>`),
but may make a request if the username, phone or link
has not been found yet.
In short, unlike in most bot API libraries where you use the ID, you
**should not** use the ID *if* you have the input entity. This is OK:
.. code-block:: python
async def handler(event):
await client.send_message(event.sender_id, 'Hi')
However, **this is better**:
.. code-block:: python
async def handler(event):
await client.send_message(event.input_sender, 'Hi')
Note that this also works for `message <telethon.tl.custom.message.Message>`
instead of ``event``. Telegram may not send the sender information, so if you
want to be 99% confident that the above will work you should do this:
.. code-block:: python
async def handler(event):
await client.send_message(await event.get_input_sender(), 'Hi')
Methods are able to make network requests to get information that
could be missing. Properties will never make a network request.
Of course, it is convenient to IDs or usernames for most purposes. It will
be fast enough and caching with `client.get_input_entity(...)
<telethon.client.users.UserMethods.get_input_entity>` will
be a micro-optimization. However it's worth knowing, and it
will also let you know if the entity cannot be found beforehand.
.. note::
Sometimes Telegram doesn't send the access hash inside entities,
so using `chat <telethon.tl.custom.chatgetter.ChatGetter.chat>`
or `sender <telethon.tl.custom.sendergetter.SenderGetter.sender>`
may not work, but `input_chat
<telethon.tl.custom.chatgetter.ChatGetter.input_chat>`
and `input_sender
<telethon.tl.custom.sendergetter.SenderGetter.input_sender>`
while making requests definitely will since that's what they exist
for. If Telegram did not send information about the access hash,
you will get something like "Invalid channel object" or
"Invalid user object".
Debugging
*********
**Please enable logging**:
.. code-block:: python
import logging
logging.basicConfig(level=logging.WARNING)
Change it for ``logging.DEBUG`` if you are asked for logs. It will save you
a lot of headaches and time when you work with events. This is for errors.
Debugging is *really* important. Telegram's API is really big and there
is a lot of things that you should know. Such as, what attributes or fields
does a result have? Well, the easiest thing to do is printing it:
.. code-block:: python
user = client.get_entity('Lonami')
print(user)
That will show a huge line similar to the following:
.. code-block:: python
User(id=10885151, is_self=False, contact=False, mutual_contact=False, deleted=False, bot=False, bot_chat_history=False, bot_nochats=False, verified=False, restricted=False, min=False, bot_inline_geo=False, access_hash=123456789012345678, first_name='Lonami', last_name=None, username='Lonami', phone=None, photo=UserProfilePhoto(photo_id=123456789012345678, photo_small=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678), photo_big=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678)), status=UserStatusOffline(was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc)), bot_info_version=None, restriction_reason=None, bot_inline_placeholder=None, lang_code=None)
That's a lot of text. But as you can see, all the properties are there.
So if you want the username you **don't use regex** or anything like
splitting ``str(user)`` to get what you want. You just access the
attribute you need:
.. code-block:: python
username = user.username
Can we get better than the shown string, though? Yes!
.. code-block:: python
print(user.stringify())
Will show a much better:
.. code-block:: python
User(
id=10885151,
is_self=False,
contact=False,
mutual_contact=False,
deleted=False,
bot=False,
bot_chat_history=False,
bot_nochats=False,
verified=False,
restricted=False,
min=False,
bot_inline_geo=False,
access_hash=123456789012345678,
first_name='Lonami',
last_name=None,
username='Lonami',
phone=None,
photo=UserProfilePhoto(
photo_id=123456789012345678,
photo_small=FileLocation(
dc_id=4,
volume_id=123456789,
local_id=123456789,
secret=-123456789012345678
),
photo_big=FileLocation(
dc_id=4,
volume_id=123456789,
local_id=123456789,
secret=123456789012345678
)
),
status=UserStatusOffline(
was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc)
),
bot_info_version=None,
restriction_reason=None,
bot_inline_placeholder=None,
lang_code=None
)
Now it's easy to see how we could get, for example,
the ``was_online`` time. It's inside ``status``:
.. code-block:: python
online_at = user.status.was_online
You don't need to print everything to see what all the possible values
can be. You can just search in http://lonamiwebs.github.io/Telethon/.
Remember that you can use Python's `isinstance
<https://docs.python.org/3/library/functions.html#isinstance>`_
to check the type of something. For example:
.. code-block:: python
from telethon import types
if isinstance(user.status, types.UserStatusOffline):
print(user.status.was_online)
Avoiding Limits
***************
Don't spam. You won't get ``FloodWaitError`` or your account banned or
deleted if you use the library *for legit use cases*. Make cool tools.
Don't spam! Nobody knows the exact limits for all requests since they
depend on a lot of factors, so don't bother asking.
Still, if you do have a legit use case and still get those errors, the
library will automatically sleep when they are smaller than 60 seconds
by default. You can set different "auto-sleep" thresholds:
.. code-block:: python
client.flood_sleep_threshold = 0 # Don't auto-sleep
client.flood_sleep_threshold = 24 * 60 * 60 # Sleep always
You can also except it and act as you prefer:
.. code-block:: python
from telethon.errors import FloodWaitError
try:
...
except FloodWaitError as e:
print('Flood waited for', e.seconds)
quit(1)
VoIP numbers are very limited, and some countries are more limited too.
Chat or User From Messages
**************************
Although it's explicitly noted in the documentation that messages
*subclass* `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`
and `SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>`,
some people still don't get inheritance.
When the documentation says "Bases: `telethon.tl.custom.chatgetter.ChatGetter`"
it means that the class you're looking at, *also* can act as the class it
bases. In this case, `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`
knows how to get the *chat* where a thing belongs to.
So, a `Message <telethon.tl.custom.message.Message>` is a
`ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`.
That means you can do this:
.. code-block:: python
message.is_private
message.chat_id
message.get_chat()
# ...etc
`SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>` is similar:
.. code-block:: python
message.user_id
message.get_input_user()
message.user
# ...etc
Quite a few things implement them, so it makes sense to reuse the code.
For example, all events (except raw updates) implement `ChatGetter
<telethon.tl.custom.chatgetter.ChatGetter>` since all events occur
in some chat.
Session Files
*************
They are an important part for the library to be efficient, such as caching
and handling your authorization key (or you would have to login every time!).
However, some people have a lot of trouble with SQLite, especially in Windows:
.. code-block:: text
...some lines of traceback
'insert or replace into entities values (?,?,?,?,?)', rows)
sqlite3.OperationalError: database is locked
This error occurs when **two or more clients use the same session**,
that is, when you write the same session name to be used in the client:
* You have two scripts running (interactive sessions count too).
* You have two clients in the same script running at the same time.
The solution is, if you need two clients, use two sessions. If the
problem persists and you're on Linux, you can use ``fuser my.session``
to find out the process locking the file. As a last resort, you can
reboot your system.
If you really dislike SQLite, use a different session storage. There
is an entire section covering that at :ref:`sessions`.
Final Words
***********
Now you are aware of some common errors and use cases, this should help
you master your Telethon skills to get the most out of the library. Have
fun developing awesome things!

View File

@ -1,65 +0,0 @@
.. _update-modes:
============
Update Modes
============
With ``asyncio``, the library has several tasks running in the background.
One task is used for sending requests, another task is used to receive them,
and a third one is used to handle updates.
To handle updates, you must keep your script running. You can do this in
several ways. For instance, if you are *not* running ``asyncio``'s event
loop, you should use `client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>`:
.. code-block:: python
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
...
client.run_until_disconnected()
Behind the scenes, this method is ``await``'ing on the `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>` property,
so the code above and the following are equivalent:
.. code-block:: python
import asyncio
from telethon import TelegramClient
client = TelegramClient(...)
async def main():
await client.disconnected
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
You could also run `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>`
until it completed.
But if you don't want to ``await``, then you should know what you want
to be doing instead! What matters is that you shouldn't let your script
die. If you don't care about updates, you don't need any of this.
Notice that unlike `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>`,
`client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>` will
handle ``KeyboardInterrupt`` with you. This method is special and can
also be ran while the loop is running, so you can do this:
.. code-block:: python
async def main():
await client.run_until_disconnected()
loop.run_until_complete(main())

View File

@ -1,322 +0,0 @@
.. _asyncio-magic:
==================
Magic with asyncio
==================
.. important::
TL; DR; If you've upgraded to Telethon 1.0 from a previous version
**and you're not using events or updates**, add this line:
.. code-block:: python
import telethon.sync
At the beginning of your main script and you will be good. If you **do**
use updates or events, keep reading, or ``pip install telethon-sync``, a
branch that mimics the ``asyncio`` code with threads and should work
under Python 3.4.
You might also want to check the :ref:`changelog`.
The sync module
***************
It's time to tell you the truth. The library has been doing magic behind
the scenes. We're sorry to tell you this, but at least it wasn't dark magic!
You may have noticed one of these lines across the documentation:
.. code-block:: python
from telethon import sync
# or
import telethon.sync
Either of these lines will import the *magic* ``sync`` module. When you
import this module, you can suddenly use all the methods defined in the
:ref:`TelegramClient <telethon-client>` like so:
.. code-block:: python
client.send_message('me', 'Hello!')
for dialog in client.iter_dialogs():
print(dialog.title)
What happened behind the scenes is that all those methods, called *coroutines*,
were rewritten to be normal methods that will block (with some exceptions).
This means you can use the library without worrying about ``asyncio`` and
event loops.
However, this only works until you run the event loop yourself explicitly:
.. code-block:: python
import asyncio
async def coro():
client.send_message('me', 'Hello!') # <- no longer works!
loop = asyncio.get_event_loop()
loop.run_until_complete(coro())
What things will work and when?
*******************************
You can use all the methods in the :ref:`TelegramClient <telethon-client>`
in a synchronous, blocking way without trouble, as long as you're not running
the loop as we saw above (the ``loop.run_until_complete(...)`` line runs "the
loop"). If you're running the loop, then *you* are the one responsible to
``await`` everything. So to fix the code above:
.. code-block:: python
import asyncio
async def coro():
await client.send_message('me', 'Hello!')
# ^ notice this new await
loop = asyncio.get_event_loop()
loop.run_until_complete(coro())
The library can only run the loop until the method completes if the loop
isn't already running, which is why the magic can't work if you run the
loop yourself.
**When you work with updates or events**, the loop needs to be
running one way or another (using `client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` runs the loop),
so your event handlers must be ``async def``.
.. important::
Turning your event handlers into ``async def`` is the biggest change
between Telethon pre-1.0 and 1.0, but updating will likely cause a
noticeable speed-up in your programs. Keep reading!
So in short, you can use **all** methods in the client with ``await`` or
without it if the loop isn't running:
.. code-block:: python
client.send_message('me', 'Hello!') # works
async def main():
await client.send_message('me', 'Hello!') # also works
loop.run_until_complete(main())
When you work with updates, you should stick using the ``async def main``
way, since your event handlers will be ``async def`` too.
.. note::
There are two exceptions. Both `client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` and
`client.start() <telethon.client.auth.AuthMethods.start>` work in
and outside of ``async def`` for convenience without importing the
magic module. The rest of methods remain ``async`` unless you import it.
You can skip the rest if you already know how ``asyncio`` works and you
already understand what the magic does and how it works. Just remember
to ``await`` all your methods if you're inside an ``async def`` or are
using updates and you will be good.
Why asyncio?
************
Python's `asyncio <https://docs.python.org/3/library/asyncio.html>`_ is the
standard way to run asynchronous code from within Python. Since Python 3.5,
using ``async def`` and ``await`` became possible, and Python 3.6 further
improves what you can do with asynchronous code, although it's not the only
way (other projects like `Trio <https://github.com/python-trio>`_ also exist).
Telegram is a service where all API calls are executed in an asynchronous
way. You send your request, and eventually, Telegram will process it and
respond to it. It feels natural to make a library that also behaves this
way: you send a request, and you can ``await`` for its result.
Now that we know that Telegram's API follows an asynchronous model, you
should understand the benefits of developing a library that does the same,
it greatly simplifies the internal code and eases working with the API.
Using ``asyncio`` keeps a cleaner library that will be easier to understand,
develop, and that will be faster than using threads, which are harder to get
right and can cause issues. It also enables to use the powerful ``asyncio``
system such as futures, timeouts, cancellation, etc. in a natural way.
If you're still not convinced or you're just not ready for using ``asyncio``,
the library offers a synchronous interface without the need for all the
``async`` and ``await`` you would otherwise see. `Follow this link
<https://github.com/LonamiWebs/Telethon/tree/sync>`_ to find out more.
How do I get started?
*********************
To get started with ``asyncio``, all you need is to setup your main
``async def`` like so:
.. code-block:: python
import asyncio
async def main():
pass # Your code goes here
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
You don't need to ``import telethon.sync`` if you're going to work this
way. This is the best way to work in real programs since the loop won't
be starting and ending all the time, but is a bit more annoying to setup.
Inside ``async def main()``, you can use the ``await`` keyword. Most
methods in the :ref:`TelegramClient <telethon-client>` are ``async def``.
You must ``await`` all ``async def``, also known as a *coroutines*:
.. code-block:: python
async def main():
client = TelegramClient(...)
# client.start() is a coroutine (async def), it needs an await
await client.start()
# Sending a message also interacts with the API, and needs an await
await client.send_message('me', 'Hello myself!')
If you don't know anything else about ``asyncio``, this will be enough
to get you started. Once you're ready to learn more about it, you will
be able to use that power and everything you've learnt with Telethon.
Just remember that if you use ``await``, you need to be inside of an
``async def``.
Another way to use ``async def`` is to use ``loop.run_until_complete(f())``,
but the loop must not be running before.
If you want to handle updates (and don't let the script die), you must
`await client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>`
which is a property that you can wait on until you call
`await client.disconnect()
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnect>`:
.. code-block:: python
client = TelegramClient(...)
@client.on(events.NewMessage)
async def handler(event):
print(event)
async def main():
await client.start()
await client.run_until_disconnected()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
`client.run_until_disconnected()
<telethon.client.updates.UpdateMethods.run_until_disconnected>` and
`client.start()
<telethon.client.auth.AuthMethods.start>` are special-cased and work
inside or outside ``async def`` for convenience, even without importing
the ``sync`` module, so you can also do this:
.. code-block:: python
client = TelegramClient(...)
@client.on(events.NewMessage)
async def handler(event):
print(event)
if __name__ == '__main__':
client.start()
client.run_until_disconnected()
Which methods should I use and when?
************************************
Something to note is that you must always get an event loop if you
want to be able to make any API calls. This is done as follows:
.. code-block:: python
import asyncio
loop = asyncio.get_event_loop()
The loop must be running, or things will never get sent.
Normally, you use ``run_until_complete``:
.. code-block:: python
async def coroutine():
await asyncio.sleep(1)
loop.run_until_complete(coroutine())
Note that ``asyncio.sleep`` is in itself a coroutine, so this will
work too:
.. code-block:: python
loop.run_until_complete(asyncio.sleep(1))
Generally, you make an ``async def main()`` if you need to ``await``
a lot of things, instead of typing ``run_until_complete`` all the time:
.. code-block:: python
async def main():
message = await client.send_message('me', 'Hi')
await asyncio.sleep(1)
await message.delete()
loop.run_until_complete(main())
# vs
message = loop.run_until_complete(client.send_message('me', 'Hi'))
loop.run_until_complete(asyncio.sleep(1))
loop.run_until_complete(message.delete())
You can see that the first version has more lines, but you had to type
a lot less. You can also rename the run method to something shorter:
.. code-block:: python
# Note no parenthesis (), we're not running it, just copying the method
rc = loop.run_until_complete
message = rc(client.send_message('me', 'Hi'))
rc(asyncio.sleep(1))
rc(message.delete())
The documentation generally runs the loop until complete behind the
scenes if you've imported the magic ``sync`` module, but if you haven't,
you need to run the loop yourself. We recommend that you use the
``async def main()`` method to do all your work with ``await``.
It's the easiest and most performant thing to do.
More resources to learn asyncio
*******************************
If you would like to learn a bit more about why ``asyncio`` is something
you should learn, `check out my blog post
<https://lonamiwebs.github.io/blog/asyncio/>`_ that goes into more detail.

View File

@ -1,263 +0,0 @@
.. _creating-a-client:
=================
Creating a Client
=================
Before working with Telegram's API, you need to get your own API ID and hash:
1. Follow `this link <https://my.telegram.org/>`_ and login with your
phone number.
2. Click under API Development tools.
3. A *Create new application* window will appear. Fill in your application
details. There is no need to enter any *URL*, and only the first two
fields (*App title* and *Short name*) can currently be changed later.
4. Click on *Create application* at the end. Remember that your
**API hash is secret** and Telegram won't let you revoke it.
Don't post it anywhere!
Once that's ready, the next step is to create a ``TelegramClient``.
This class will be your main interface with Telegram's API, and creating
one is very simple:
.. code-block:: python
from telethon import TelegramClient, sync
# Use your own values here
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('some_name', api_id, api_hash)
Note that ``'some_name'`` will be used to save your session (persistent
information such as access key and others) as ``'some_name.session'`` in
your disk. This is by default a database file using Python's ``sqlite3``.
.. note::
It's important that the library always accesses the same session file so
that you don't need to re-send the code over and over again. By default it
creates the file in your working directory, but absolute paths work too.
Once you have a client ready, simply `.start()
<telethon.client.auth.AuthMethods.start>` it:
.. code-block:: python
client.start()
This line connects to Telegram, checks whether the current user is
authorized or not, and if it's not, it begins the login or sign up process.
When you're done with your code, you should always disconnect:
.. code-block:: python
client = TelegramClient(...)
try:
client.start()
... # your code here
finally:
client.disconnect()
You can also use a ``with`` block to achieve the same effect:
.. code-block:: python
client = TelegramClient(...)
with client:
... # your code here
# or
with TelegramClient(...) as client:
... # your code here
Wrapping it all together:
.. code-block:: python
from telethon import TelegramClient, sync
with TelegramClient('session_name', api_id, api_hash) as client:
... # your code
Just two setup lines.
.. warning::
Please note that if you fail to login around 5 times (or change the first
parameter of the :ref:`TelegramClient <telethon-client>`, which is the session
name) you will receive a ``FloodWaitError`` of around 22 hours, so be
careful not to mess this up! This shouldn't happen if you're doing things
as explained, though.
.. note::
If you want to use a **proxy**, you have to `install PySocks`__
(via pip or manual) and then set the appropriated parameters:
.. code-block:: python
import socks
client = TelegramClient('session_id',
api_id=12345, api_hash='0123456789abcdef0123456789abcdef',
proxy=(socks.SOCKS5, 'localhost', 4444)
)
The ``proxy=`` argument should be a tuple, a list or a dict,
consisting of parameters described `here`__.
Manually Signing In
*******************
.. note::
Skip this unless you need more control when connecting.
If you need more control, you can replicate what `client.start()
<telethon.client.auth.AuthMethods.start>` is doing behind the scenes
for your convenience. The first step is to connect to the servers:
.. code-block:: python
client.connect()
You may or may not be authorized yet. You must be authorized
before you're able to send any request:
.. code-block:: python
client.is_user_authorized() # Returns True if you can send requests
If you're not authorized, you need to `.sign_in
<telethon.client.auth.AuthMethods.sign_in>`:
.. code-block:: python
phone_number = '+34600000000'
client.send_code_request(phone_number)
myself = client.sign_in(phone_number, input('Enter code: '))
# If .sign_in raises PhoneNumberUnoccupiedError, use .sign_up instead
# If .sign_in raises SessionPasswordNeeded error, call .sign_in(password=...)
# You can import both exceptions from telethon.errors.
.. note::
If you send the code that Telegram sent you over the app through the
app itself, it will expire immediately. You can still send the code
through the app by "obfuscating" it (maybe add a magic constant, like
``12345``, and then subtract it to get the real code back) or any other
technique.
``myself`` is your Telegram user. You can view all the information about
yourself by doing ``print(myself.stringify())``. You're now ready to use
the client as you wish! Remember that any object returned by the API has
mentioned ``.stringify()`` method, and printing these might prove useful.
As a full example:
.. code-block:: python
from telethon import TelegramClient, sync
client = TelegramClient('session_name', api_id, api_hash)
client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
Remember that this is the manual process and it's so much easier
to use the code snippets shown at the beginning of the page.
The code shown is just what `.start()
<telethon.client.auth.AuthMethods.start>` will be doing behind the scenes
(with a few extra checks), so that you know how to sign in case you want
to avoid using ``input()`` (the default) for whatever reason. If no phone
or bot token is provided, you will be asked one through ``input()``. The
method also accepts a ``phone=`` and ``bot_token`` parameters.
You can use either, as both will work. Determining which
is just a matter of taste, and how much control you need.
Remember that you can get yourself at any time with `client.get_me()
<telethon.client.users.UserMethods.get_me>`.
Two Factor Authorization (2FA)
------------------------------
If you have Two Factor Authorization (from now on, 2FA) enabled on your
account, calling `.sign_in()
<telethon.client.auth.AuthMethods.sign_in>` will raise a
``SessionPasswordNeededError``. When this happens, just use the method
again with a ``password=``:
.. code-block:: python
import getpass
from telethon.errors import SessionPasswordNeededError
client.sign_in(phone)
try:
client.sign_in(code=input('Enter code: '))
except SessionPasswordNeededError:
client.sign_in(password=getpass.getpass())
The mentioned `.start()
<telethon.client.auth.AuthMethods.start>` method will handle this for you as
well, but you must set the ``password=`` parameter beforehand (it won't be
asked).
If you don't have 2FA enabled, but you would like to do so through the
library, use `client.edit_2fa()
<telethon.client.auth.AuthMethods.edit_2fa>`.
Be sure to know what you're doing when using this function and
you won't run into any problems. Take note that if you want to
set only the email/hint and leave the current password unchanged,
you need to "redo" the 2fa.
See the examples below:
.. code-block:: python
from telethon.errors import EmailUnconfirmedError
# Sets 2FA password for first time:
client.edit_2fa(new_password='supersecurepassword')
# Changes password:
client.edit_2fa(current_password='supersecurepassword',
new_password='changedmymind')
# Clears current password (i.e. removes 2FA):
client.edit_2fa(current_password='changedmymind', new_password=None)
# Sets new password with recovery email:
try:
client.edit_2fa(new_password='memes and dreams',
email='JohnSmith@example.com')
# Raises error (you need to check your email to complete 2FA setup.)
except EmailUnconfirmedError:
# You can put email checking code here if desired.
pass
# Also take note that unless you remove 2FA or explicitly
# give email parameter again it will keep the last used setting
# Set hint after already setting password:
client.edit_2fa(current_password='memes and dreams',
new_password='memes and dreams',
hint='It keeps you alive')
__ https://github.com/Anorov/PySocks#installation
__ https://github.com/Anorov/PySocks#usage-1

View File

@ -1,179 +0,0 @@
.. _entities:
=========================
Users, Chats and Channels
=========================
Introduction
************
The library widely uses the concept of "entities". An entity will refer
to any :tl:`User`, :tl:`Chat` or :tl:`Channel` object that the API may return
in response to certain methods, such as :tl:`GetUsersRequest`.
.. note::
When something "entity-like" is required, it means that you need to
provide something that can be turned into an entity. These things include,
but are not limited to, usernames, exact titles, IDs, :tl:`Peer` objects,
or even entire :tl:`User`, :tl:`Chat` and :tl:`Channel` objects and even
phone numbers from people you have in your contacts.
To "encounter" an ID, you would have to "find it" like you would in the
normal app. If the peer is in your dialogs, you would need to
`client.get_dialogs() <telethon.client.dialogs.DialogMethods.get_dialogs>`.
If the peer is someone in a group, you would similarly
`client.get_participants(group) <telethon.client.chats.ChatMethods.get_participants>`.
Once you have encountered an ID, the library will (by default) have saved
their ``access_hash`` for you, which is needed to invoke most methods.
This is why sometimes you might encounter this error when working with
the library. You should ``except ValueError`` and run code that you know
should work to find the entity.
Getting entities
****************
Through the use of the :ref:`sessions`, the library will automatically
remember the ID and hash pair, along with some extra information, so
you're able to just do this:
.. code-block:: python
# Dialogs are the "conversations you have open".
# This method returns a list of Dialog, which
# has the .entity attribute and other information.
dialogs = client.get_dialogs()
# All of these work and do the same.
lonami = client.get_entity('lonami')
lonami = client.get_entity('t.me/lonami')
lonami = client.get_entity('https://telegram.dog/lonami')
# Other kind of entities.
channel = client.get_entity('telegram.me/joinchat/AAAAAEkk2WdoDrB4-Q8-gg')
contact = client.get_entity('+34xxxxxxxxx')
friend = client.get_entity(friend_id)
# Getting entities through their ID (User, Chat or Channel)
entity = client.get_entity(some_id)
# You can be more explicit about the type for said ID by wrapping
# it inside a Peer instance. This is recommended but not necessary.
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
my_user = client.get_entity(PeerUser(some_id))
my_chat = client.get_entity(PeerChat(some_id))
my_channel = client.get_entity(PeerChannel(some_id))
.. note::
You **don't** need to get the entity before using it! Just let
the library do its job. Use the phone, username, ID or input
entity (preferred but not necessary), whatever you already have.
All methods in the :ref:`telegram-client` call `.get_input_entity()
<telethon.client.users.UserMethods.get_input_entity>` prior
to sending the requst to save you from the hassle of doing so manually.
That way, convenience calls such as `client.send_message('lonami', 'hi!')
<telethon.client.messages.MessageMethods.send_message>`
become possible.
Every entity the library encounters (in any response to any call) will by
default be cached in the ``.session`` file (an SQLite database), to avoid
performing unnecessary API calls. If the entity cannot be found, additonal
calls like :tl:`ResolveUsernameRequest` or :tl:`GetContactsRequest` may be
made to obtain the required information.
Entities vs. Input Entities
***************************
.. note::
Don't worry if you don't understand this section, just remember some
of the details listed here are important. When you're calling a method,
don't call `client.get_entity() <telethon.client.users.UserMethods.get_entity>`
beforehand, just use the username or phone, or the entity retrieved by
other means like `client.get_dialogs()
<telethon.client.dialogs.DialogMethods.get_dialogs>`.
On top of the normal types, the API also make use of what they call their
``Input*`` versions of objects. The input version of an entity (e.g.
:tl:`InputPeerUser`, :tl:`InputChat`, etc.) only contains the minimum
information that's required from Telegram to be able to identify
who you're referring to: a :tl:`Peer`'s **ID** and **hash**. They
are named like this because they are input parameters in the requests.
Entities' ID are the same for all user and bot accounts, however, the access
hash is **different for each account**, so trying to reuse the access hash
from one account in another will **not** work.
Sometimes, Telegram only needs to indicate the type of the entity along
with their ID. For this purpose, :tl:`Peer` versions of the entities also
exist, which just have the ID. You cannot get the hash out of them since
you should not be needing it. The library probably has cached it before.
Peers are enough to identify an entity, but they are not enough to make
a request with them use them. You need to know their hash before you can
"use them", and to know the hash you need to "encounter" them, let it
be in your dialogs, participants, message forwards, etc.
.. note::
You *can* use peers with the library. Behind the scenes, they are
replaced with the input variant. Peers "aren't enough" on their own
but the library will do some more work to use the right type.
As we just mentioned, API calls don't need to know the whole information
about the entities, only their ID and hash. For this reason, another method,
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
is available. This will always use the cache while possible, making zero API
calls most of the time. When a request is made, if you provided the full
entity, e.g. an :tl:`User`, the library will convert it to the required
:tl:`InputPeer` automatically for you.
**You should always favour**
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
**over**
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
for this reason! Calling the latter will always make an API call to get
the most recent information about said entity, but invoking requests don't
need this information, just the :tl:`InputPeer`. Only use
`client.get_entity() <telethon.client.users.UserMethods.get_entity>`
if you need to get actual information, like the username, name, title, etc.
of the entity.
To further simplify the workflow, since the version ``0.16.2`` of the
library, the raw requests you make to the API are also able to call
`client.get_input_entity() <telethon.client.users.UserMethods.get_input_entity>`
wherever needed, so you can even do things like:
.. code-block:: python
client(SendMessageRequest('username', 'hello'))
The library will call the ``.resolve()`` method of the request, which will
resolve ``'username'`` with the appropriated :tl:`InputPeer`. Don't worry if
you don't get this yet, but remember some of the details here are important.
Full entities
*************
In addition to :tl:`PeerUser`, :tl:`InputPeerUser`, :tl:`User` (and its
variants for chats and channels), there is also the concept of :tl:`UserFull`.
This full variant has additional information such as whether the user is
blocked, its notification settings, the bio or about of the user, etc.
There is also :tl:`messages.ChatFull` which is the equivalent of full entities
for chats and channels, with also the about section of the channel. Note that
the ``users`` field only contains bots for the channel (so that clients can
suggest commands to use).
You can get both of these by invoking :tl:`GetFullUser`, :tl:`GetFullChat`
and :tl:`GetFullChannel` respectively.

View File

@ -1,93 +0,0 @@
.. _getting-started:
===============
Getting Started
===============
Simple Installation
*******************
.. code-block:: sh
pip3 install telethon
**More details**: :ref:`installation`
Creating a client
*****************
.. code-block:: python
from telethon import TelegramClient, sync
# These example values won't work. You must get your own api_id and
# api_hash from https://my.telegram.org, under API Development.
api_id = 12345
api_hash = '0123456789abcdef0123456789abcdef'
client = TelegramClient('session_name', api_id, api_hash).start()
**More details**: :ref:`creating-a-client`
Basic Usage
***********
.. code-block:: python
# Getting information about yourself
me = client.get_me()
print(me.stringify())
# Sending a message (you can use 'me' or 'self' to message yourself)
client.send_message('username', 'Hello World from Telethon!')
# Sending a file
client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# Retrieving messages from a chat
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# Listing all the dialogs (conversations you have open)
for dialog in client.get_dialogs(limit=10):
print(dialog.name, dialog.draft.text)
# Downloading profile photos (default path is the working directory)
client.download_profile_photo('username')
# Once you have a message with .media (if message.media)
# you can download it using client.download_media(),
# or even using message.download_media():
messages = client.get_messages('username')
messages[0].download_media()
**More details**: :ref:`telegram-client`
See :ref:`telethon-client` for all available friendly methods.
Handling Updates
****************
.. code-block:: python
from telethon import events
@client.on(events.NewMessage(incoming=True, pattern='(?i)hi'))
async def handler(event):
await event.reply('Hello!')
client.run_until_disconnected()
**More details**: :ref:`working-with-updates`
----------
You can continue by clicking on the "More details" link below each
snippet of code or the "Next" button at the bottom of the page.

View File

@ -1,97 +0,0 @@
.. _installation:
============
Installation
============
Automatic Installation
**********************
To install Telethon, simply do:
.. code-block:: sh
pip3 install telethon
Needless to say, you must have Python 3 and PyPi installed in your system.
See https://python.org and https://pypi.python.org/pypi/pip for more.
If you already have the library installed, upgrade with:
.. code-block:: sh
pip3 install --upgrade telethon
You can also install the library directly from GitHub or a fork:
.. code-block:: sh
# pip3 install git+https://github.com/LonamiWebs/Telethon.git
or
$ git clone https://github.com/LonamiWebs/Telethon.git
$ cd Telethon/
# pip install -Ue .
If you don't have root access, simply pass the ``--user`` flag to the pip
command. If you want to install a specific branch, append ``@branch`` to
the end of the first install command.
By default the library will use a pure Python implementation for encryption,
which can be really slow when uploading or downloading files. If you don't
mind using a C extension, install `cryptg <https://github.com/Lonami/cryptg>`__
via ``pip`` or as an extra:
.. code-block:: sh
pip3 install telethon[cryptg]
Manual Installation
*******************
1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and
``rsa`` (`GitHub`__ | `PyPi`__) modules:
.. code-block:: sh
pip3 install pyaes rsa
2. Clone Telethon's GitHub repository:
.. code-block:: sh
git clone https://github.com/LonamiWebs/Telethon.git
3. Enter the cloned repository:
.. code-block:: sh
cd Telethon
4. Run the code generator:
.. code-block:: sh
python3 setup.py gen
5. Done!
To generate the `method documentation`__, ``python3 setup.py gen docs``.
Optional dependencies
*********************
If the `cryptg`__ is installed, you might notice a speed-up in the download
and upload speed, since these are the most cryptographic-heavy part of the
library and said module is a C extension. Otherwise, the ``pyaes`` fallback
will be used.
__ https://github.com/ricmoo/pyaes
__ https://pypi.python.org/pypi/pyaes
__ https://github.com/sybrenstuvel/python-rsa
__ https://pypi.python.org/pypi/rsa/3.4.2
__ https://lonamiwebs.github.io/Telethon
__ https://github.com/Lonami/cryptg

View File

@ -1,108 +0,0 @@
.. _telegram-client:
==============
TelegramClient
==============
Introduction
************
.. note::
Make sure to use the friendly methods described in :ref:`telethon-client`!
This section is just an introduction to using the client, but all the
available methods are in the :ref:`telethon-client` reference, including
detailed descriptions to what they do.
The :ref:`TelegramClient <telethon-client>` is the
central class of the library, the one you will be using most of the time. For
this reason, it's important to know what it offers.
Since we're working with Python, one must not forget that we can do
``help(client)`` or ``help(TelegramClient)`` at any time for a more
detailed description and a list of all the available methods. Calling
``help()`` from an interactive Python session will always list all the
methods for any object, even yours!
Interacting with the Telegram API is done through sending **requests**,
this is, any "method" listed on the API. There are a few methods (and
growing!) on the :ref:`TelegramClient <telethon-client>` class that abstract
you from the need of manually importing the requests you need.
For instance, retrieving your own user can be done in a single line
(assuming you have ``from telethon import sync`` or ``import telethon.sync``):
.. code-block:: python
myself = client.get_me()
Internally, this method has sent a request to Telegram, who replied with
the information about your own user, and then the desired information
was extracted from their response.
If you want to retrieve any other user, chat or channel (channels are a
special subset of chats), you want to retrieve their "entity". This is
how the library refers to either of these:
.. code-block:: python
# The method will infer that you've passed a username
# It also accepts phone numbers, and will get the user
# from your contact list.
lonami = client.get_entity('lonami')
The so called "entities" are another important whole concept on its own,
but for now you don't need to worry about it. Simply know that they are
a good way to get information about a user, chat or channel.
Many other common methods for quick scripts are also available:
.. code-block:: python
# Note that you can use 'me' or 'self' to message yourself
client.send_message('username', 'Hello World from Telethon!')
# .send_message's parse mode defaults to markdown, so you
# can use **bold**, __italics__, [links](https://example.com), `code`,
# and even [mentions](@username)/[mentions](tg://user?id=123456789)
client.send_message('username', '**Using** __markdown__ `too`!')
client.send_file('username', '/home/myself/Pictures/holidays.jpg')
# The utils package has some goodies, like .get_display_name()
from telethon import utils
for message in client.iter_messages('username', limit=10):
print(utils.get_display_name(message.sender), message.message)
# Dialogs are the conversations you have open
for dialog in client.get_dialogs(limit=10):
print(dialog.name, dialog.draft.text)
# Default path is the working directory
client.download_profile_photo('username')
# Call .disconnect() when you're done
client.disconnect()
Remember that you can call ``.stringify()`` to any object Telegram returns
to pretty print it. Calling ``str(result)`` does the same operation, but on
a single line.
Available methods
*****************
The :ref:`reference <telethon-package>` lists all the "handy" methods
available for you to use in the :ref:`TelegramClient <telethon-client>` class.
These are simply wrappers around the "raw" Telegram API, making it much more
manageable and easier to work with.
Please refer to :ref:`accessing-the-full-api` if these aren't enough,
and don't be afraid to read the source code of the InteractiveTelegramClient_
or even the TelegramClient_ itself to learn how it works.
See the mentioned :ref:`telethon-client` to find the available methods.
.. _InteractiveTelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/interactive_telegram_client.py
.. _TelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon/telegram_client.py

View File

@ -1,338 +0,0 @@
.. _working-with-updates:
====================
Working with Updates
====================
.. important::
Make sure you have read at least the first part of :ref:`asyncio-magic`
before working with updates. **This is a big change from Telethon pre-1.0
and 1.0, and your old handlers won't work with this version**.
To port your code to the new version, you should just prefix all your
event handlers with ``async`` and ``await`` everything that makes an
API call, such as replying, deleting messages, etc.
The library comes with the `telethon.events` module. *Events* are an abstraction
over what Telegram calls `updates`__, and are meant to ease simple and common
usage when dealing with them, since there are many updates. If you're looking
for the method reference, check :ref:`telethon-events-package`, otherwise,
let's dive in!
.. important::
The library logs by default no output, and any exception that occurs
inside your handlers will be "hidden" from you to prevent the thread
from terminating (so it can still deliver events). You should enable
logging when working with events, at least the error level, to see if
this is happening so you can debug the error.
**When using updates, please enable logging:**
.. code-block:: python
import logging
logging.basicConfig(level=logging.ERROR)
.. contents::
Getting Started
***************
.. code-block:: python
from telethon import TelegramClient, events
client = TelegramClient('name', api_id, api_hash)
@client.on(events.NewMessage)
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('hi!')
client.start()
client.run_until_disconnected()
Not much, but there might be some things unclear. What does this code do?
.. code-block:: python
from telethon import TelegramClient, events
client = TelegramClient('name', api_id, api_hash)
This is normal creation (of course, pass session name, API ID and hash).
Nothing we don't know already.
.. code-block:: python
@client.on(events.NewMessage)
This Python decorator will attach itself to the ``my_event_handler``
definition, and basically means that *on* a `NewMessage
<telethon.events.newmessage.NewMessage>` *event*,
the callback function you're about to define will be called:
.. code-block:: python
async def my_event_handler(event):
if 'hello' in event.raw_text:
await event.reply('hi!')
If a `NewMessage
<telethon.events.newmessage.NewMessage>` event occurs,
and ``'hello'`` is in the text of the message, we `.reply()
<telethon.tl.custom.message.Message.reply>` to the event
with a ``'hi!'`` message.
Do you notice anything different? Yes! Event handlers **must** be ``async``
for them to work, and **every method using the network** needs to have an
``await``, otherwise, Python's ``asyncio`` will tell you that you forgot
to do so, so you can easily add it.
.. code-block:: python
client.start()
client.run_until_disconnected()
Finally, this tells the client that we're done with our code. We run the
``asyncio`` loop until the client starts (this is done behind the scenes,
since the method is so common), and then we run it again until we are
disconnected. Of course, you can do other things instead of running
until disconnected. For this refer to :ref:`update-modes`.
More on events
**************
The `NewMessage <telethon.events.newmessage.NewMessage>` event has much
more than what was shown. You can access the `.sender
<telethon.tl.custom.message.Message.sender>` of the message
through that member, or even see if the message had `.media
<telethon.tl.custom.message.Message.media>`, a `.photo
<telethon.tl.custom.message.Message.photo>` or a `.document
<telethon.tl.custom.message.Message.document>` (which you
could download with for example `client.download_media(event.photo)
<telethon.client.downloads.DownloadMethods.download_media>`.
If you don't want to `.reply()
<telethon.tl.custom.message.Message.reply>` as a reply,
you can use the `.respond() <telethon.tl.custom.message.Message.respond>`
method instead. Of course, there are more events such as `ChatAction
<telethon.events.chataction.ChatAction>` or `UserUpdate
<telethon.events.userupdate.UserUpdate>`, and they're all
used in the same way. Simply add the `@client.on(events.XYZ)
<telethon.client.updates.UpdateMethods.on>` decorator on the top
of your handler and you're done! The event that will be passed always
is of type ``XYZ.Event`` (for instance, `NewMessage.Event
<telethon.events.newmessage.NewMessage.Event>`), except for the `Raw
<telethon.events.raw.Raw>` event which just passes the :tl:`Update` object.
Note that `.reply()
<telethon.tl.custom.message.Message.reply>` and `.respond()
<telethon.tl.custom.message.Message.respond>` are just wrappers around the
`client.send_message() <telethon.client.messages.MessageMethods.send_message>`
method which supports the ``file=`` parameter.
This means you can reply with a photo if you do `event.reply(file=photo)
<telethon.tl.custom.message.Message.reply>`.
You can put the same event on many handlers, and even different events on
the same handler. You can also have a handler work on only specific chats,
for example:
.. code-block:: python
import ast
import random
# Either a single item or a list of them will work for the chats.
# You can also use the IDs, Peers, or even User/Chat/Channel objects.
@client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic')))
async def normal_handler(event):
if 'roll' in event.raw_text:
await event.reply(str(random.randint(1, 6)))
# Similarly, you can use incoming=True for messages that you receive
@client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True,
pattern='eval (.+)'))
async def admin_handler(event):
expression = event.pattern_match.group(1)
await event.reply(str(ast.literal_eval(expression)))
You can pass one or more chats to the ``chats`` parameter (as a list or tuple),
and only events from there will be processed. You can also specify whether you
want to handle incoming or outgoing messages (those you receive or those you
send). In this example, people can say ``'roll'`` and you will reply with a
random number, while if you say ``'eval 4+4'``, you will reply with the
solution. Try it!
Properties vs. Methods
**********************
The event shown above acts just like a `custom.Message
<telethon.tl.custom.message.Message>`, which means you
can access all the properties it has, like ``.sender``.
**However** events are different to other methods in the client, like
`client.get_messages <telethon.client.messages.MessageMethods.get_messages>`.
Events *may not* send information about the sender or chat, which means it
can be ``None``, but all the methods defined in the client always have this
information so it doesn't need to be re-fetched. For this reason, you have
``get_`` methods, which will make a network call if necessary.
In short, you should do this:
.. code-block:: python
@client.on(events.NewMessage)
async def handler(event):
# event.input_chat may be None, use event.get_input_chat()
chat = await event.get_input_chat()
sender = await event.get_sender()
buttons = await event.get_buttons()
async def main():
async for message in client.iter_messages('me', 10):
# Methods from the client always have these properties ready
chat = message.input_chat
sender = message.sender
buttons = message.buttons
Notice, properties (`message.sender
<telethon.tl.custom.message.Message.sender>`) don't need an ``await``, but
methods (`message.get_sender
<telethon.tl.custom.message.Message.get_sender>`) **do** need an ``await``,
and you should use methods in events for these properties that may need network.
Events Without the client
*************************
The code of your application starts getting big, so you decide to
separate the handlers into different files. But how can you access
the client from these files? You don't need to! Just `events.register
<telethon.events.register>` them:
.. code-block:: python
# handlers/welcome.py
from telethon import events
@events.register(events.NewMessage('(?i)hello'))
async def handler(event):
client = event.client
await event.respond('Hey!')
await client.send_message('me', 'I said hello to someone')
Registering events is a way of saying "this method is an event handler".
You can use `telethon.events.is_handler` to check if any method is a handler.
You can think of them as a different approach to Flask's blueprints.
It's important to note that this does **not** add the handler to any client!
You never specified the client on which the handler should be used. You only
declared that it is a handler, and its type.
To actually use the handler, you need to `client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>` to the
client (or clients) where they should be added to:
.. code-block:: python
# main.py
from telethon import TelegramClient
import handlers.welcome
with TelegramClient(...) as client:
client.add_event_handler(handlers.welcome.handler)
client.run_until_disconnected()
This also means that you can register an event handler once and
then add it to many clients without re-declaring the event.
Events Without Decorators
*************************
If for any reason you don't want to use `telethon.events.register`,
you can explicitly pass the event handler to use to the mentioned
`client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>`:
.. code-block:: python
from telethon import TelegramClient, events
async def handler(event):
...
with TelegramClient(...) as client:
client.add_event_handler(handler, events.NewMessage)
client.run_until_disconnected()
Similarly, you also have `client.remove_event_handler
<telethon.client.updates.UpdateMethods.remove_event_handler>`
and `client.list_event_handlers
<telethon.client.updates.UpdateMethods.list_event_handlers>`.
The ``event`` argument is optional in all three methods and defaults to
`events.Raw <telethon.events.raw.Raw>` for adding, and ``None`` when
removing (so all callbacks would be removed).
.. note::
The ``event`` type is ignored in `client.add_event_handler
<telethon.client.updates.UpdateMethods.add_event_handler>`
if you have used `telethon.events.register` on the ``callback``
before, since that's the point of using such method at all.
Stopping Propagation of Updates
*******************************
There might be cases when an event handler is supposed to be used solitary and
it makes no sense to process any other handlers in the chain. For this case,
it is possible to raise a `telethon.events.StopPropagation` exception which
will cause the propagation of the update through your handlers to stop:
.. code-block:: python
from telethon.events import StopPropagation
@client.on(events.NewMessage)
async def _(event):
# ... some conditions
await event.delete()
# Other handlers won't have an event to work with
raise StopPropagation
@client.on(events.NewMessage)
async def _(event):
# Will never be reached, because it is the second handler
# in the chain.
pass
Remember to check :ref:`telethon-events-package` if you're looking for
the methods reference.
__ https://lonamiwebs.github.io/Telethon/types/update.html

View File

@ -1,54 +0,0 @@
.. _api-status:
==========
API Status
==========
In an attempt to help everyone who works with the Telegram API, the
library will by default report all *Remote Procedure Call* errors to
`RPC PWRTelegram <https://rpc.pwrtelegram.xyz/>`__, a public database
anyone can query, made by `Daniil <https://github.com/danog>`__. All the
information sent is a ``GET`` request with the error code, error message
and method used.
If you still would like to opt out, you can disable this feature by setting
``client.session.report_errors = False``. However Daniil would really thank
you if you helped him (and everyone) by keeping it on!
Querying the API status
***********************
The API is accessed through ``GET`` requests, which can be made for
instance through ``curl``. A JSON response will be returned.
**All known errors and their description**:
.. code:: bash
curl https://rpc.pwrtelegram.xyz/?all
**Error codes for a specific request**:
.. code:: bash
curl https://rpc.pwrtelegram.xyz/?for=messages.sendMessage
**Number of** ``RPC_CALL_FAIL``:
.. code:: bash
curl https://rpc.pwrtelegram.xyz/?rip # last hour
curl https://rpc.pwrtelegram.xyz/?rip=$(time()-60) # last minute
**Description of errors**:
.. code:: bash
curl https://rpc.pwrtelegram.xyz/?description_for=SESSION_REVOKED
**Code of a specific error**:
.. code:: bash
curl https://rpc.pwrtelegram.xyz/?code_for=STICKERSET_INVALID

View File

@ -1,73 +0,0 @@
===============================
Telegram API in Other Languages
===============================
Telethon was made for **Python**, and as far as I know, there is no
*exact* port to other languages. However, there *are* other
implementations made by awesome people (one needs to be awesome to
understand the official Telegram documentation) on several languages
(even more Python too), listed below:
C
*
Possibly the most well-known unofficial open source implementation out
there by `@vysheng <https://github.com/vysheng>`__,
`tgl <https://github.com/vysheng/tgl>`__, and its console client
`telegram-cli <https://github.com/vysheng/tg>`__. Latest development
has been moved to `BitBucket <https://bitbucket.org/vysheng/tdcli>`__.
C++
***
The newest (and official) library, written from scratch, is called
`tdlib <https://github.com/tdlib/td>`__ and is what the Telegram X
uses. You can find more information in the official documentation,
published `here <https://core.telegram.org/tdlib/docs/>`__.
JavaScript
**********
`@zerobias <https://github.com/zerobias>`__ is working on
`telegram-mtproto <https://github.com/zerobias/telegram-mtproto>`__,
a work-in-progress JavaScript library installable via
`npm <https://www.npmjs.com/>`__.
Kotlin
******
`Kotlogram <https://github.com/badoualy/kotlogram>`__ is a Telegram
implementation written in Kotlin (one of the
`official <https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/>`__
languages for
`Android <https://developer.android.com/kotlin/index.html>`__) by
`@badoualy <https://github.com/badoualy>`__, currently as a beta
yet working.
PHP
***
A PHP implementation is also available thanks to
`@danog <https://github.com/danog>`__ and his
`MadelineProto <https://github.com/danog/MadelineProto>`__ project, with
a very nice `online
documentation <https://daniil.it/MadelineProto/API_docs/>`__ too.
Python
******
A fairly new (as of the end of 2017) Telegram library written from the
ground up in Python by
`@delivrance <https://github.com/delivrance>`__ and his
`Pyrogram <https://github.com/pyrogram/pyrogram>`__ library.
There isn't really a reason to pick it over Telethon and it'd be kinda
sad to see you go, but it would be nice to know what you miss from each
other library in either one so both can improve.
Rust
****
Yet another work-in-progress implementation, this time for Rust thanks
to `@JuanPotato <https://github.com/JuanPotato>`__ under the fancy
name of `Vail <https://github.com/JuanPotato/Vail>`__.

View File

@ -1,71 +0,0 @@
====
Bots
====
.. note::
These examples assume you have read :ref:`accessing-the-full-api`.
Talking to Inline Bots
**********************
You can query an inline bot, such as `@VoteBot`__ (note, *query*,
not *interact* with a voting message), by making use of the
:tl:`GetInlineBotResultsRequest` request:
.. code-block:: python
from telethon.tl.functions.messages import GetInlineBotResultsRequest
bot_results = client(GetInlineBotResultsRequest(
bot, user_or_chat, 'query', ''
))
And you can select any of their results by using
:tl:`SendInlineBotResultRequest`:
.. code-block:: python
from telethon.tl.functions.messages import SendInlineBotResultRequest
client(SendInlineBotResultRequest(
get_input_peer(user_or_chat),
obtained_query_id,
obtained_str_id
))
Talking to Bots with special reply markup
*****************************************
Generally, you just use the `message.click()
<telethon.tl.custom.message.Message.click>` method:
.. code-block:: python
messages = client.get_messages('somebot')
messages[0].click(0)
You can also do it manually.
To interact with a message that has a special reply markup, such as
`@VoteBot`__ polls, you would use :tl:`GetBotCallbackAnswerRequest`:
.. code-block:: python
from telethon.tl.functions.messages import GetBotCallbackAnswerRequest
client(GetBotCallbackAnswerRequest(
user_or_chat,
msg.id,
data=msg.reply_markup.rows[wanted_row].buttons[wanted_button].data
))
It's a bit verbose, but it has all the information you would need to
show it visually (button rows, and buttons within each row, each with
its own data).
__ https://t.me/vote
__ https://t.me/vote

View File

@ -1,325 +0,0 @@
===============================
Working with Chats and Channels
===============================
.. note::
These examples assume you have read :ref:`accessing-the-full-api`.
Joining a chat or channel
*************************
Note that :tl:`Chat` are normal groups, and :tl:`Channel` are a
special form of :tl:`Chat`, which can also be super-groups if
their ``megagroup`` member is ``True``.
Joining a public channel
************************
Once you have the :ref:`entity <entities>` of the channel you want to join
to, you can make use of the :tl:`JoinChannelRequest` to join such channel:
.. code-block:: python
from telethon.tl.functions.channels import JoinChannelRequest
client(JoinChannelRequest(channel))
# In the same way, you can also leave such channel
from telethon.tl.functions.channels import LeaveChannelRequest
client(LeaveChannelRequest(input_channel))
For more on channels, check the `channels namespace`__.
__ https://lonamiwebs.github.io/Telethon/methods/channels/index.html
Joining a private chat or channel
*********************************
If all you have is a link like this one:
``https://t.me/joinchat/AAAAAFFszQPyPEZ7wgxLtd``, you already have
enough information to join! The part after the
``https://t.me/joinchat/``, this is, ``AAAAAFFszQPyPEZ7wgxLtd`` on this
example, is the ``hash`` of the chat or channel. Now you can use
:tl:`ImportChatInviteRequest` as follows:
.. code-block:: python
from telethon.tl.functions.messages import ImportChatInviteRequest
updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg'))
Adding someone else to such chat or channel
*******************************************
If you don't want to add yourself, maybe because you're already in,
you can always add someone else with the :tl:`AddChatUserRequest`, which
use is very straightforward, or :tl:`InviteToChannelRequest` for channels:
.. code-block:: python
# For normal chats
from telethon.tl.functions.messages import AddChatUserRequest
# Note that ``user_to_add`` is NOT the name of the parameter.
# It's the user you want to add (``user_id=user_to_add``).
client(AddChatUserRequest(
chat_id,
user_to_add,
fwd_limit=10 # Allow the user to see the 10 last messages
))
# For channels (which includes megagroups)
from telethon.tl.functions.channels import InviteToChannelRequest
client(InviteToChannelRequest(
channel,
[users_to_add]
))
Checking a link without joining
*******************************
If you don't need to join but rather check whether it's a group or a
channel, you can use the :tl:`CheckChatInviteRequest`, which takes in
the hash of said channel or group.
Retrieving all chat members (channels too)
******************************************
.. note::
Use the `telethon.telegram_client.TelegramClient.iter_participants`
friendly method instead unless you have a better reason not to!
This method will handle different chat types for you automatically.
Here is the easy way to do it:
.. code-block:: python
participants = client.get_participants(group)
Now we will show how the method works internally.
In order to get all the members from a mega-group or channel, you need
to use :tl:`GetParticipantsRequest`. As we can see it needs an
:tl:`InputChannel`, (passing the mega-group or channel you're going to
use will work), and a mandatory :tl:`ChannelParticipantsFilter`. The
closest thing to "no filter" is to simply use
:tl:`ChannelParticipantsSearch` with an empty ``'q'`` string.
If we want to get *all* the members, we need to use a moving offset and
a fixed limit:
.. code-block:: python
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
from time import sleep
offset = 0
limit = 100
all_participants = []
while True:
participants = client(GetParticipantsRequest(
channel, ChannelParticipantsSearch(''), offset, limit, hash=0
))
if not participants.users:
break
all_participants.extend(participants.users)
offset += len(participants.users)
.. note::
If you need more than 10,000 members from a group you should use the
mentioned ``client.get_participants(..., aggressive=True)``. It will
do some tricks behind the scenes to get as many entities as possible.
Refer to `issue 573`__ for more on this.
Note that :tl:`GetParticipantsRequest` returns :tl:`ChannelParticipants`,
which may have more information you need (like the role of the
participants, total count of members, etc.)
__ https://github.com/LonamiWebs/Telethon/issues/573
Recent Actions
**************
"Recent actions" is simply the name official applications have given to
the "admin log". Simply use :tl:`GetAdminLogRequest` for that, and
you'll get AdminLogResults.events in return which in turn has the final
`.action`__.
__ https://lonamiwebs.github.io/Telethon/types/channel_admin_log_event_action.html
Admin Permissions
*****************
Giving or revoking admin permissions can be done with the :tl:`EditAdminRequest`:
.. code-block:: python
from telethon.tl.functions.channels import EditAdminRequest
from telethon.tl.types import ChannelAdminRights
# You need both the channel and who to grant permissions
# They can either be channel/user or input channel/input user.
#
# ChannelAdminRights is a list of granted permissions.
# Set to True those you want to give.
rights = ChannelAdminRights(
post_messages=None,
add_admins=None,
invite_users=None,
change_info=True,
ban_users=None,
delete_messages=True,
pin_messages=True,
invite_link=None,
edit_messages=None
)
# Equivalent to:
# rights = ChannelAdminRights(
# change_info=True,
# delete_messages=True,
# pin_messages=True
# )
# Once you have a ChannelAdminRights, invoke it
client(EditAdminRequest(channel, user, rights))
# User will now be able to change group info, delete other people's
# messages and pin messages.
.. note::
Thanks to `@Kyle2142`__ for `pointing out`__ that you **cannot** set all
parameters to ``True`` to give a user full permissions, as not all
permissions are related to both broadcast channels/megagroups.
E.g. trying to set ``post_messages=True`` in a megagroup will raise an
error. It is recommended to always use keyword arguments, and to set only
the permissions the user needs. If you don't need to change a permission,
it can be omitted (full list `here`__).
Restricting Users
*****************
Similar to how you give or revoke admin permissions, you can edit the
banned rights of a user through :tl:`EditBannedRequest` and its parameter
:tl:`ChannelBannedRights`:
.. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights
from datetime import datetime, timedelta
# Restricting a user for 7 days, only allowing view/send messages.
#
# Note that it's "reversed". You must set to ``True`` the permissions
# you want to REMOVE, and leave as ``None`` those you want to KEEP.
rights = ChannelBannedRights(
until_date=timedelta(days=7),
view_messages=None,
send_messages=None,
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
# The above is equivalent to
rights = ChannelBannedRights(
until_date=datetime.now() + timedelta(days=7),
send_media=True,
send_stickers=True,
send_gifs=True,
send_games=True,
send_inline=True,
embed_links=True
)
client(EditBannedRequest(channel, user, rights))
You can also use a ``datetime`` object for ``until_date=``, or even a
Unix timestamp. Note that if you ban someone for less than 30 seconds
or for more than 366 days, Telegram will consider the ban to actually
last forever. This is officially documented under
https://core.telegram.org/bots/api#restrictchatmember.
Kicking a member
****************
Telegram doesn't actually have a request to kick a user from a group.
Instead, you need to restrict them so they can't see messages. Any date
is enough:
.. code-block:: python
from telethon.tl.functions.channels import EditBannedRequest
from telethon.tl.types import ChannelBannedRights
client(EditBannedRequest(
channel, user, ChannelBannedRights(
until_date=None,
view_messages=True
)
))
__ https://github.com/Kyle2142
__ https://github.com/LonamiWebs/Telethon/issues/490
__ https://lonamiwebs.github.io/Telethon/constructors/channel_admin_rights.html
Increasing View Count in a Channel
**********************************
It has been asked `quite`__ `a few`__ `times`__ (really, `many`__), and
while I don't understand why so many people ask this, the solution is to
use :tl:`GetMessagesViewsRequest`, setting ``increment=True``:
.. code-block:: python
# Obtain `channel' through dialogs or through client.get_entity() or anyhow.
# Obtain `msg_ids' through `.get_messages()` or anyhow. Must be a list.
client(GetMessagesViewsRequest(
peer=channel,
id=msg_ids,
increment=True
))
Note that you can only do this **once or twice a day** per account,
running this in a loop will obviously not increase the views forever
unless you wait a day between each iteration. If you run it any sooner
than that, the views simply won't be increased.
__ https://github.com/LonamiWebs/Telethon/issues/233
__ https://github.com/LonamiWebs/Telethon/issues/305
__ https://github.com/LonamiWebs/Telethon/issues/409
__ https://github.com/LonamiWebs/Telethon/issues/447

View File

@ -1,55 +0,0 @@
=======================
Projects using Telethon
=======================
This page lists some real world examples showcasing what can be built with
the library.
.. note::
Do you have a project that uses the library or know of any that's not
listed here? Feel free to leave a comment at
`issue 744 <https://github.com/LonamiWebs/Telethon/issues/744>`_
so it can be included in the next revision of the documentation!
.. _projects-telegram-export:
telethon_examples/
******************
`Link <https://github.com/LonamiWebs/Telethon/tree/master/telethon_examples>`_ /
`Author's website <https://lonamiwebs.github.io>`_
This documentation is not the only place where you can find useful code
snippets using the library. The main repository also has a folder with
some cool examples (even a Tkinter GUI!) which you can download, edit
and run to learn and play with them.
telegram-export
***************
`Link <https://github.com/expectocode/telegram-export>`_ /
`Author's website <https://github.com/expectocode>`_
A tool to download Telegram data (users, chats, messages, and media)
into a database (and display the saved data).
.. _projects-mautrix-telegram:
mautrix-telegram
****************
`Link <https://github.com/tulir/mautrix-telegram>`_ /
`Author's website <https://maunium.net/>`_
A Matrix-Telegram hybrid puppeting/relaybot bridge.
.. _projects-telegramtui:
TelegramTUI
***********
`Link <https://github.com/bad-day/TelegramTUI>`_ /
`Author's website <https://github.com/bad-day>`_
A Telegram client on your terminal.

View File

@ -1,619 +0,0 @@
.. _telegram-client-example:
========================
Examples with the Client
========================
This section explores the methods defined in the :ref:`telegram-client`
with some practical examples. The section assumes that you have imported
the ``telethon.sync`` package and that you have a client ready to use.
.. note::
There are some very common errors (such as forgetting to add
``import telethon.sync``) for newcomers to ``asyncio``:
.. code-block:: python
# AttributeError: 'coroutine' object has no attribute 'first_name'
print(client.get_me().first_name)
# TypeError: 'AsyncGenerator' object is not iterable
for message in client.iter_messages('me'):
...
# RuntimeError: This event loop is already running
with client.conversation('me') as conv:
...
That error means you're probably inside an ``async def`` so you
need to use:
.. code-block:: python
print((await client.get_me()).first_name)
async for message in client.iter_messages('me'):
...
async with client.conversation('me') as conv:
...
You can of course call other ``def`` functions from your ``async def``
event handlers, but if they need making API calls, make your own
functions ``async def`` so you can ``await`` things:
.. code-block:: python
async def helper(client):
await client.send_message('me', 'Hi')
If you're not inside an ``async def`` you can enter one like so:
.. code-block:: python
import asyncio
loop = asyncio.get_event_loop()
loop.run_until_complete(my_async_def())
.. contents::
Authorization
*************
Starting the client is as easy as calling `client.start()
<telethon.client.auth.AuthMethods.start>`:
.. code-block:: python
client.start()
... # code using the client
client.disconnect()
And you can even use a ``with`` block:
.. code-block:: python
with client:
... # code using the client
.. note::
Remember we assume you have ``import telethon.sync``. You can of course
use the library without importing it. The code would be rewritten as:
.. code-block:: python
import asyncio
loop = asyncio.get_event_loop()
async def main():
await client.start()
...
await client.disconnect()
# or
async with client:
...
loop.run_until_complete(main())
All methods that need access to the network (e.g. to make an API call)
**must** be awaited (or their equivalent such as ``async for`` and
``async with``). You can do this yourself or you can let the library
do it for you by using ``import telethon.sync``. With event handlers,
you must do this yourself.
The cleanest way to delete your ``*.session`` file is `client.log_out
<telethon.client.auth.AuthMethods.log_out>`. Note that you will obviously
need to login again if you use this:
.. code-block:: python
# Logs out and deletes the session file; you will need to sign in again
client.log_out()
# You often simply want to disconnect. You will not need to sign in again
client.disconnect()
Group Chats
***********
You can easily iterate over all the :tl:`User` in a chat and
do anything you want with them by using `client.iter_participants
<telethon.client.chats.ChatMethods.iter_participants>`:
.. code-block:: python
for user in client.iter_participants(chat):
... # do something with the user
You can also search by their name:
.. code-block:: python
for user in client.iter_participants(chat, search='name'):
...
Or by their type (e.g. if they are admin) with :tl:`ChannelParticipantsFilter`:
.. code-block:: python
from telethon.tl.types import ChannelParticipantsAdmins
for user in client.iter_participants(chat, filter=ChannelParticipantsAdmins):
...
Open Conversations and Joined Channels
**************************************
The conversations you have open and the channels you have joined
are in your "dialogs", so to get them you need to `client.get_dialogs
<telethon.client.dialogs.DialogMethods.get_dialogs>`:
.. code-block:: python
dialogs = client.get_dialogs()
first = dialogs[0]
print(first.title)
You can then use the dialog as if it were a peer:
.. code-block:: python
client.send_message(first, 'hi')
You can access `dialog.draft <telethon.tl.custom.draft.Draft>` or you can
get them all at once without getting the dialogs:
.. code-block:: python
drafts = client.get_drafts()
Downloading Media
*****************
It's easy to `download_profile_photo
<telethon.client.downloads.DownloadMethods.download_profile_photo>`:
.. code-block:: python
client.download_profile_photo(user)
Or `download_media <telethon.client.downloads.DownloadMethods.download_media>`
from a message:
.. code-block:: python
client.download_media(message)
client.download_media(message, filename)
# or
message.download_media()
message.download_media(filename)
Remember that these methods return the final filename where the
media was downloaded (e.g. it may add the extension automatically).
Getting Messages
****************
You can easily iterate over all the `messages
<telethon.tl.custom.message.Message>` of a chat with `iter_messages
<telethon.client.messages.MessageMethods.iter_messages>`:
.. code-block:: python
for message in client.iter_messages(chat):
... # do something with the message from recent to older
for message in client.iter_messages(chat, reverse=True):
... # going from the oldest to the most recent
You can also use it to search for messages from a specific person:
.. code-block:: python
for message in client.iter_messages(chat, from_user='me'):
...
Or you can search by text:
.. code-block:: python
for message in client.iter_messages(chat, search='hello'):
...
Or you can search by media with a :tl:`MessagesFilter`:
.. code-block:: python
from telethon.tl.types import InputMessagesFilterPhotos
for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos):
...
If you want a list instead, use the get variant. The second
argument is the limit, and ``None`` means "get them all":
.. code-block:: python
from telethon.tl.types import InputMessagesFilterPhotos
# Get 0 photos and print the total
photos = client.get_messages(chat, 0, filter=InputMessagesFilterPhotos)
print(photos.total)
# Get all the photos
photos = client.get_messages(chat, None, filter=InputMessagesFilterPhotos)
Or just some IDs:
.. code-block:: python
message_1337 = client.get_messages(chats, ids=1337)
Sending Messages
****************
Just use `send_message <telethon.client.messages.MessageMethods.send_message>`:
.. code-block:: python
client.send_message('lonami', 'Thanks for the Telethon library!')
The function returns the `custom.Message <telethon.tl.custom.message.Message>`
that was sent so you can do more things with it if you want.
You can also `reply <telethon.tl.custom.message.Message.reply>` or
`respond <telethon.tl.custom.message.Message.respond>` to messages:
.. code-block:: python
message.reply('Hello')
message.respond('World')
Sending Markdown or HTML messages
*********************************
Markdown (``'md'`` or ``'markdown'``) is the default `parse_mode
<telethon.client.messageparse.MessageParseMethods.parse_mode>`
for the client. You can change the default parse mode like so:
.. code-block:: python
client.parse_mode = 'html'
Now all messages will be formatted as HTML by default:
.. code-block:: python
client.send_message('me', 'Some <b>bold</b> and <i>italic</i> text')
client.send_message('me', 'An <a href="https://example.com">URL</a>')
client.send_message('me', '<code>code</code> and <pre>pre\nblocks</pre>')
client.send_message('me', '<a href="tg://user?id=me">Mentions</a>')
You can override the default parse mode to use for special cases:
.. code-block:: python
# No parse mode by default
client.parse_mode = None
# ...but here I want markdown
client.send_message('me', 'Hello, **world**!', parse_mode='md')
# ...and here I need HTML
client.send_message('me', 'Hello, <i>world</i>!', parse_mode='html')
The rules are the same as for Bot API, so please refer to
https://core.telegram.org/bots/api#formatting-options.
Sending Messages with Media
***************************
Sending media can be done with `send_file
<telethon.client.uploads.UploadMethods.send_file>`:
.. code-block:: python
client.send_file(chat, '/my/photos/me.jpg', caption="It's me!")
# or
client.send_message(chat, "It's me!", file='/my/photos/me.jpg')
You can send voice notes or round videos by setting the right arguments:
.. code-block:: python
client.send_file(chat, '/my/songs/song.mp3', voice_note=True)
client.send_file(chat, '/my/videos/video.mp4', video_note=True)
You can set a JPG thumbnail for any document:
.. code-block:: python
client.send_file(chat, '/my/documents/doc.txt', thumb='photo.jpg')
You can force sending images as documents:
.. code-block:: python
client.send_file(chat, '/my/photos/photo.png', force_document=True)
You can send albums if you pass more than one file:
.. code-block:: python
client.send_file(chat, [
'/my/photos/holiday1.jpg',
'/my/photos/holiday2.jpg',
'/my/drawings/portrait.png'
])
The caption can also be a list to match the different photos.
Sending Messages with Buttons
*****************************
You must sign in as a bot in order to add inline buttons (or normal
keyboards) to your messages. Once you have signed in as a bot specify
the `Button <telethon.tl.custom.button.Button>` or buttons to use:
.. code-block:: python
from telethon.tl.custom import Button
async def callback(event):
await event.edit('Thank you!')
client.send_message(chat, 'Hello!',
buttons=Button.inline('Click me', callback))
You can also add the event handler yourself, or change the data payload:
.. code-block:: python
from telethon import events
@client.on(events.CallbackQuery)
async def handler(event):
await event.answer('You clicked {}!'.format(event.data))
client.send_message(chat, 'Pick one', buttons=[
[Button.inline('Left'), Button.inline('Right')],
[Button.url('Check my site!', 'https://lonamiwebs.github.io')]
])
You can also use normal buttons (not inline) to request the user's
location, phone number, or simply for them to easily send a message:
.. code-block:: python
client.send_message(chat, 'Welcome', buttons=[
Button.text('Thanks!'),
Button.request_phone('Send phone'),
Button.request_location('Send location')
])
Forcing a reply or removing the keyboard can also be done:
.. code-block:: python
client.send_message(chat, 'Reply to me', buttons=Button.force_reply())
client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear())
Remember to check `Button <telethon.tl.custom.button.Button>` for more.
Making Inline Queries
*********************
You can send messages ``via @bot`` by first making an inline query:
.. code-block:: python
results = client.inline_query('like', 'Do you like Telethon?')
Then access the result you want and `click
<telethon.tl.custom.inlineresult.InlineResult.click>` it in the chat
where you want to send it to:
.. code-block:: python
message = results[0].click('TelethonOffTopic')
Sending messages through inline bots lets you use buttons as a normal user.
Clicking Buttons
****************
Let's `click <telethon.tl.custom.message.Message.click>`
the message we sent in the example above!
.. code-block:: python
message.click(0)
This will click the first button in the message. You could also
``click(row, column)``, using some text such as ``click(text='👍')``
or even the data directly ``click(data=b'payload')``.
Conversations: Waiting for Messages or Replies
**********************************************
This one is really useful for unit testing your bots, which you can
even write within Telethon itself! You can open a `Conversation
<telethon.tl.custom.conversation.Conversation>` in any chat as:
.. code-block:: python
with client.conversation(chat) as conv:
...
Conversations let you program a finite state machine with the
higher-level constructs we are all used to, such as ``while``
and ``if`` conditionals instead setting the state and jumping
from one place to another which is less clean.
For instance, let's imagine ``you`` are the bot talking to ``usr``:
.. code-block:: text
<you> Hi!
<usr> Hello!
<you> Please tell me your name
<usr> ?
<you> Your name didn't have any letters! Try again
<usr> Lonami
<you> Thanks!
This can be programmed as follows:
.. code-block:: python
with bot.conversation(chat) as conv:
conv.send_message('Hi!')
hello = conv.get_response()
conv.send_message('Please tell me your name')
name = conv.get_response().raw_text
while not any(x.isalpha() for x in name):
conv.send_message("Your name didn't have any letters! Try again")
name = conv.get_response().raw_text
conv.send_message('Thanks {}!'.format(name))
Note how we sent a message **with the conversation**, not with the client.
This is important so the conversation remembers what messages you sent.
The method reference for getting a response, getting a reply or marking
the conversation as read can be found by clicking here: `Conversation
<telethon.tl.custom.conversation.Conversation>`.
Sending a message or getting a response returns a `Message
<telethon.tl.custom.message.Message>`. Reading its documentation
will also be really useful!
If a reply never arrives or too many messages come in, getting
responses will raise ``asyncio.TimeoutError`` or ``ValueError``
respectively. You may want to ``except`` these and tell the user
they were too slow, or simply drop the conversation.
Forwarding Messages
*******************
You can forward up to 100 messages with `forward_messages
<telethon.client.messages.MessageMethods.forward_messages>`,
or a single one if you have the message with `forward_to
<telethon.tl.custom.message.Message.forward_to>`:
.. code-block:: python
# a single one
client.forward_messages(chat, message)
# or
client.forward_messages(chat, message_id, from_chat)
# or
message.forward_to(chat)
# multiple
client.forward_messages(chat, messages)
# or
client.forward_messages(chat, message_ids, from_chat)
You can also "forward" messages without showing "Forwarded from" by
re-sending the message:
.. code-block:: python
client.send_message(chat, message)
Editing Messages
****************
With `edit_message <telethon.client.messages.MessageMethods.edit_message>`
or `message.edit <telethon.tl.custom.message.Message.edit>`:
.. code-block:: python
client.edit_message(message, 'New text')
# or
message.edit('New text')
# or
client.edit_message(chat, message_id, 'New text')
Deleting Messages
*****************
With `delete_messages <telethon.client.messages.MessageMethods.delete_messages>`
or `message.delete <telethon.tl.custom.message.Message.delete>`. Note that the
first one supports deleting entire chats at once!:
.. code-block:: python
client.delete_messages(chat, messages)
# or
message.delete()
Marking Messages as Read
************************
Marking messages up to a certain point as read with `send_read_acknowledge
<telethon.client.messages.MessageMethods.send_read_acknowledge>`:
.. code-block:: python
client.send_read_acknowledge(last_message)
# or
client.send_read_acknowledge(last_message_id)
# or
client.send_read_acknowledge(messages)
Getting Entities
****************
Entities are users, chats, or channels. You can get them by their ID if
you have seen them before (e.g. you probably need to get all dialogs or
all the members from a chat first):
.. code-block:: python
from telethon import utils
me = client.get_entity('me')
print(utils.get_display_name(me))
chat = client.get_input_entity('username')
for message in client.iter_messages(chat):
...
# Note that you could have used the username directly, but it's
# good to use get_input_entity if you will reuse it a lot.
for message in client.iter_messages('username'):
...
some_id = client.get_peer_id('+34123456789')
The documentation for shown methods are `get_entity
<telethon.client.users.UserMethods.get_entity>`, `get_input_entity
<telethon.client.users.UserMethods.get_input_entity>` and `get_peer_id
<telethon.client.users.UserMethods.get_peer_id>`.
Note that the utils package also has a `get_peer_id
<telethon.utils.get_peer_id>` but it won't work with things
that need access to the network such as usernames or phones,
which need to be in your contact list.

View File

@ -1,138 +0,0 @@
=====================
Working with messages
=====================
.. note::
These examples assume you have read :ref:`accessing-the-full-api`.
Forwarding messages
*******************
.. note::
Use the `telethon.client.messages.MessageMethods.forward_messages`
friendly method instead unless you have a better reason not to!
This method automatically accepts either a single message or many of them.
.. code-block:: python
# If you only have the message IDs
client.forward_messages(
entity, # to which entity you are forwarding the messages
message_ids, # the IDs of the messages (or message) to forward
from_entity # who sent the messages?
)
# If you have ``Message`` objects
client.forward_messages(
entity, # to which entity you are forwarding the messages
messages # the messages (or message) to forward
)
# You can also do it manually if you prefer
from telethon.tl.functions.messages import ForwardMessagesRequest
messages = foo() # retrieve a few messages (or even one, in a list)
from_entity = bar()
to_entity = baz()
client(ForwardMessagesRequest(
from_peer=from_entity, # who sent these messages?
id=[msg.id for msg in messages], # which are the messages?
to_peer=to_entity # who are we forwarding them to?
))
The named arguments are there for clarity, although they're not needed because
they appear in order. You can obviously just wrap a single message on the list
too, if that's all you have.
Searching Messages
*******************
.. note::
Use the `telethon.client.messages.MessageMethods.iter_messages`
friendly method instead unless you have a better reason not to!
This method has ``search`` and ``filter`` parameters that will
suit your needs.
Messages are searched through the obvious :tl:`SearchRequest`, but you may run
into issues_. A valid example would be:
.. code-block:: python
from telethon.tl.functions.messages import SearchRequest
from telethon.tl.types import InputMessagesFilterEmpty
filter = InputMessagesFilterEmpty()
result = client(SearchRequest(
peer=peer, # On which chat/conversation
q='query', # What to search for
filter=filter, # Filter to use (maybe filter for media)
min_date=None, # Minimum date
max_date=None, # Maximum date
offset_id=0, # ID of the message to use as offset
add_offset=0, # Additional offset
limit=10, # How many results
max_id=0, # Maximum message ID
min_id=0, # Minimum message ID
from_id=None, # Who must have sent the message (peer)
hash=0 # Special number to return nothing on no-change
))
It's important to note that the optional parameter ``from_id`` could have
been omitted (defaulting to ``None``). Changing it to :tl:`InputUserEmpty`, as one
could think to specify "no user", won't work because this parameter is a flag,
and it being unspecified has a different meaning.
If one were to set ``from_id=InputUserEmpty()``, it would filter messages
from "empty" senders, which would likely match no users.
If you get a ``ChatAdminRequiredError`` on a channel, it's probably because
you tried setting the ``from_id`` filter, and as the error says, you can't
do that. Leave it set to ``None`` and it should work.
As with every method, make sure you use the right ID/hash combination for
your :tl:`InputUser` or :tl:`InputChat`, or you'll likely run into errors like
``UserIdInvalidError``.
Sending stickers
****************
Stickers are nothing else than ``files``, and when you successfully retrieve
the stickers for a certain sticker set, all you will have are ``handles`` to
these files. Remember, the files Telegram holds on their servers can be
referenced through this pair of ID/hash (unique per user), and you need to
use this handle when sending a "document" message. This working example will
send yourself the very first sticker you have:
.. code-block:: python
# Get all the sticker sets this user has
from telethon.tl.functions.messages import GetAllStickersRequest
sticker_sets = client(GetAllStickersRequest(0))
# Choose a sticker set
from telethon.tl.functions.messages import GetStickerSetRequest
from telethon.tl.types import InputStickerSetID
sticker_set = sticker_sets.sets[0]
# Get the stickers for this sticker set
stickers = client(GetStickerSetRequest(
stickerset=InputStickerSetID(
id=sticker_set.id, access_hash=sticker_set.access_hash
)
))
# Stickers are nothing more than files, so send that
client.send_file('me', stickers.documents[0])
.. _issues: https://github.com/LonamiWebs/Telethon/issues/215

View File

@ -1,27 +0,0 @@
========================================
Deleted, Limited or Deactivated Accounts
========================================
If you're from Iran or Russia, we have bad news for you. Telegram is much more
likely to ban these numbers, as they are often used to spam other accounts,
likely through the use of libraries like this one. The best advice we can
give you is to not abuse the API, like calling many requests really quickly,
and to sign up with these phones through an official application.
We have also had reports from Kazakhstan and China, where connecting
would fail. To solve these connection problems, you should use a proxy.
Telegram may also ban virtual (VoIP) phone numbers,
as again, they're likely to be used for spam.
If you want to check if your account has been limited,
simply send a private message to `@SpamBot`__ through Telegram itself.
You should notice this by getting errors like ``PeerFloodError``,
which means you're limited, for instance,
when sending a message to some accounts but not others.
For more discussion, please see `issue 297`__.
__ https://t.me/SpamBot
__ https://github.com/LonamiWebs/Telethon/issues/297

View File

@ -1,40 +0,0 @@
================
Enabling Logging
================
Telethon makes use of the `logging`__ module, and you can enable it as follows:
.. code:: python
import logging
logging.basicConfig(level=logging.DEBUG)
The library has the `NullHandler`__ added by default so that no log calls
will be printed unless you explicitly enable it.
You can also `use the module`__ on your own project very easily:
.. code-block:: python
import logging
logger = logging.getLogger(__name__)
logger.debug('Debug messages')
logger.info('Useful information')
logger.warning('This is a warning!')
If you want to enable ``logging`` for your project *but* use a different
log level for the library:
.. code-block:: python
import logging
logging.basicConfig(level=logging.DEBUG)
# For instance, show only warnings and above
logging.getLogger('telethon').setLevel(level=logging.WARNING)
__ https://docs.python.org/3/library/logging.html
__ https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library
__ https://docs.python.org/3/howto/logging.html

View File

@ -1,29 +0,0 @@
==========
RPC Errors
==========
RPC stands for Remote Procedure Call, and when the library raises
a ``RPCError``, it's because you have invoked some of the API
methods incorrectly (wrong parameters, wrong permissions, or even
something went wrong on Telegram's server). All the errors are
available in :ref:`telethon-errors-package`, but some examples are:
- ``FloodWaitError`` (420), the same request was repeated many times.
Must wait ``.seconds`` (you can access this parameter).
- ``SessionPasswordNeededError``, if you have setup two-steps
verification on Telegram.
- ``CdnFileTamperedError``, if the media you were trying to download
from a CDN has been altered.
- ``ChatAdminRequiredError``, you don't have permissions to perform
said operation on a chat or channel. Try avoiding filters, i.e. when
searching messages.
The generic classes for different error codes are:
- ``InvalidDCError`` (303), the request must be repeated on another DC.
- ``BadRequestError`` (400), the request contained errors.
- ``UnauthorizedError`` (401), the user is not authorized yet.
- ``ForbiddenError`` (403), privacy violation error.
- ``NotFoundError`` (404), make sure you're invoking ``Request``\ 's!
If the error is not recognised, it will only be an ``RPCError``.

View File

@ -1,43 +1,32 @@
.. Telethon documentation master file, created by
sphinx-quickstart on Fri Nov 17 15:36:11 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
========================
Telethon's Documentation
========================
====================================
Welcome to Telethon's documentation!
====================================
.. code-block:: python
from telethon.sync import TelegramClient, events
with TelegramClient('name', api_id, api_hash) as client:
client.send_message('me', 'Hello, myself!')
print(client.download_profile_photo('me'))
@client.on(events.NewMessage(pattern='(?i).*Hello'))
async def handler(event):
await event.reply('Hey!')
client.run_until_disconnected()
Pure Python 3 Telegram client library.
Official Site `here <https://lonamiwebs.github.io/Telethon>`_.
Please follow the links on the index below to navigate from here,
or use the menu on the left. Remember to read the :ref:`changelog`
when you upgrade!
.. important::
If you're new here, you want to read :ref:`getting-started`. If you're
looking for the method reference, you should check :ref:`telethon-client`.
The mentioned :ref:`telethon-client` is an important section and it
contains the friendly methods that **you should use** most of the time.
.. note::
The library uses `asyncio <https://docs.python.org/3/library/asyncio.html>`_
under the hood, but you don't need to know anything about it unless you're
going to work with updates! If you're a user of Telethon pre-1.0 and you
aren't ready to convert your event handlers into ``async``, you can use
`a simpler version <https://github.com/LonamiWebs/Telethon/tree/sync>`_
(select the "sync" version in ``readthedocs``' bottom left corner).
If you used Telethon pre-1.0 but your scripts don't use updates or threads,
running ``import telethon.sync`` should make them Just Work. Otherwise,
we have :ref:`asyncio-magic` to teach you why ``asyncio`` is good and
how to use it.
* Are you new here? Jump straight into :ref:`installation`!
* Looking for the method reference? See :ref:`client-ref`.
* Did you upgrade the library? Please read :ref:`changelog`.
* Used Telethon before v1.0? See :ref:`compatibility-and-convenience`.
* Coming from Bot API or want to create new bots? See :ref:`botapi`.
* Need the full API reference? https://tl.telethon.dev/.
What is this?
*************
-------------
Telegram is a popular messaging application. This library is meant
to make it easy for you to write Python programs that can interact
@ -45,93 +34,87 @@ with Telegram. Think of it as a wrapper that has already done the
heavy job for you, so you can focus on developing an application.
.. _installation-and-usage:
How should I use the documentation?
-----------------------------------
If you are getting started with the library, you should follow the
documentation in order by pressing the "Next" button at the bottom-right
of every page.
You can also use the menu on the left to quickly skip over sections.
.. toctree::
:maxdepth: 2
:caption: Installation and Simple Usage
:hidden:
:caption: First Steps
extra/basic/getting-started
extra/basic/installation
extra/basic/creating-a-client
extra/basic/telegram-client
extra/basic/entities
extra/basic/asyncio-magic
extra/basic/working-with-updates
.. _Advanced-usage:
basic/installation
basic/signing-in
basic/quick-start
basic/updates
basic/next-steps
.. toctree::
:maxdepth: 2
:caption: Advanced Usage
:hidden:
:caption: Quick References
extra/advanced-usage/accessing-the-full-api
extra/advanced-usage/sessions
extra/advanced-usage/update-modes
extra/advanced-usage/mastering-telethon
.. _Examples:
quick-references/faq
quick-references/client-reference
quick-references/events-reference
quick-references/objects-reference
.. toctree::
:maxdepth: 2
:caption: Examples
:hidden:
:caption: Concepts
extra/examples/telegram-client
extra/examples/working-with-messages
extra/examples/chats-and-channels
extra/examples/users
extra/examples/bots
extra/examples/projects-using-telethon
.. _Troubleshooting:
concepts/strings
concepts/entities
concepts/chats-vs-channels
concepts/updates
concepts/sessions
concepts/full-api
concepts/errors
concepts/botapi-vs-mtproto
concepts/asyncio
.. toctree::
:maxdepth: 2
:caption: Troubleshooting
:hidden:
:caption: Full API Examples
extra/troubleshooting/enable-logging
extra/troubleshooting/deleted-limited-or-deactivated-accounts
extra/troubleshooting/rpc-errors
.. _Developing:
examples/word-of-warning
examples/chats-and-channels
examples/users
examples/working-with-messages
.. toctree::
:maxdepth: 2
:caption: Developing
:hidden:
:caption: Developing
extra/developing/philosophy.rst
extra/developing/api-status.rst
extra/developing/test-servers.rst
extra/developing/project-structure.rst
extra/developing/coding-style.rst
extra/developing/understanding-the-type-language.rst
extra/developing/tips-for-porting-the-project.rst
extra/developing/telegram-api-in-other-languages.rst
.. _More:
developing/philosophy.rst
developing/test-servers.rst
developing/project-structure.rst
developing/coding-style.rst
developing/testing.rst
developing/understanding-the-type-language.rst
developing/tips-for-porting-the-project.rst
developing/telegram-api-in-other-languages.rst
.. toctree::
:maxdepth: 2
:caption: More
extra/changelog
extra/wall-of-shame.rst
:hidden:
:caption: Miscellaneous
misc/changelog
misc/wall-of-shame.rst
misc/compatibility-and-convenience
.. toctree::
:caption: Telethon modules
:hidden:
:caption: Telethon Modules
modules
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
modules/client
modules/events
modules/custom
modules/utils
modules/errors
modules/sessions
modules/network
modules/helpers

View File

@ -0,0 +1,185 @@
.. _compatibility-and-convenience:
=============================
Compatibility and Convenience
=============================
Telethon is an `asyncio` library. Compatibility is an important concern,
and while it can't always be kept and mistakes happens, the :ref:`changelog`
is there to tell you when these important changes happen.
.. contents::
Compatibility
=============
Some decisions when developing will inevitable be proven wrong in the future.
One of these decisions was using threads. Now that Python 3.4 is reaching EOL
and using `asyncio` is usable as of Python 3.5 it makes sense for a library
like Telethon to make a good use of it.
If you have old code, **just use old versions** of the library! There is
nothing wrong with that other than not getting new updates or fixes, but
using a fixed version with ``pip install telethon==0.19.1.6`` is easy
enough to do.
You might want to consider using `Virtual Environments
<https://docs.python.org/3/tutorial/venv.html>`_ in your projects.
There's no point in maintaining a synchronous version because the whole point
is that people don't have time to upgrade, and there has been several changes
and clean-ups. Using an older version is the right way to go.
Sometimes, other small decisions are made. These all will be reflected in the
:ref:`changelog` which you should read when upgrading.
If you want to jump the `asyncio` boat, here are some of the things you will
need to start migrating really old code:
.. code-block:: python
# 1. Import the client from telethon.sync
from telethon.sync import TelegramClient
# 2. Change this monster...
try:
assert client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
me = client.sign_in(phone_number, input('Enter code: '))
... # REST OF YOUR CODE
finally:
client.disconnect()
# ...for this:
with client:
... # REST OF YOUR CODE
# 3. client.idle() no longer exists.
# Change this...
client.idle()
# ...to this:
client.run_until_disconnected()
# 4. client.add_update_handler no longer exists.
# Change this...
client.add_update_handler(handler)
# ...to this:
client.add_event_handler(handler)
In addition, all the update handlers must be ``async def``, and you need
to ``await`` method calls that rely on network requests, such as getting
the chat or sender. If you don't use updates, you're done!
Convenience
===========
.. note::
The entire documentation assumes you have done one of the following:
.. code-block:: python
from telethon import TelegramClient, sync
# or
from telethon.sync import TelegramClient
This makes the examples shorter and easier to think about.
For quick scripts that don't need updates, it's a lot more convenient to
forget about `asyncio` and just work with sequential code. This can prove
to be a powerful hybrid for running under the Python REPL too.
.. code-block:: python
from telethon.sync import TelegramClient
# ^~~~~ note this part; it will manage the asyncio loop for you
with TelegramClient(...) as client:
print(client.get_me().username)
# ^ notice the lack of await, or loop.run_until_complete().
# Since there is no loop running, this is done behind the scenes.
#
message = client.send_message('me', 'Hi!')
import time
time.sleep(5)
message.delete()
# You can also have an hybrid between a synchronous
# part and asynchronous event handlers.
#
from telethon import events
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
async def handler(event):
await event.reply('hey')
client.run_until_disconnected()
Some methods, such as ``with``, ``start``, ``disconnect`` and
``run_until_disconnected`` work both in synchronous and asynchronous
contexts by default for convenience, and to avoid the little overhead
it has when using methods like sending a message, getting messages, etc.
This keeps the best of both worlds as a sane default.
.. note::
As a rule of thumb, if you're inside an ``async def`` and you need
the client, you need to ``await`` calls to the API. If you call other
functions that also need API calls, make them ``async def`` and ``await``
them too. Otherwise, there is no need to do so with this mode.
Speed
=====
When you're ready to micro-optimize your application, or if you simply
don't need to call any non-basic methods from a synchronous context,
just get rid of ``telethon.sync`` and work inside an ``async def``:
.. code-block:: python
import asyncio
from telethon import TelegramClient, events
async def main():
async with TelegramClient(...) as client:
print((await client.get_me()).username)
# ^_____________________^ notice these parenthesis
# You want to ``await`` the call, not the username.
#
message = await client.send_message('me', 'Hi!')
await asyncio.sleep(5)
await message.delete()
@client.on(events.NewMessage(pattern='(?i)hi|hello'))
async def handler(event):
await event.reply('hey')
await client.run_until_disconnected()
asyncio.run(main())
The ``telethon.sync`` magic module essentially wraps every method behind:
.. code-block:: python
asyncio.run(main())
With some other tricks, so that you don't have to write it yourself every time.
That's the overhead you pay if you import it, and what you save if you don't.
Learning
========
You know the library uses `asyncio` everywhere, and you want to learn
how to do things right. Even though `asyncio` is its own topic, the
documentation wants you to learn how to use Telethon correctly, and for
that, you need to use `asyncio` correctly too. For this reason, there
is a section called :ref:`mastering-asyncio` that will introduce you to
the `asyncio` world, with links to more resources for learning how to
use it. Feel free to check that section out once you have read the rest.

View File

@ -10,7 +10,7 @@ library. Said section is **not** for issues on *your* program but rather
issues with Telethon itself.
If you have not made the effort to 1. read through the docs and 2.
`look for the method you need <https://lonamiwebs.github.io/Telethon/>`__,
`look for the method you need <https://tl.telethon.dev/>`__,
you will end up on the `Wall of
Shame <https://github.com/LonamiWebs/Telethon/issues?q=is%3Aissue+label%3ARTFM+is%3Aclosed>`__,
i.e. all issues labeled

View File

@ -1,7 +0,0 @@
telethon
========
.. toctree::
:maxdepth: 3
telethon

View File

@ -1,38 +1,66 @@
.. _telethon-client:
==============
TelegramClient
==============
telethon\.client package
========================
.. currentmodule:: telethon.client
The `telethon.TelegramClient` aggregates several mixin classes to provide
all the common functionality in a nice, Pythonic interface. Each mixin has
its own methods, which you all can use.
The `TelegramClient <telegramclient.TelegramClient>` aggregates several mixin
classes to provide all the common functionality in a nice, Pythonic interface.
Each mixin has its own methods, which you all can use.
**In short, to create a client you must run:**
.. code-block:: python
import asyncio
from telethon import TelegramClient
client = TelegramClient(name, api_id, api_hash)
async def main():
client = await TelegramClient(name, api_id, api_hash).start()
# Now you can use all client methods listed below, like for example...
await client.send_message('me', 'Hello to myself!')
asyncio.get_event_loop().run_until_complete(main())
with client:
client.loop.run_until_complete(main())
You **don't** need to import these `AuthMethods`, `MessageMethods`, etc.
Together they are the `telethon.TelegramClient` and you can access all of
their methods.
Together they are the `TelegramClient <telegramclient.TelegramClient>` and
you can access all of their methods.
See :ref:`client-ref` for a short summary.
.. automodule:: telethon.client.telegramclient
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.telegrambaseclient
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.account
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.auth
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.bots
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.buttons
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.chats
:members:
@ -44,6 +72,11 @@ their methods.
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.downloads
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.messageparse
:members:
:undoc-members:
@ -59,11 +92,6 @@ their methods.
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.downloads
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.uploads
:members:
:undoc-members:
@ -73,9 +101,3 @@ their methods.
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.client.telegrambaseclient
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,163 @@
==============
Custom package
==============
The `telethon.tl.custom` package contains custom classes that the library
uses in order to make working with Telegram easier. Only those that you
are supposed to use will be documented here. You can use undocumented ones
at your own risk.
More often than not, you don't need to import these (unless you want
type hinting), nor do you need to manually create instances of these
classes. They are returned by client methods.
.. contents::
.. automodule:: telethon.tl.custom
:members:
:undoc-members:
:show-inheritance:
AdminLogEvent
=============
.. automodule:: telethon.tl.custom.adminlogevent
:members:
:undoc-members:
:show-inheritance:
Button
======
.. automodule:: telethon.tl.custom.button
:members:
:undoc-members:
:show-inheritance:
ChatGetter
==========
.. automodule:: telethon.tl.custom.chatgetter
:members:
:undoc-members:
:show-inheritance:
Conversation
============
.. automodule:: telethon.tl.custom.conversation
:members:
:undoc-members:
:show-inheritance:
Dialog
======
.. automodule:: telethon.tl.custom.dialog
:members:
:undoc-members:
:show-inheritance:
Draft
=====
.. automodule:: telethon.tl.custom.draft
:members:
:undoc-members:
:show-inheritance:
File
====
.. automodule:: telethon.tl.custom.file
:members:
:undoc-members:
:show-inheritance:
Forward
=======
.. automodule:: telethon.tl.custom.forward
:members:
:undoc-members:
:show-inheritance:
InlineBuilder
=============
.. automodule:: telethon.tl.custom.inlinebuilder
:members:
:undoc-members:
:show-inheritance:
InlineResult
============
.. automodule:: telethon.tl.custom.inlineresult
:members:
:undoc-members:
:show-inheritance:
InlineResults
=============
.. automodule:: telethon.tl.custom.inlineresults
:members:
:undoc-members:
:show-inheritance:
Message
=======
.. automodule:: telethon.tl.custom.message
:members:
:undoc-members:
:show-inheritance:
MessageButton
=============
.. automodule:: telethon.tl.custom.messagebutton
:members:
:undoc-members:
:show-inheritance:
ParticipantPermissions
======================
.. automodule:: telethon.tl.custom.participantpermissions
:members:
:undoc-members:
:show-inheritance:
QRLogin
=======
.. automodule:: telethon.tl.custom.qrlogin
:members:
:undoc-members:
:show-inheritance:
SenderGetter
============
.. automodule:: telethon.tl.custom.sendergetter
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,20 @@
.. _telethon-errors:
==========
API Errors
==========
These are the base errors that Telegram's API may raise.
See :ref:`rpc-errors` for a more in-depth explanation on how to handle all
known possible errors and learning to determine what a method may raise.
.. automodule:: telethon.errors.common
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.errors.rpcbaseerrors
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,9 +1,12 @@
.. _telethon-events-package:
.. _telethon-events:
telethon\.events package
========================
=============
Update Events
=============
Every event (builder) subclasses `telethon.events.common.EventBuilder`,
.. currentmodule:: telethon.events
Every event (builder) subclasses `common.EventBuilder`,
so all the methods in it can be used from any event builder/event instance.
.. automodule:: telethon.events.common
@ -11,62 +14,56 @@ so all the methods in it can be used from any event builder/event instance.
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.newmessage
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.chataction
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.userupdate
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.messageedited
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.messagedeleted
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.messageread
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.callbackquery
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.inlinequery
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.album
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events.raw
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.events
:members:
:undoc-members:

View File

@ -0,0 +1,8 @@
=======
Helpers
=======
.. automodule:: telethon.helpers
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,33 @@
.. _telethon-network:
================
Connection Modes
================
The only part about network that you should worry about are
the different connection modes, which are the following:
.. automodule:: telethon.network.connection.tcpfull
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.network.connection.tcpabridged
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.network.connection.tcpintermediate
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.network.connection.tcpobfuscated
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.network.connection.http
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,27 @@
.. _telethon-sessions:
========
Sessions
========
These are the different built-in session storage that you may subclass.
.. automodule:: telethon.sessions.abstract
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.sessions.memory
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.sessions.sqlite
:members:
:undoc-members:
:show-inheritance:
.. automodule:: telethon.sessions.string
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,12 @@
.. _telethon-utils:
=========
Utilities
=========
These are the utilities that the library has to offer.
.. automodule:: telethon.utils
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,202 @@
.. _client-ref:
================
Client Reference
================
This page contains a summary of all the important methods and properties that
you may need when using Telethon. They are sorted by relevance and are not in
alphabetical order.
You should use this page to learn about which methods are available, and
if you need a usage example or further description of the arguments, be
sure to follow the links.
.. contents::
TelegramClient
==============
This is a summary of the methods and
properties you will find at :ref:`telethon-client`.
Auth
----
.. currentmodule:: telethon.client.auth.AuthMethods
.. autosummary::
:nosignatures:
start
send_code_request
sign_in
qr_login
log_out
edit_2fa
Base
----
.. py:currentmodule:: telethon.client.telegrambaseclient.TelegramBaseClient
.. autosummary::
:nosignatures:
connect
disconnect
is_connected
disconnected
loop
set_proxy
Messages
--------
.. py:currentmodule:: telethon.client.messages.MessageMethods
.. autosummary::
:nosignatures:
send_message
edit_message
delete_messages
forward_messages
iter_messages
get_messages
pin_message
unpin_message
send_read_acknowledge
Uploads
-------
.. py:currentmodule:: telethon.client.uploads.UploadMethods
.. autosummary::
:nosignatures:
send_file
upload_file
Downloads
---------
.. currentmodule:: telethon.client.downloads.DownloadMethods
.. autosummary::
:nosignatures:
download_media
download_profile_photo
download_file
iter_download
Dialogs
-------
.. py:currentmodule:: telethon.client.dialogs.DialogMethods
.. autosummary::
:nosignatures:
iter_dialogs
get_dialogs
edit_folder
iter_drafts
get_drafts
delete_dialog
conversation
Users
-----
.. py:currentmodule:: telethon.client.users.UserMethods
.. autosummary::
:nosignatures:
get_me
is_bot
is_user_authorized
get_entity
get_input_entity
get_peer_id
Chats
-----
.. currentmodule:: telethon.client.chats.ChatMethods
.. autosummary::
:nosignatures:
iter_participants
get_participants
kick_participant
iter_admin_log
get_admin_log
iter_profile_photos
get_profile_photos
edit_admin
edit_permissions
get_permissions
get_stats
action
Parse Mode
----------
.. py:currentmodule:: telethon.client.messageparse.MessageParseMethods
.. autosummary::
:nosignatures:
parse_mode
Updates
-------
.. py:currentmodule:: telethon.client.updates.UpdateMethods
.. autosummary::
:nosignatures:
on
run_until_disconnected
add_event_handler
remove_event_handler
list_event_handlers
catch_up
set_receive_updates
Bots
----
.. currentmodule:: telethon.client.bots.BotMethods
.. autosummary::
:nosignatures:
inline_query
Buttons
-------
.. currentmodule:: telethon.client.buttons.ButtonMethods
.. autosummary::
:nosignatures:
build_reply_markup
Account
-------
.. currentmodule:: telethon.client.account.AccountMethods
.. autosummary::
:nosignatures:
takeout
end_takeout

View File

@ -0,0 +1,247 @@
================
Events Reference
================
Here you will find a quick summary of all the methods
and properties that you can access when working with events.
You can access the client that creates this event by doing
``event.client``, and you should view the description of the
events to find out what arguments it allows on creation and
its **attributes** (the properties will be shown here).
.. important::
Remember that **all events base** `ChatGetter
<telethon.tl.custom.chatgetter.ChatGetter>`! Please see :ref:`faq`
if you don't know what this means or the implications of it.
.. contents::
NewMessage
==========
Occurs whenever a new text message or a message with media arrives.
.. note::
The new message event **should be treated as** a
normal `Message <telethon.tl.custom.message.Message>`, with
the following exceptions:
* ``pattern_match`` is the match object returned by ``pattern=``.
* ``message`` is **not** the message string. It's the `Message
<telethon.tl.custom.message.Message>` object.
Remember, this event is just a proxy over the message, so while
you won't see its attributes and properties, you can still access
them. Please see the full documentation for examples.
Full documentation for the `NewMessage
<telethon.events.newmessage.NewMessage>`.
MessageEdited
=============
Occurs whenever a message is edited. Just like `NewMessage
<telethon.events.newmessage.NewMessage>`, you should treat
this event as a `Message <telethon.tl.custom.message.Message>`.
Full documentation for the `MessageEdited
<telethon.events.messageedited.MessageEdited>`.
MessageDeleted
==============
Occurs whenever a message is deleted. Note that this event isn't 100%
reliable, since Telegram doesn't always notify the clients that a message
was deleted.
It only has the ``deleted_id`` and ``deleted_ids`` attributes
(in addition to the chat if the deletion happened in a channel).
Full documentation for the `MessageDeleted
<telethon.events.messagedeleted.MessageDeleted>`.
MessageRead
===========
Occurs whenever one or more messages are read in a chat.
Full documentation for the `MessageRead
<telethon.events.messageread.MessageRead>`.
.. currentmodule:: telethon.events.messageread.MessageRead.Event
.. autosummary::
:nosignatures:
inbox
message_ids
get_messages
is_read
ChatAction
==========
Occurs on certain chat actions, such as chat title changes,
user join or leaves, pinned messages, photo changes, etc.
Full documentation for the `ChatAction
<telethon.events.chataction.ChatAction>`.
.. currentmodule:: telethon.events.chataction.ChatAction.Event
.. autosummary::
:nosignatures:
added_by
kicked_by
user
input_user
user_id
users
input_users
user_ids
respond
reply
delete
get_pinned_message
get_added_by
get_kicked_by
get_user
get_input_user
get_users
get_input_users
UserUpdate
==========
Occurs whenever a user goes online, starts typing, etc.
Full documentation for the `UserUpdate
<telethon.events.userupdate.UserUpdate>`.
.. currentmodule:: telethon.events.userupdate.UserUpdate.Event
.. autosummary::
:nosignatures:
user
input_user
user_id
get_user
get_input_user
typing
uploading
recording
playing
cancel
geo
audio
round
video
contact
document
photo
last_seen
until
online
recently
within_weeks
within_months
CallbackQuery
=============
Occurs whenever you sign in as a bot and a user
clicks one of the inline buttons on your messages.
Full documentation for the `CallbackQuery
<telethon.events.callbackquery.CallbackQuery>`.
.. currentmodule:: telethon.events.callbackquery.CallbackQuery.Event
.. autosummary::
:nosignatures:
id
message_id
data
chat_instance
via_inline
respond
reply
edit
delete
answer
get_message
InlineQuery
===========
Occurs whenever you sign in as a bot and a user
sends an inline query such as ``@bot query``.
Full documentation for the `InlineQuery
<telethon.events.inlinequery.InlineQuery>`.
.. currentmodule:: telethon.events.inlinequery.InlineQuery.Event
.. autosummary::
:nosignatures:
id
text
offset
geo
builder
answer
Album
=====
Occurs whenever you receive an entire album.
Full documentation for the `Album
<telethon.events.album.Album>`.
.. currentmodule:: telethon.events.album.Album.Event
.. autosummary::
:nosignatures:
grouped_id
text
raw_text
is_reply
forward
get_reply_message
respond
reply
forward_to
edit
delete
mark_read
pin
Raw
===
Raw events are not actual events. Instead, they are the raw
:tl:`Update` object that Telegram sends. You normally shouldn't
need these.

View File

@ -0,0 +1,423 @@
.. _faq:
===
FAQ
===
Let's start the quick references section with some useful tips to keep in
mind, with the hope that you will understand why certain things work the
way that they do.
.. contents::
Code without errors doesn't work
================================
Then it probably has errors, but you haven't enabled logging yet.
To enable logging, at the following code to the top of your main file:
.. code-block:: python
import logging
logging.basicConfig(format='[%(levelname) %(asctime)s] %(name)s: %(message)s',
level=logging.WARNING)
You can change the logging level to be something different, from less to more information:
.. code-block:: python
level=logging.CRITICAL # won't show errors (same as disabled)
level=logging.ERROR # will only show errors that you didn't handle
level=logging.WARNING # will also show messages with medium severity, such as internal Telegram issues
level=logging.INFO # will also show informational messages, such as connection or disconnections
level=logging.DEBUG # will show a lot of output to help debugging issues in the library
See the official Python documentation for more information on logging_.
How can I except FloodWaitError?
================================
You can use all errors from the API by importing:
.. code-block:: python
from telethon import errors
And except them as such:
.. code-block:: python
try:
await client.send_message(chat, 'Hi')
except errors.FloodWaitError as e:
# e.seconds is how many seconds you have
# to wait before making the request again.
print('Flood for', e.seconds)
My account was deleted/limited when using the library
=====================================================
First and foremost, **this is not a problem exclusive to Telethon.
Any third-party library is prone to cause the accounts to appear banned.**
Even official applications can make Telegram ban an account under certain
circumstances. Third-party libraries such as Telethon are a lot easier to
use, and as such, they are misused to spam, which causes Telegram to learn
certain patterns and ban suspicious activity.
There is no point in Telethon trying to circumvent this. Even if it succeeded,
spammers would then abuse the library again, and the cycle would repeat.
The library will only do things that you tell it to do. If you use
the library with bad intentions, Telegram will hopefully ban you.
However, you may also be part of a limited country, such as Iran or Russia.
In that case, we have bad news for you. Telegram is much more likely to ban
these numbers, as they are often used to spam other accounts, likely through
the use of libraries like this one. The best advice we can give you is to not
abuse the API, like calling many requests really quickly.
We have also had reports from Kazakhstan and China, where connecting
would fail. To solve these connection problems, you should use a proxy.
Telegram may also ban virtual (VoIP) phone numbers,
as again, they're likely to be used for spam.
More recently (year 2023 onwards), Telegram has started putting a lot more
measures to prevent spam (with even additions such as anonymous participants
in groups or the inability to fetch group members at all). This means some
of the anti-spam measures have gotten more aggressive.
The recommendation has usually been to use the library only on well-established
accounts (and not an account you just created), and to not perform actions that
could be seen as abuse. Telegram decides what those actions are, and they're
free to change how they operate at any time.
If you want to check if your account has been limited,
simply send a private message to `@SpamBot`_ through Telegram itself.
You should notice this by getting errors like ``PeerFloodError``,
which means you're limited, for instance,
when sending a message to some accounts but not others.
For more discussion, please see `issue 297`_.
How can I use a proxy?
======================
This was one of the first things described in :ref:`signing-in`.
How do I access a field?
========================
This is basic Python knowledge. You should use the dot operator:
.. code-block:: python
me = await client.get_me()
print(me.username)
# ^ we used the dot operator to access the username attribute
result = await client(functions.photos.GetUserPhotosRequest(
user_id='me',
offset=0,
max_id=0,
limit=100
))
# Working with list is also pretty basic
print(result.photos[0].sizes[-1].type)
# ^ ^ ^ ^ ^
# | | | | \ type
# | | | \ last size
# | | \ list of sizes
# access | \ first photo from the list
# the... \ list of photos
#
# To print all, you could do (or mix-and-match):
for photo in result.photos:
for size in photo.sizes:
print(size.type)
AttributeError: 'coroutine' object has no attribute 'id'
========================================================
You either forgot to:
.. code-block:: python
import telethon.sync
# ^^^^^ import sync
Or:
.. code-block:: python
async def handler(event):
me = await client.get_me()
# ^^^^^ note the await
print(me.username)
sqlite3.OperationalError: database is locked
============================================
An older process is still running and is using the same ``'session'`` file.
This error occurs when **two or more clients use the same session**,
that is, when you write the same session name to be used in the client:
* You have an older process using the same session file.
* You have two different scripts running (interactive sessions count too).
* You have two clients in the same script running at the same time.
The solution is, if you need two clients, use two sessions. If the
problem persists and you're on Linux, you can use ``fuser my.session``
to find out the process locking the file. As a last resort, you can
reboot your system.
If you really dislike SQLite, use a different session storage. There
is an entire section covering that at :ref:`sessions`.
event.chat or event.sender is None
==================================
Telegram doesn't always send this information in order to save bandwidth.
If you need the information, you should fetch it yourself, since the library
won't do unnecessary work unless you need to:
.. code-block:: python
async def handler(event):
chat = await event.get_chat()
sender = await event.get_sender()
File download is slow or sending files takes too long
=====================================================
The communication with Telegram is encrypted. Encryption requires a lot of
math, and doing it in pure Python is very slow. ``cryptg`` is a library which
containns the encryption functions used by Telethon. If it is installed (via
``pip install cryptg``), it will automatically be used and should provide
a considerable speed boost. You can know whether it's used by configuring
``logging`` (at ``INFO`` level or lower) *before* importing ``telethon``.
Note that the library does *not* download or upload files in parallel, which
can also help with the speed of downloading or uploading a single file. There
are snippets online implementing that. The reason why this is not built-in
is because the limiting factor in the long run are ``FloodWaitError``, and
using parallel download or uploads only makes them occur sooner.
What does "Server sent a very new message with ID" mean?
========================================================
You may also see this error as "Server sent a very old message with ID".
This is a security feature from Telethon that cannot be disabled and is
meant to protect you against replay attacks.
When this message is incorrectly reported as a "bug",
the most common patterns seem to be:
* Your system time is incorrect.
* The proxy you're using may be interfering somehow.
* The Telethon session is being used or has been used from somewhere else.
Make sure that you created the session from Telethon, and are not using the
same session anywhere else. If you need to use the same account from
multiple places, login and use a different session for each place you need.
What does "Server replied with a wrong session ID" mean?
========================================================
This is a security feature from Telethon that cannot be disabled and is
meant to protect you against unwanted session reuse.
When this message is reported as a "bug", the most common patterns seem to be:
* The proxy you're using may be interfering somehow.
* The Telethon session is being used or has been used from somewhere else.
Make sure that you created the session from Telethon, and are not using the
same session anywhere else. If you need to use the same account from
multiple places, login and use a different session for each place you need.
* You may be using multiple connections to the Telegram server, which seems
to confuse Telegram.
Most of the time it should be safe to ignore this warning. If the library
still doesn't behave correctly, make sure to check if any of the above bullet
points applies in your case and try to work around it.
If the issue persists and there is a way to reliably reproduce this error,
please add a comment with any additional details you can provide to
`issue 3759`_, and perhaps some additional investigation can be done
(but it's unlikely, as Telegram *is* sending unexpected data).
What does "Could not find a matching Constructor ID for the TLObject" mean?
===========================================================================
Telegram uses "layers", which you can think of as "versions" of the API they
offer. When Telethon reads responses that the Telegram servers send, these
need to be deserialized (into what Telethon calls "TLObjects").
Every Telethon version understands a single Telegram layer. When Telethon
connects to Telegram, both agree on the layer to use. If the layers don't
match, Telegram may send certain objects which Telethon no longer understands.
When this message is reported as a "bug", the most common patterns seem to be
that the Telethon session is being used or has been used from somewhere else.
Make sure that you created the session from Telethon, and are not using the
same session anywhere else. If you need to use the same account from
multiple places, login and use a different session for each place you need.
What does "Task was destroyed but it is pending" mean?
======================================================
Your script likely finished abruptly, the ``asyncio`` event loop got
destroyed, and the library did not get a chance to properly close the
connection and close the session.
Make sure you're either using the context manager for the client or always
call ``await client.disconnect()`` (by e.g. using a ``try/finally``).
What does "The asyncio event loop must not change after connection" mean?
=========================================================================
Telethon uses ``asyncio``, and makes use of things like tasks and queues
internally to manage the connection to the server and match responses to the
requests you make. Most of them are initialized after the client is connected.
For example, if the library expects a result to a request made in loop A, but
you attempt to get that result in loop B, you will very likely find a deadlock.
To avoid a deadlock, the library checks to make sure the loop in use is the
same as the one used to initialize everything, and if not, it throws an error.
The most common cause is ``asyncio.run``, since it creates a new event loop.
If you ``asyncio.run`` a function to create the client and set it up, and then
you ``asyncio.run`` another function to do work, things won't work, so the
library throws an error early to let you know something is wrong.
Instead, it's often a good idea to have a single ``async def main`` and simply
``asyncio.run()`` it and do all the work there. From it, you're also able to
call other ``async def`` without having to touch ``asyncio.run`` again:
.. code-block:: python
# It's fine to create the client outside as long as you don't connect
client = TelegramClient(...)
async def main():
# Now the client will connect, so the loop must not change from now on.
# But as long as you do all the work inside main, including calling
# other async functions, things will work.
async with client:
....
if __name__ == '__main__':
asyncio.run(main())
Be sure to read the ``asyncio`` documentation if you want a better
understanding of event loop, tasks, and what functions you can use.
What does "bases ChatGetter" mean?
==================================
In Python, classes can base others. This is called `inheritance
<https://ddg.gg/python%20inheritance>`_. What it means is that
"if a class bases another, you can use the other's methods too".
For example, `Message <telethon.tl.custom.message.Message>` *bases*
`ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`. In turn,
`ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>` defines
things like `obj.chat_id <telethon.tl.custom.chatgetter.ChatGetter>`.
So if you have a message, you can access that too:
.. code-block:: python
# ChatGetter has a chat_id property, and Message bases ChatGetter.
# Thus you can use ChatGetter properties and methods from Message
print(message.chat_id)
Telegram has a lot to offer, and inheritance helps the library reduce
boilerplate, so it's important to know this concept. For newcomers,
this may be a problem, so we explain what it means here in the FAQ.
Can I send files by ID?
=======================
When people talk about IDs, they often refer to one of two things:
the integer ID inside media, and a random-looking long string.
You cannot use the integer ID to send media. Generally speaking, sending media
requires a combination of ID, ``access_hash`` and ``file_reference``.
The first two are integers, while the last one is a random ``bytes`` sequence.
* The integer ``id`` will always be the same for every account, so every user
or bot looking at a particular media file, will see a consistent ID.
* The ``access_hash`` will always be the same for a given account, but
different accounts will each see their own, different ``access_hash``.
This makes it impossible to get media object from one account and use it in
another. The other account must fetch the media object itself.
* The ``file_reference`` is random for everyone and will only work for a few
hours before it expires. It must be refetched before the media can be used
(to either resend the media or download it).
The second type of "`file ID <https://core.telegram.org/bots/api#inputfile>`_"
people refer to is a concept from the HTTP Bot API. It's a custom format which
encodes enough information to use the media.
Telethon provides an old version of these HTTP Bot API-style file IDs via
``message.file.id``, however, this feature is no longer maintained, so it may
not work. It will be removed in future versions. Nonetheless, it is possible
to find a different Python package (or write your own) to parse these file IDs
and construct the necessary input file objects to send or download the media.
Can I use Flask with the library?
=================================
Yes, if you know what you are doing. However, you will probably have a
lot of headaches to get threads and asyncio to work together. Instead,
consider using `Quart <https://pgjones.gitlab.io/quart/>`_, an asyncio-based
alternative to `Flask <flask.pocoo.org/>`_.
Check out `quart_login.py`_ for an example web-application based on Quart.
Can I use Anaconda/Spyder/IPython with the library?
===================================================
Yes, but these interpreters run the asyncio event loop implicitly,
which interferes with the ``telethon.sync`` magic module.
If you use them, you should **not** import ``sync``:
.. code-block:: python
# Change any of these...:
from telethon import TelegramClient, sync, ...
from telethon.sync import TelegramClient, ...
# ...with this:
from telethon import TelegramClient, ...
You are also more likely to get "sqlite3.OperationalError: database is locked"
with them. If they cause too much trouble, just write your code in a ``.py``
file and run that, or use the normal ``python`` interpreter.
.. _logging: https://docs.python.org/3/library/logging.html
.. _@SpamBot: https://t.me/SpamBot
.. _issue 297: https://github.com/LonamiWebs/Telethon/issues/297
.. _issue 3759: https://github.com/LonamiWebs/Telethon/issues/3759
.. _quart_login.py: https://github.com/LonamiWebs/Telethon/tree/v1/telethon_examples#quart_loginpy

View File

@ -0,0 +1,353 @@
=================
Objects Reference
=================
This is the quick reference for those objects returned by client methods
or other useful modules that the library has to offer. They are kept in
a separate page to help finding and discovering them.
Remember that this page only shows properties and methods,
**not attributes**. Make sure to open the full documentation
to find out about the attributes.
.. contents::
ChatGetter
==========
All events base `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`,
and some of the objects below do too, so it's important to know its methods.
.. currentmodule:: telethon.tl.custom.chatgetter.ChatGetter
.. autosummary::
:nosignatures:
chat
input_chat
chat_id
is_private
is_group
is_channel
get_chat
get_input_chat
SenderGetter
============
Similar to `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`, a
`SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>` is the same,
but it works for senders instead.
.. currentmodule:: telethon.tl.custom.sendergetter.SenderGetter
.. autosummary::
:nosignatures:
sender
input_sender
sender_id
get_sender
get_input_sender
Message
=======
.. currentmodule:: telethon.tl.custom.message
The `Message` type is very important, mostly because we are working
with a library for a *messaging* platform, so messages are widely used:
in events, when fetching history, replies, etc.
It bases `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>` and
`SenderGetter <telethon.tl.custom.sendergetter.SenderGetter>`.
Properties
----------
.. note::
We document *custom properties* here, not all the attributes of the
`Message` (which is the information Telegram actually returns).
.. currentmodule:: telethon.tl.custom.message.Message
.. autosummary::
:nosignatures:
text
raw_text
is_reply
forward
buttons
button_count
file
photo
document
web_preview
audio
voice
video
video_note
gif
sticker
contact
game
geo
invoice
poll
venue
action_entities
via_bot
via_input_bot
client
Methods
-------
.. autosummary::
:nosignatures:
respond
reply
forward_to
edit
delete
get_reply_message
click
mark_read
pin
download_media
get_entities_text
get_buttons
File
====
The `File <telethon.tl.custom.file.File>` type is a wrapper object
returned by `Message.file <telethon.tl.custom.message.Message.file>`,
and you can use it to easily access a document's attributes, such as
its name, bot-API style file ID, etc.
.. currentmodule:: telethon.tl.custom.file.File
.. autosummary::
:nosignatures:
id
name
ext
mime_type
width
height
size
duration
title
performer
emoji
sticker_set
Conversation
============
The `Conversation <telethon.tl.custom.conversation.Conversation>` object
is returned by the `client.conversation()
<telethon.client.dialogs.DialogMethods.conversation>` method to easily
send and receive responses like a normal conversation.
It bases `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`.
.. currentmodule:: telethon.tl.custom.conversation.Conversation
.. autosummary::
:nosignatures:
send_message
send_file
mark_read
get_response
get_reply
get_edit
wait_read
wait_event
cancel
cancel_all
AdminLogEvent
=============
The `AdminLogEvent <telethon.tl.custom.adminlogevent.AdminLogEvent>` object
is returned by the `client.iter_admin_log()
<telethon.client.chats.ChatMethods.iter_admin_log>` method to easily iterate
over past "events" (deleted messages, edits, title changes, leaving members…)
These are all the properties you can find in it:
.. currentmodule:: telethon.tl.custom.adminlogevent.AdminLogEvent
.. autosummary::
:nosignatures:
id
date
user_id
action
old
new
changed_about
changed_title
changed_username
changed_photo
changed_sticker_set
changed_message
deleted_message
changed_admin
changed_restrictions
changed_invites
joined
joined_invite
left
changed_hide_history
changed_signatures
changed_pin
changed_default_banned_rights
stopped_poll
Button
======
The `Button <telethon.tl.custom.button.Button>` class is used when you login
as a bot account to send messages with reply markup, such as inline buttons
or custom keyboards.
These are the static methods you can use to create instances of the markup:
.. currentmodule:: telethon.tl.custom.button.Button
.. autosummary::
:nosignatures:
inline
switch_inline
url
auth
text
request_location
request_phone
request_poll
clear
force_reply
InlineResult
============
The `InlineResult <telethon.tl.custom.inlineresult.InlineResult>` object
is returned inside a list by the `client.inline_query()
<telethon.client.bots.BotMethods.inline_query>` method to make an inline
query to a bot that supports being used in inline mode, such as
`@like <https://t.me/like>`_.
Note that the list returned is in fact a *subclass* of a list called
`InlineResults <telethon.tl.custom.inlineresults.InlineResults>`, which,
in addition of being a list (iterator, indexed access, etc.), has extra
attributes and methods.
These are the constants for the types, properties and methods you
can find the individual results:
.. currentmodule:: telethon.tl.custom.inlineresult.InlineResult
.. autosummary::
:nosignatures:
ARTICLE
PHOTO
GIF
VIDEO
VIDEO_GIF
AUDIO
DOCUMENT
LOCATION
VENUE
CONTACT
GAME
type
message
title
description
url
photo
document
click
download_media
Dialog
======
The `Dialog <telethon.tl.custom.dialog.Dialog>` object is returned when
you call `client.iter_dialogs() <telethon.client.dialogs.DialogMethods.iter_dialogs>`.
.. currentmodule:: telethon.tl.custom.dialog.Dialog
.. autosummary::
:nosignatures:
send_message
archive
delete
Draft
======
The `Draft <telethon.tl.custom.draft.Draft>` object is returned when
you call `client.iter_drafts() <telethon.client.dialogs.DialogMethods.iter_drafts>`.
.. currentmodule:: telethon.tl.custom.draft.Draft
.. autosummary::
:nosignatures:
entity
input_entity
get_entity
get_input_entity
text
raw_text
is_empty
set_message
send
delete
Utils
=====
The `telethon.utils` module has plenty of methods that make using the
library a lot easier. Only the interesting ones will be listed here.
.. currentmodule:: telethon.utils
.. autosummary::
:nosignatures:
get_display_name
get_extension
get_inner_text
get_peer_id
resolve_id
pack_bot_file_id
resolve_bot_file_id
resolve_invite_link

View File

@ -1 +1,2 @@
telethon
./
sphinx-rtd-theme~=1.3.0

View File

@ -1,22 +0,0 @@
.. _telethon-errors-package:
telethon\.errors package
========================
telethon\.errors\.common module
-------------------------------
.. automodule:: telethon.errors.common
:members:
:undoc-members:
:show-inheritance:
telethon\.errors\.rpcbaseerrors module
--------------------------------------
.. automodule:: telethon.errors.rpcbaseerrors
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,35 +0,0 @@
telethon\.extensions package
============================
telethon\.extensions\.binaryreader module
-----------------------------------------
.. automodule:: telethon.extensions.binaryreader
:members:
:undoc-members:
:show-inheritance:
telethon\.extensions\.markdown module
-------------------------------------
.. automodule:: telethon.extensions.markdown
:members:
:undoc-members:
:show-inheritance:
telethon\.extensions\.html module
---------------------------------
.. automodule:: telethon.extensions.html
:members:
:undoc-members:
:show-inheritance:
telethon\.extensions\.tcpclient module
--------------------------------------
.. automodule:: telethon.extensions.tcpclient
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,35 +0,0 @@
telethon\.network package
=========================
telethon\.network\.connection module
------------------------------------
.. automodule:: telethon.network.connection
:members:
:undoc-members:
:show-inheritance:
telethon\.network\.mtprotoplainsender module
------------------------------------------------
.. automodule:: telethon.network.mtprotoplainsender
:members:
:undoc-members:
:show-inheritance:
telethon\.network\.mtprotosender module
-----------------------------------------
.. automodule:: telethon.network.mtprotosender
:members:
:undoc-members:
:show-inheritance:
telethon\.network\.authenticator module
---------------------------------------
.. automodule:: telethon.network.authenticator
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,90 +0,0 @@
.. _telethon-package:
telethon package
================
telethon\.client module
-----------------------
.. toctree::
telethon.client
.. automodule:: telethon.client
:members:
:undoc-members:
:show-inheritance:
telethon\.utils module
----------------------
.. automodule:: telethon.utils
:members:
:undoc-members:
:show-inheritance:
telethon\.helpers module
------------------------
.. automodule:: telethon.helpers
:members:
:undoc-members:
:show-inheritance:
telethon\.events package
------------------------
.. toctree::
telethon.events
telethon\.sessions module
-------------------------
.. automodule:: telethon.sessions
:members:
:undoc-members:
:show-inheritance:
telethon\.errors package
------------------------
.. toctree::
telethon.errors
telethon\.extensions package
----------------------------
.. toctree::
telethon.extensions
telethon\.network package
-------------------------
.. toctree::
telethon.network
telethon\.tl package
--------------------
.. toctree::
telethon.tl
Module contents
---------------
.. automodule:: telethon
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,87 +0,0 @@
telethon\.tl\.custom package
============================
telethon\.tl\.custom\.draft module
----------------------------------
.. automodule:: telethon.tl.custom.draft
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.dialog module
-----------------------------------
.. automodule:: telethon.tl.custom.dialog
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.message module
------------------------------------
.. automodule:: telethon.tl.custom.message
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.messagebutton module
------------------------------------------
.. automodule:: telethon.tl.custom.messagebutton
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.forward module
------------------------------------
.. automodule:: telethon.tl.custom.forward
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.button module
-----------------------------------
.. automodule:: telethon.tl.custom.button
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.inlineresult module
-----------------------------------------
.. automodule:: telethon.tl.custom.inlineresult
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.chatgetter module
---------------------------------------
.. automodule:: telethon.tl.custom.chatgetter
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.sendergetter module
-----------------------------------------
.. automodule:: telethon.tl.custom.sendergetter
:members:
:undoc-members:
:show-inheritance:
telethon\.tl\.custom\.conversation module
-----------------------------------------
.. automodule:: telethon.tl.custom.conversation
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,16 +0,0 @@
telethon\.tl\.custom package
============================
.. toctree::
telethon.tl.custom
telethon\.tl\.tlobject module
-----------------------------
.. automodule:: telethon.tl.tlobject
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,3 +1,2 @@
pyaes
rsa
async_generator

View File

@ -1,24 +0,0 @@
#!/usr/bin/env python3
import unittest
if __name__ == '__main__':
from telethon_tests import \
CryptoTests, ParserTests, TLTests, UtilsTests, NetworkTests
test_classes = [CryptoTests, ParserTests, TLTests, UtilsTests]
network = input('Run network tests (y/n)?: ').lower() == 'y'
if network:
test_classes.append(NetworkTests)
loader = unittest.TestLoader()
suites_list = []
for test_class in test_classes:
suite = loader.loadTestsFromTestCase(test_class)
suites_list.append(suite)
big_suite = unittest.TestSuite(suites_list)
runner = unittest.TextTestRunner()
results = runner.run(big_suite)

163
setup.py
View File

@ -15,68 +15,75 @@ import json
import os
import re
import shutil
from codecs import open
from sys import argv
import sys
import urllib.request
from pathlib import Path
from subprocess import run
from setuptools import find_packages, setup
# Needed since we're importing local files
sys.path.insert(0, os.path.dirname(__file__))
class TempWorkDir:
"""Switches the working directory to be the one on which this file lives,
while within the 'with' block.
"""
def __init__(self):
def __init__(self, new=None):
self.original = None
self.new = new or str(Path(__file__).parent.resolve())
def __enter__(self):
self.original = os.path.abspath(os.path.curdir)
os.chdir(os.path.abspath(os.path.dirname(__file__)))
# os.chdir does not work with Path in Python 3.5.x
self.original = str(Path('.').resolve())
os.makedirs(self.new, exist_ok=True)
os.chdir(self.new)
return self
def __exit__(self, *args):
os.chdir(self.original)
GENERATOR_DIR = 'telethon_generator'
LIBRARY_DIR = 'telethon'
API_REF_URL = 'https://tl.telethon.dev/'
ERRORS_IN_JSON = os.path.join(GENERATOR_DIR, 'data', 'errors.json')
ERRORS_IN_DESC = os.path.join(GENERATOR_DIR, 'data', 'error_descriptions')
ERRORS_OUT = os.path.join(LIBRARY_DIR, 'errors', 'rpcerrorlist.py')
GENERATOR_DIR = Path('telethon_generator')
LIBRARY_DIR = Path('telethon')
INVALID_BM_IN = os.path.join(GENERATOR_DIR, 'data', 'invalid_bot_methods.json')
ERRORS_IN = GENERATOR_DIR / 'data/errors.csv'
ERRORS_OUT = LIBRARY_DIR / 'errors/rpcerrorlist.py'
TLOBJECT_IN_CORE_TL = os.path.join(GENERATOR_DIR, 'data', 'mtproto_api.tl')
TLOBJECT_IN_TL = os.path.join(GENERATOR_DIR, 'data', 'telegram_api.tl')
TLOBJECT_OUT = os.path.join(LIBRARY_DIR, 'tl')
METHODS_IN = GENERATOR_DIR / 'data/methods.csv'
# Which raw API methods are covered by *friendly* methods in the client?
FRIENDLY_IN = GENERATOR_DIR / 'data/friendly.csv'
TLOBJECT_IN_TLS = [Path(x) for x in GENERATOR_DIR.glob('data/*.tl')]
TLOBJECT_OUT = LIBRARY_DIR / 'tl'
IMPORT_DEPTH = 2
DOCS_IN_RES = os.path.join(GENERATOR_DIR, 'data', 'html')
DOCS_OUT = 'docs'
DOCS_IN_RES = GENERATOR_DIR / 'data/html'
DOCS_OUT = Path('docs')
def generate(which):
from telethon_generator.parsers import parse_errors, parse_tl, find_layer
def generate(which, action='gen'):
from telethon_generator.parsers import\
parse_errors, parse_methods, parse_tl, find_layer
from telethon_generator.generators import\
generate_errors, generate_tlobjects, generate_docs, clean_tlobjects
# Older Python versions open the file as bytes instead (3.4.2)
with open(INVALID_BM_IN, 'r') as f:
invalid_bot_methods = set(json.load(f))
layer = next(filter(None, map(find_layer, TLOBJECT_IN_TLS)))
errors = list(parse_errors(ERRORS_IN))
methods = list(parse_methods(METHODS_IN, FRIENDLY_IN, {e.str_code: e for e in errors}))
layer = find_layer(TLOBJECT_IN_TL)
errors = list(parse_errors(ERRORS_IN_JSON, ERRORS_IN_DESC))
tlobjects = list(itertools.chain(
parse_tl(TLOBJECT_IN_CORE_TL, layer, invalid_bot_methods),
parse_tl(TLOBJECT_IN_TL, layer, invalid_bot_methods)))
tlobjects = list(itertools.chain(*(
parse_tl(file, layer, methods) for file in TLOBJECT_IN_TLS)))
if not which:
which.extend(('tl', 'errors'))
clean = 'clean' in which
clean = action == 'clean'
action = 'Cleaning' if clean else 'Generating'
if clean:
which.remove('clean')
if 'all' in which:
which.remove('all')
@ -96,84 +103,105 @@ def generate(which):
which.remove('errors')
print(action, 'RPCErrors...')
if clean:
if os.path.isfile(ERRORS_OUT):
os.remove(ERRORS_OUT)
if ERRORS_OUT.is_file():
ERRORS_OUT.unlink()
else:
with open(ERRORS_OUT, 'w', encoding='utf-8') as file:
with ERRORS_OUT.open('w') as file:
generate_errors(errors, file)
if 'docs' in which:
which.remove('docs')
print(action, 'documentation...')
if clean:
if os.path.isdir(DOCS_OUT):
shutil.rmtree(DOCS_OUT)
if DOCS_OUT.is_dir():
shutil.rmtree(str(DOCS_OUT))
else:
generate_docs(tlobjects, errors, layer, DOCS_IN_RES, DOCS_OUT)
in_path = DOCS_IN_RES.resolve()
with TempWorkDir(DOCS_OUT):
generate_docs(tlobjects, methods, layer, in_path)
if 'json' in which:
which.remove('json')
print(action, 'JSON schema...')
mtproto = 'mtproto_api.json'
telegram = 'telegram_api.json'
json_files = [x.with_suffix('.json') for x in TLOBJECT_IN_TLS]
if clean:
for x in (mtproto, telegram):
if os.path.isfile(x):
os.remove(x)
for file in json_files:
if file.is_file():
file.unlink()
else:
def gen_json(fin, fout):
methods = []
meths = []
constructors = []
for tl in parse_tl(fin, layer):
if tl.is_function:
methods.append(tl.to_dict())
meths.append(tl.to_dict())
else:
constructors.append(tl.to_dict())
what = {'constructors': constructors, 'methods': methods}
what = {'constructors': constructors, 'methods': meths}
with open(fout, 'w') as f:
json.dump(what, f, indent=2)
gen_json(TLOBJECT_IN_CORE_TL, mtproto)
gen_json(TLOBJECT_IN_TL, telegram)
for fs in zip(TLOBJECT_IN_TLS, json_files):
gen_json(*fs)
if which:
print('The following items were not understood:', which)
print(' Consider using only "tl", "errors" and/or "docs".')
print(' Using only "clean" will clean them. "all" to act on all.')
print(' For instance "gen tl errors".')
print(
'The following items were not understood:', which,
'\n Consider using only "tl", "errors" and/or "docs".'
'\n Using only "clean" will clean them. "all" to act on all.'
'\n For instance "gen tl errors".'
)
def main():
if len(argv) >= 2 and argv[1] == 'gen':
generate(argv[2:])
def main(argv):
if len(argv) >= 2 and argv[1] in ('gen', 'clean'):
generate(argv[2:], argv[1])
elif len(argv) >= 2 and argv[1] == 'pypi':
# Make sure tl.telethon.dev is up-to-date first
with urllib.request.urlopen(API_REF_URL) as resp:
html = resp.read()
m = re.search(br'layer\s+(\d+)', html)
if not m:
print('Failed to check that the API reference is up to date:', API_REF_URL)
return
from telethon_generator.parsers import find_layer
layer = next(filter(None, map(find_layer, TLOBJECT_IN_TLS)))
published_layer = int(m[1])
if published_layer != layer:
print('Published layer', published_layer, 'does not match current layer', layer, '.')
print('Make sure to update the API reference site first:', API_REF_URL)
return
# (Re)generate the code to make sure we don't push without it
generate(['tl', 'errors'])
# Try importing the telethon module to assert it has no errors
try:
import telethon
except:
except Exception as e:
print('Packaging for PyPi aborted, importing the module failed.')
print(e)
return
# Need python3.5 or higher, but Telethon is supposed to support 3.x
# Place it here since noone should be running ./setup.py pypi anyway
from subprocess import run
from shutil import rmtree
remove_dirs = ['__pycache__', 'build', 'dist', 'Telethon.egg-info']
for root, _dirs, _files in os.walk(LIBRARY_DIR, topdown=False):
# setuptools is including __pycache__ for some reason (#1605)
if root.endswith('/__pycache__'):
remove_dirs.append(root)
for x in remove_dirs:
shutil.rmtree(x, ignore_errors=True)
for x in ('build', 'dist', 'Telethon.egg-info'):
rmtree(x, ignore_errors=True)
run('python3 setup.py sdist', shell=True)
run('python3 setup.py bdist_wheel', shell=True)
run('twine upload dist/*', shell=True)
for x in ('build', 'dist', 'Telethon.egg-info'):
rmtree(x, ignore_errors=True)
shutil.rmtree(x, ignore_errors=True)
else:
# e.g. install from GitHub
if os.path.isdir(GENERATOR_DIR):
if GENERATOR_DIR.is_dir():
generate(['tl', 'errors'])
# Get the long description from the README file
@ -216,14 +244,15 @@ def main():
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6'
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
keywords='telegram api chat client library messaging mtproto',
packages=find_packages(exclude=[
'telethon_*', 'run_tests.py', 'try_telethon.py'
'telethon_*', 'tests*'
]),
install_requires=['pyaes', 'rsa',
'async_generator'],
install_requires=['pyaes', 'rsa'],
extras_require={
'cryptg': ['cryptg']
}
@ -231,5 +260,5 @@ def main():
if __name__ == '__main__':
with TempWorkDir(): # Could just use a try/finally but this is + reusable
main()
with TempWorkDir():
main(sys.argv)

View File

@ -1,12 +1,13 @@
import logging
from .client.telegramclient import TelegramClient
from .network import connection
from .tl import types, functions, custom
from . import version, events, utils, errors
from .tl.custom import Button
from .tl import patched as _ # import for its side-effects
from . import version, events, utils, errors, types, functions, custom
__version__ = version.__version__
logging.getLogger(__name__).addHandler(logging.NullHandler())
__all__ = ['TelegramClient', 'types', 'functions', 'custom',
'events', 'utils', 'errors']
__all__ = [
'TelegramClient', 'Button',
'types', 'functions', 'custom', 'errors',
'events', 'utils', 'connection'
]

View File

@ -0,0 +1,3 @@
from .entitycache import EntityCache
from .messagebox import MessageBox, GapError, PrematureEndReason
from .session import SessionState, ChannelState, Entity, EntityType

View File

@ -0,0 +1,62 @@
from .session import EntityType, Entity
_sentinel = object()
class EntityCache:
def __init__(
self,
hash_map: dict = _sentinel,
self_id: int = None,
self_bot: bool = None
):
self.hash_map = {} if hash_map is _sentinel else hash_map
self.self_id = self_id
self.self_bot = self_bot
def set_self_user(self, id, bot, hash):
self.self_id = id
self.self_bot = bot
if hash:
self.hash_map[id] = (hash, EntityType.BOT if bot else EntityType.USER)
def get(self, id):
try:
hash, ty = self.hash_map[id]
return Entity(ty, id, hash)
except KeyError:
return None
def extend(self, users, chats):
# See https://core.telegram.org/api/min for "issues" with "min constructors".
self.hash_map.update(
(u.id, (
u.access_hash,
EntityType.BOT if u.bot else EntityType.USER,
))
for u in users
if getattr(u, 'access_hash', None) and not u.min
)
self.hash_map.update(
(c.id, (
c.access_hash,
EntityType.MEGAGROUP if c.megagroup else (
EntityType.GIGAGROUP if getattr(c, 'gigagroup', None) else EntityType.CHANNEL
),
))
for c in chats
if getattr(c, 'access_hash', None) and not getattr(c, 'min', None)
)
def get_all_entities(self):
return [Entity(ty, id, hash) for id, (hash, ty) in self.hash_map.items()]
def put(self, entity):
self.hash_map[entity.id] = (entity.hash, entity.ty)
def retain(self, filter):
self.hash_map = {k: v for k, v in self.hash_map.items() if filter(k)}
def __len__(self):
return len(self.hash_map)

View File

@ -0,0 +1,833 @@
"""
This module deals with correct handling of updates, including gaps, and knowing when the code
should "get difference" (the set of updates that the client should know by now minus the set
of updates that it actually knows).
Each chat has its own [`Entry`] in the [`MessageBox`] (this `struct` is the "entry point").
At any given time, the message box may be either getting difference for them (entry is in
[`MessageBox::getting_diff_for`]) or not. If not getting difference, a possible gap may be
found for the updates (entry is in [`MessageBox::possible_gaps`]). Otherwise, the entry is
on its happy path.
Gaps are cleared when they are either resolved on their own (by waiting for a short time)
or because we got the difference for the corresponding entry.
While there are entries for which their difference must be fetched,
[`MessageBox::check_deadlines`] will always return [`Instant::now`], since "now" is the time
to get the difference.
"""
import asyncio
import datetime
import time
import logging
from enum import Enum
from .session import SessionState, ChannelState
from ..tl import types as tl, functions as fn
from ..helpers import get_running_loop
# Telegram sends `seq` equal to `0` when "it doesn't matter", so we use that value too.
NO_SEQ = 0
# See https://core.telegram.org/method/updates.getChannelDifference.
BOT_CHANNEL_DIFF_LIMIT = 100000
USER_CHANNEL_DIFF_LIMIT = 100
# > It may be useful to wait up to 0.5 seconds
POSSIBLE_GAP_TIMEOUT = 0.5
# After how long without updates the client will "timeout".
#
# When this timeout occurs, the client will attempt to fetch updates by itself, ignoring all the
# updates that arrive in the meantime. After all updates are fetched when this happens, the
# client will resume normal operation, and the timeout will reset.
#
# Documentation recommends 15 minutes without updates (https://core.telegram.org/api/updates).
NO_UPDATES_TIMEOUT = 15 * 60
# object() but with a tag to make it easier to debug
class Sentinel:
__slots__ = ('tag',)
def __init__(self, tag=None):
self.tag = tag or '_'
def __repr__(self):
return self.tag
# Entry "enum".
# Account-wide `pts` includes private conversations (one-to-one) and small group chats.
ENTRY_ACCOUNT = Sentinel('ACCOUNT')
# Account-wide `qts` includes only "secret" one-to-one chats.
ENTRY_SECRET = Sentinel('SECRET')
# Integers will be Channel-specific `pts`, and includes "megagroup", "broadcast" and "supergroup" channels.
# Python's logging doesn't define a TRACE level. Pick halfway between DEBUG and NOTSET.
# We don't define a name for this as libraries shouldn't do that though.
LOG_LEVEL_TRACE = (logging.DEBUG - logging.NOTSET) // 2
_sentinel = Sentinel()
def next_updates_deadline():
return get_running_loop().time() + NO_UPDATES_TIMEOUT
def epoch():
return datetime.datetime(*time.gmtime(0)[:6]).replace(tzinfo=datetime.timezone.utc)
class GapError(ValueError):
def __repr__(self):
return 'GapError()'
class PrematureEndReason(Enum):
TEMPORARY_SERVER_ISSUES = 'tmp'
BANNED = 'ban'
# Represents the information needed to correctly handle a specific `tl::enums::Update`.
class PtsInfo:
__slots__ = ('pts', 'pts_count', 'entry')
def __init__(
self,
pts: int,
pts_count: int,
entry: object
):
self.pts = pts
self.pts_count = pts_count
self.entry = entry
@classmethod
def from_update(cls, update):
pts = getattr(update, 'pts', None)
if pts:
pts_count = getattr(update, 'pts_count', None) or 0
try:
entry = update.message.peer_id.channel_id
except AttributeError:
entry = getattr(update, 'channel_id', None) or ENTRY_ACCOUNT
return cls(pts=pts, pts_count=pts_count, entry=entry)
qts = getattr(update, 'qts', None)
if qts:
return cls(pts=qts, pts_count=1, entry=ENTRY_SECRET)
return None
def __repr__(self):
return f'PtsInfo(pts={self.pts}, pts_count={self.pts_count}, entry={self.entry})'
# The state of a particular entry in the message box.
class State:
__slots__ = ('pts', 'deadline')
def __init__(
self,
# Current local persistent timestamp.
pts: int,
# Next instant when we would get the update difference if no updates arrived before then.
deadline: float
):
self.pts = pts
self.deadline = deadline
def __repr__(self):
return f'State(pts={self.pts}, deadline={self.deadline})'
# > ### Recovering gaps
# > […] Manually obtaining updates is also required in the following situations:
# > • Loss of sync: a gap was found in `seq` / `pts` / `qts` (as described above).
# > It may be useful to wait up to 0.5 seconds in this situation and abort the sync in case a new update
# > arrives, that fills the gap.
#
# This is really easy to trigger by spamming messages in a channel (with as little as 3 members works), because
# the updates produced by the RPC request take a while to arrive (whereas the read update comes faster alone).
class PossibleGap:
__slots__ = ('deadline', 'updates')
def __init__(
self,
deadline: float,
# Pending updates (those with a larger PTS, producing the gap which may later be filled).
updates: list # of updates
):
self.deadline = deadline
self.updates = updates
def __repr__(self):
return f'PossibleGap(deadline={self.deadline}, update_count={len(self.updates)})'
# Represents a "message box" (event `pts` for a specific entry).
#
# See https://core.telegram.org/api/updates#message-related-event-sequences.
class MessageBox:
__slots__ = ('_log', 'map', 'date', 'seq', 'next_deadline', 'possible_gaps', 'getting_diff_for')
def __init__(
self,
log,
# Map each entry to their current state.
map: dict = _sentinel, # entry -> state
# Additional fields beyond PTS needed by `ENTRY_ACCOUNT`.
date: datetime.datetime = epoch() + datetime.timedelta(seconds=1),
seq: int = NO_SEQ,
# Holds the entry with the closest deadline (optimization to avoid recalculating the minimum deadline).
next_deadline: object = None, # entry
# Which entries have a gap and may soon trigger a need to get difference.
#
# If a gap is found, stores the required information to resolve it (when should it timeout and what updates
# should be held in case the gap is resolved on its own).
#
# Not stored directly in `map` as an optimization (else we would need another way of knowing which entries have
# a gap in them).
possible_gaps: dict = _sentinel, # entry -> possiblegap
# For which entries are we currently getting difference.
getting_diff_for: set = _sentinel, # entry
):
self._log = log
self.map = {} if map is _sentinel else map
self.date = date
self.seq = seq
self.next_deadline = next_deadline
self.possible_gaps = {} if possible_gaps is _sentinel else possible_gaps
self.getting_diff_for = set() if getting_diff_for is _sentinel else getting_diff_for
if __debug__:
self._trace('MessageBox initialized')
def _trace(self, msg, *args, **kwargs):
# Calls to trace can't really be removed beforehand without some dark magic.
# So every call to trace is prefixed with `if __debug__`` instead, to remove
# it when using `python -O`. Probably unnecessary, but it's nice to avoid
# paying the cost for something that is not used.
self._log.log(LOG_LEVEL_TRACE, 'Current MessageBox state: seq = %r, date = %s, map = %r',
self.seq, self.date.isoformat(), self.map)
self._log.log(LOG_LEVEL_TRACE, msg, *args, **kwargs)
# region Creation, querying, and setting base state.
def load(self, session_state, channel_states):
"""
Create a [`MessageBox`] from a previously known update state.
"""
if __debug__:
self._trace('Loading MessageBox with session_state = %r, channel_states = %r', session_state, channel_states)
deadline = next_updates_deadline()
self.map.clear()
if session_state.pts != NO_SEQ:
self.map[ENTRY_ACCOUNT] = State(pts=session_state.pts, deadline=deadline)
if session_state.qts != NO_SEQ:
self.map[ENTRY_SECRET] = State(pts=session_state.qts, deadline=deadline)
self.map.update((s.channel_id, State(pts=s.pts, deadline=deadline)) for s in channel_states)
self.date = datetime.datetime.fromtimestamp(session_state.date, tz=datetime.timezone.utc)
self.seq = session_state.seq
self.next_deadline = ENTRY_ACCOUNT
def session_state(self):
"""
Return the current state.
This should be used for persisting the state.
"""
return dict(
pts=self.map[ENTRY_ACCOUNT].pts if ENTRY_ACCOUNT in self.map else NO_SEQ,
qts=self.map[ENTRY_SECRET].pts if ENTRY_SECRET in self.map else NO_SEQ,
date=self.date,
seq=self.seq,
), {id: state.pts for id, state in self.map.items() if isinstance(id, int)}
def is_empty(self) -> bool:
"""
Return true if the message box is empty and has no state yet.
"""
return ENTRY_ACCOUNT not in self.map
def check_deadlines(self):
"""
Return the next deadline when receiving updates should timeout.
If a deadline expired, the corresponding entries will be marked as needing to get its difference.
While there are entries pending of getting their difference, this method returns the current instant.
"""
now = get_running_loop().time()
if self.getting_diff_for:
return now
deadline = next_updates_deadline()
# Most of the time there will be zero or one gap in flight so finding the minimum is cheap.
if self.possible_gaps:
deadline = min(deadline, *(gap.deadline for gap in self.possible_gaps.values()))
elif self.next_deadline in self.map:
deadline = min(deadline, self.map[self.next_deadline].deadline)
# asyncio's loop time precision only seems to be about 3 decimal places, so it's possible that
# we find the same number again on repeated calls. Without the "or equal" part we would log the
# timeout for updates several times (it also makes sense to get difference if now is the deadline).
if now >= deadline:
# Check all expired entries and add them to the list that needs getting difference.
self.getting_diff_for.update(entry for entry, gap in self.possible_gaps.items() if now >= gap.deadline)
self.getting_diff_for.update(entry for entry, state in self.map.items() if now >= state.deadline)
if __debug__:
self._trace('Deadlines met, now getting diff for %r', self.getting_diff_for)
# When extending `getting_diff_for`, it's important to have the moral equivalent of
# `begin_get_diff` (that is, clear possible gaps if we're now getting difference).
for entry in self.getting_diff_for:
self.possible_gaps.pop(entry, None)
return deadline
# Reset the deadline for the periods without updates for the given entries.
#
# It also updates the next deadline time to reflect the new closest deadline.
def reset_deadlines(self, entries, deadline):
if not entries:
return
for entry in entries:
if entry not in self.map:
raise RuntimeError('Called reset_deadline on an entry for which we do not have state')
self.map[entry].deadline = deadline
if self.next_deadline in entries:
# If the updated deadline was the closest one, recalculate the new minimum.
self.next_deadline = min(self.map.items(), key=lambda entry_state: entry_state[1].deadline)[0]
elif self.next_deadline in self.map and deadline < self.map[self.next_deadline].deadline:
# If the updated deadline is smaller than the next deadline, change the next deadline to be the new one.
# Any entry will do, so the one from the last iteration is fine.
self.next_deadline = entry
# else an unrelated deadline was updated, so the closest one remains unchanged.
# Convenience to reset a channel's deadline, with optional timeout.
def reset_channel_deadline(self, channel_id, timeout):
self.reset_deadlines({channel_id}, get_running_loop().time() + (timeout or NO_UPDATES_TIMEOUT))
# Sets the update state.
#
# Should be called right after login if [`MessageBox::new`] was used, otherwise undesirable
# updates will be fetched.
def set_state(self, state, reset=True):
if __debug__:
self._trace('Setting state %s', state)
deadline = next_updates_deadline()
if state.pts != NO_SEQ or not reset:
self.map[ENTRY_ACCOUNT] = State(pts=state.pts, deadline=deadline)
else:
self.map.pop(ENTRY_ACCOUNT, None)
# Telegram seems to use the `qts` for bot accounts, but while applying difference,
# it might be reset back to 0. See issue #3873 for more details.
#
# During login, a value of zero would mean the `pts` is unknown,
# so the map shouldn't contain that entry.
# But while applying difference, if the value is zero, it (probably)
# truly means that's what should be used (hence the `reset` flag).
if state.qts != NO_SEQ or not reset:
self.map[ENTRY_SECRET] = State(pts=state.qts, deadline=deadline)
else:
self.map.pop(ENTRY_SECRET, None)
self.date = state.date
self.seq = state.seq
# Like [`MessageBox::set_state`], but for channels. Useful when getting dialogs.
#
# The update state will only be updated if no entry was known previously.
def try_set_channel_state(self, id, pts):
if __debug__:
self._trace('Trying to set channel state for %r: %r', id, pts)
if id not in self.map:
self.map[id] = State(pts=pts, deadline=next_updates_deadline())
# Try to begin getting difference for the given entry.
# Fails if the entry does not have a previously-known state that can be used to get its difference.
#
# Clears any previous gaps.
def try_begin_get_diff(self, entry, reason):
if entry not in self.map:
# Won't actually be able to get difference for this entry if we don't have a pts to start off from.
if entry in self.possible_gaps:
raise RuntimeError('Should not have a possible_gap for an entry not in the state map')
if __debug__:
self._trace('Should get difference for %r because %s but cannot due to missing hash', entry, reason)
return
if __debug__:
self._trace('Marking %r as needing difference because %s', entry, reason)
self.getting_diff_for.add(entry)
self.possible_gaps.pop(entry, None)
# Finish getting difference for the given entry.
#
# It also resets the deadline.
def end_get_diff(self, entry):
try:
self.getting_diff_for.remove(entry)
except KeyError:
raise RuntimeError('Called end_get_diff on an entry which was not getting diff for')
self.reset_deadlines({entry}, next_updates_deadline())
assert entry not in self.possible_gaps, "gaps shouldn't be created while getting difference"
# endregion Creation, querying, and setting base state.
# region "Normal" updates flow (processing and detection of gaps).
# Process an update and return what should be done with it.
#
# Updates corresponding to entries for which their difference is currently being fetched
# will be ignored. While according to the [updates' documentation]:
#
# > Implementations [have] to postpone updates received via the socket while
# > filling gaps in the event and `Update` sequences, as well as avoid filling
# > gaps in the same sequence.
#
# In practice, these updates should have also been retrieved through getting difference.
#
# [updates documentation] https://core.telegram.org/api/updates
def process_updates(
self,
updates,
chat_hashes,
result, # out list of updates; returns list of user, chat, or raise if gap
):
# v1 has never sent updates produced by the client itself to the handlers.
# However proper update handling requires those to be processed.
# This is an ugly workaround for that.
self_outgoing = getattr(updates, '_self_outgoing', False)
real_result = result
result = []
date = getattr(updates, 'date', None)
seq = getattr(updates, 'seq', None)
seq_start = getattr(updates, 'seq_start', None)
users = getattr(updates, 'users', None) or []
chats = getattr(updates, 'chats', None) or []
if __debug__:
self._trace('Processing updates with seq = %r, seq_start = %r, date = %s: %s',
seq, seq_start, date.isoformat() if date else None, updates)
if date is None:
# updatesTooLong is the only one with no date (we treat it as a gap)
self.try_begin_get_diff(ENTRY_ACCOUNT, 'received updatesTooLong')
raise GapError
if seq is None:
seq = NO_SEQ
if seq_start is None:
seq_start = seq
# updateShort is the only update which cannot be dispatched directly but doesn't have 'updates' field
updates = getattr(updates, 'updates', None) or [updates.update if isinstance(updates, tl.UpdateShort) else updates]
for u in updates:
u._self_outgoing = self_outgoing
# > For all the other [not `updates` or `updatesCombined`] `Updates` type constructors
# > there is no need to check `seq` or change a local state.
if seq_start != NO_SEQ:
if self.seq + 1 > seq_start:
# Skipping updates that were already handled
if __debug__:
self._trace('Skipping updates as they should have already been handled')
return (users, chats)
elif self.seq + 1 < seq_start:
# Gap detected
self.try_begin_get_diff(ENTRY_ACCOUNT, 'detected gap')
raise GapError
# else apply
def _sort_gaps(update):
pts = PtsInfo.from_update(update)
return pts.pts - pts.pts_count if pts else 0
reset_deadlines = set() # temporary buffer
any_pts_applied = [False] # using a list to pass "by reference"
result.extend(filter(None, (
self.apply_pts_info(u, reset_deadlines=reset_deadlines, any_pts_applied=any_pts_applied)
# Telegram can send updates out of order (e.g. ReadChannelInbox first
# and then NewChannelMessage, both with the same pts, but the count is
# 0 and 1 respectively), so we sort them first.
for u in sorted(updates, key=_sort_gaps))))
# > If the updates were applied, local *Updates* state must be updated
# > with `seq` (unless it's 0) and `date` from the constructor.
#
# By "were applied", we assume it means "some other pts was applied".
# Updates which can be applied in any order, such as `UpdateChat`,
# should not cause `seq` to be updated (or upcoming updates such as
# `UpdateChatParticipant` could be missed).
if any_pts_applied[0]:
if __debug__:
self._trace('Updating seq as local pts was updated too')
if date != epoch():
self.date = date
if seq != NO_SEQ:
self.seq = seq
self.reset_deadlines(reset_deadlines, next_updates_deadline())
if self.possible_gaps:
if __debug__:
self._trace('Trying to re-apply %r possible gaps', len(self.possible_gaps))
# For each update in possible gaps, see if the gap has been resolved already.
for key in list(self.possible_gaps.keys()):
self.possible_gaps[key].updates.sort(key=_sort_gaps)
for _ in range(len(self.possible_gaps[key].updates)):
update = self.possible_gaps[key].updates.pop(0)
# If this fails to apply, it will get re-inserted at the end.
# All should fail, so the order will be preserved (it would've cycled once).
update = self.apply_pts_info(update, reset_deadlines=None)
if update:
result.append(update)
if __debug__:
self._trace('Resolved gap with %r: %s', PtsInfo.from_update(update), update)
# Clear now-empty gaps.
self.possible_gaps = {entry: gap for entry, gap in self.possible_gaps.items() if gap.updates}
real_result.extend(u for u in result if not u._self_outgoing)
return (users, chats)
# Tries to apply the input update if its `PtsInfo` follows the correct order.
#
# If the update can be applied, it is returned; otherwise, the update is stored in a
# possible gap (unless it was already handled or would be handled through getting
# difference) and `None` is returned.
def apply_pts_info(
self,
update,
*,
reset_deadlines,
any_pts_applied=[True], # mutable default is fine as it's write-only
):
# This update means we need to call getChannelDifference to get the updates from the channel
if isinstance(update, tl.UpdateChannelTooLong):
self.try_begin_get_diff(update.channel_id, 'received updateChannelTooLong')
return None
pts = PtsInfo.from_update(update)
if not pts:
# No pts means that the update can be applied in any order.
if __debug__:
self._trace('No pts in update, so it can be applied in any order: %s', update)
return update
# As soon as we receive an update of any form related to messages (has `PtsInfo`),
# the "no updates" period for that entry is reset.
#
# Build the `HashSet` to avoid calling `reset_deadline` more than once for the same entry.
#
# By the time this method returns, self.map will have an entry for which we can reset its deadline.
if reset_deadlines:
reset_deadlines.add(pts.entry)
if pts.entry in self.getting_diff_for:
# Note: early returning here also prevents gap from being inserted (which they should
# not be while getting difference).
if __debug__:
self._trace('Skipping update with %r as its difference is being fetched', pts)
return None
if pts.entry in self.map:
local_pts = self.map[pts.entry].pts
if local_pts + pts.pts_count > pts.pts:
# Ignore
if __debug__:
self._trace('Skipping update since local pts %r > %r: %s', local_pts, pts, update)
return None
elif local_pts + pts.pts_count < pts.pts:
# Possible gap
# TODO store chats too?
if __debug__:
self._trace('Possible gap since local pts %r < %r: %s', local_pts, pts, update)
if pts.entry not in self.possible_gaps:
self.possible_gaps[pts.entry] = PossibleGap(
deadline=get_running_loop().time() + POSSIBLE_GAP_TIMEOUT,
updates=[]
)
self.possible_gaps[pts.entry].updates.append(update)
return None
else:
# Apply
any_pts_applied[0] = True
if __debug__:
self._trace('Applying update pts since local pts %r = %r: %s', local_pts, pts, update)
# In a channel, we may immediately receive:
# * ReadChannelInbox (pts = X, pts_count = 0)
# * NewChannelMessage (pts = X, pts_count = 1)
#
# Notice how both `pts` are the same. If they were to be applied out of order, the first
# one however would've triggered a gap because `local_pts` + `pts_count` of 0 would be
# less than `remote_pts`. So there is no risk by setting the `local_pts` to match the
# `remote_pts` here of missing the new message.
#
# The message would however be lost if we initialized the pts with the first one, since
# the second one would appear "already handled". To prevent this we set the pts to be
# one less when the count is 0 (which might be wrong and trigger a gap later on, but is
# unlikely). This will prevent us from losing updates in the unlikely scenario where these
# two updates arrive in different packets (and therefore couldn't be sorted beforehand).
if pts.entry in self.map:
self.map[pts.entry].pts = pts.pts
else:
# When a chat is migrated to a megagroup, the first update can be a `ReadChannelInbox`
# with `pts = 1, pts_count = 0` followed by a `NewChannelMessage` with `pts = 2, pts_count=1`.
# Note how the `pts` for the message is 2 and not 1 unlike the case described before!
# This is likely because the `pts` cannot be 0 (or it would fail with PERSISTENT_TIMESTAMP_EMPTY),
# which forces the first update to be 1. But if we got difference with 1 and the second update
# also used 1, we would miss it, so Telegram probably uses 2 to work around that.
self.map[pts.entry] = State(
pts=(pts.pts - (0 if pts.pts_count else 1)) or 1,
deadline=next_updates_deadline()
)
return update
# endregion "Normal" updates flow (processing and detection of gaps).
# region Getting and applying account difference.
# Return the request that needs to be made to get the difference, if any.
def get_difference(self):
for entry in (ENTRY_ACCOUNT, ENTRY_SECRET):
if entry in self.getting_diff_for:
if entry not in self.map:
raise RuntimeError('Should not try to get difference for an entry without known state')
gd = fn.updates.GetDifferenceRequest(
pts=self.map[ENTRY_ACCOUNT].pts,
pts_total_limit=None,
date=self.date,
qts=self.map[ENTRY_SECRET].pts if ENTRY_SECRET in self.map else NO_SEQ,
)
if __debug__:
self._trace('Requesting account difference %s', gd)
return gd
return None
# Similar to [`MessageBox::process_updates`], but using the result from getting difference.
def apply_difference(
self,
diff,
chat_hashes,
):
if __debug__:
self._trace('Applying account difference %s', diff)
finish = None
result = None
if isinstance(diff, tl.updates.DifferenceEmpty):
finish = True
self.date = diff.date
self.seq = diff.seq
result = [], [], []
elif isinstance(diff, tl.updates.Difference):
finish = True
chat_hashes.extend(diff.users, diff.chats)
result = self.apply_difference_type(diff, chat_hashes)
elif isinstance(diff, tl.updates.DifferenceSlice):
finish = False
chat_hashes.extend(diff.users, diff.chats)
result = self.apply_difference_type(diff, chat_hashes)
elif isinstance(diff, tl.updates.DifferenceTooLong):
finish = True
self.map[ENTRY_ACCOUNT].pts = diff.pts # the deadline will be reset once the diff ends
result = [], [], []
if finish:
account = ENTRY_ACCOUNT in self.getting_diff_for
secret = ENTRY_SECRET in self.getting_diff_for
if not account and not secret:
raise RuntimeError('Should not be applying the difference when neither account or secret was diff was active')
# Both may be active if both expired at the same time.
if account:
self.end_get_diff(ENTRY_ACCOUNT)
if secret:
self.end_get_diff(ENTRY_SECRET)
return result
def apply_difference_type(
self,
diff,
chat_hashes,
):
state = getattr(diff, 'intermediate_state', None) or diff.state
self.set_state(state, reset=False)
# diff.other_updates can contain things like UpdateChannelTooLong and UpdateNewChannelMessage.
# We need to process those as if they were socket updates to discard any we have already handled.
updates = []
self.process_updates(tl.Updates(
updates=diff.other_updates,
users=diff.users,
chats=diff.chats,
date=epoch(),
seq=NO_SEQ, # this way date is not used
), chat_hashes, updates)
updates.extend(tl.UpdateNewMessage(
message=m,
pts=NO_SEQ,
pts_count=NO_SEQ,
) for m in diff.new_messages)
updates.extend(tl.UpdateNewEncryptedMessage(
message=m,
qts=NO_SEQ,
) for m in diff.new_encrypted_messages)
return updates, diff.users, diff.chats
def end_difference(self):
if __debug__:
self._trace('Ending account difference')
account = ENTRY_ACCOUNT in self.getting_diff_for
secret = ENTRY_SECRET in self.getting_diff_for
if not account and not secret:
raise RuntimeError('Should not be ending get difference when neither account or secret was diff was active')
# Both may be active if both expired at the same time.
if account:
self.end_get_diff(ENTRY_ACCOUNT)
if secret:
self.end_get_diff(ENTRY_SECRET)
# endregion Getting and applying account difference.
# region Getting and applying channel difference.
# Return the request that needs to be made to get a channel's difference, if any.
def get_channel_difference(
self,
chat_hashes,
):
entry = next((id for id in self.getting_diff_for if isinstance(id, int)), None)
if not entry:
return None
packed = chat_hashes.get(entry)
if not packed:
# Cannot get channel difference as we're missing its hash
# TODO we should probably log this
self.end_get_diff(entry)
# Remove the outdated `pts` entry from the map so that the next update can correct
# it. Otherwise, it will spam that the access hash is missing.
self.map.pop(entry, None)
return None
state = self.map.get(entry)
if not state:
raise RuntimeError('Should not try to get difference for an entry without known state')
gd = fn.updates.GetChannelDifferenceRequest(
force=False,
channel=tl.InputChannel(packed.id, packed.hash),
filter=tl.ChannelMessagesFilterEmpty(),
pts=state.pts,
limit=BOT_CHANNEL_DIFF_LIMIT if chat_hashes.self_bot else USER_CHANNEL_DIFF_LIMIT
)
if __debug__:
self._trace('Requesting channel difference %s', gd)
return gd
# Similar to [`MessageBox::process_updates`], but using the result from getting difference.
def apply_channel_difference(
self,
request,
diff,
chat_hashes,
):
entry = request.channel.channel_id
if __debug__:
self._trace('Applying channel difference for %r: %s', entry, diff)
self.possible_gaps.pop(entry, None)
if isinstance(diff, tl.updates.ChannelDifferenceEmpty):
assert diff.final
self.end_get_diff(entry)
self.map[entry].pts = diff.pts
return [], [], []
elif isinstance(diff, tl.updates.ChannelDifferenceTooLong):
assert diff.final
self.map[entry].pts = diff.dialog.pts
chat_hashes.extend(diff.users, diff.chats)
self.reset_channel_deadline(entry, diff.timeout)
# This `diff` has the "latest messages and corresponding chats", but it would
# be strange to give the user only partial changes of these when they would
# expect all updates to be fetched. Instead, nothing is returned.
return [], [], []
elif isinstance(diff, tl.updates.ChannelDifference):
if diff.final:
self.end_get_diff(entry)
self.map[entry].pts = diff.pts
chat_hashes.extend(diff.users, diff.chats)
updates = []
self.process_updates(tl.Updates(
updates=diff.other_updates,
users=diff.users,
chats=diff.chats,
date=epoch(),
seq=NO_SEQ, # this way date is not used
), chat_hashes, updates)
updates.extend(tl.UpdateNewChannelMessage(
message=m,
pts=NO_SEQ,
pts_count=NO_SEQ,
) for m in diff.new_messages)
self.reset_channel_deadline(entry, None)
return updates, diff.users, diff.chats
def end_channel_difference(self, request, reason: PrematureEndReason, chat_hashes):
entry = request.channel.channel_id
if __debug__:
self._trace('Ending channel difference for %r because %s', entry, reason)
if reason == PrematureEndReason.TEMPORARY_SERVER_ISSUES:
# Temporary issues. End getting difference without updating the pts so we can retry later.
self.possible_gaps.pop(entry, None)
self.end_get_diff(entry)
elif reason == PrematureEndReason.BANNED:
# Banned in the channel. Forget its state since we can no longer fetch updates from it.
self.possible_gaps.pop(entry, None)
self.end_get_diff(entry)
del self.map[entry]
else:
raise RuntimeError('Unknown reason to end channel difference')
# endregion Getting and applying channel difference.

View File

@ -0,0 +1,195 @@
from typing import Optional, Tuple
from enum import IntEnum
from ..tl.types import InputPeerUser, InputPeerChat, InputPeerChannel
class SessionState:
"""
Stores the information needed to fetch updates and about the current user.
* user_id: 64-bit number representing the user identifier.
* dc_id: 32-bit number relating to the datacenter identifier where the user is.
* bot: is the logged-in user a bot?
* pts: 64-bit number holding the state needed to fetch updates.
* qts: alternative 64-bit number holding the state needed to fetch updates.
* date: 64-bit number holding the date needed to fetch updates.
* seq: 64-bit-number holding the sequence number needed to fetch updates.
* takeout_id: 64-bit-number holding the identifier of the current takeout session.
Note that some of the numbers will only use 32 out of the 64 available bits.
However, for future-proofing reasons, we recommend you pretend they are 64-bit long.
"""
__slots__ = ('user_id', 'dc_id', 'bot', 'pts', 'qts', 'date', 'seq', 'takeout_id')
def __init__(
self,
user_id: int,
dc_id: int,
bot: bool,
pts: int,
qts: int,
date: int,
seq: int,
takeout_id: Optional[int]
):
self.user_id = user_id
self.dc_id = dc_id
self.bot = bot
self.pts = pts
self.qts = qts
self.date = date
self.seq = seq
self.takeout_id = takeout_id
def __repr__(self):
return repr({k: getattr(self, k) for k in self.__slots__})
class ChannelState:
"""
Stores the information needed to fetch updates from a channel.
* channel_id: 64-bit number representing the channel identifier.
* pts: 64-bit number holding the state needed to fetch updates.
"""
__slots__ = ('channel_id', 'pts')
def __init__(
self,
channel_id: int,
pts: int,
):
self.channel_id = channel_id
self.pts = pts
def __repr__(self):
return repr({k: getattr(self, k) for k in self.__slots__})
class EntityType(IntEnum):
"""
You can rely on the type value to be equal to the ASCII character one of:
* 'U' (85): this entity belongs to a :tl:`User` who is not a ``bot``.
* 'B' (66): this entity belongs to a :tl:`User` who is a ``bot``.
* 'G' (71): this entity belongs to a small group :tl:`Chat`.
* 'C' (67): this entity belongs to a standard broadcast :tl:`Channel`.
* 'M' (77): this entity belongs to a megagroup :tl:`Channel`.
* 'E' (69): this entity belongs to an "enormous" "gigagroup" :tl:`Channel`.
"""
USER = ord('U')
BOT = ord('B')
GROUP = ord('G')
CHANNEL = ord('C')
MEGAGROUP = ord('M')
GIGAGROUP = ord('E')
def canonical(self):
"""
Return the canonical version of this type.
"""
return _canon_entity_types[self]
_canon_entity_types = {
EntityType.USER: EntityType.USER,
EntityType.BOT: EntityType.USER,
EntityType.GROUP: EntityType.GROUP,
EntityType.CHANNEL: EntityType.CHANNEL,
EntityType.MEGAGROUP: EntityType.CHANNEL,
EntityType.GIGAGROUP: EntityType.CHANNEL,
}
class Entity:
"""
Stores the information needed to use a certain user, chat or channel with the API.
* ty: 8-bit number indicating the type of the entity (of type `EntityType`).
* id: 64-bit number uniquely identifying the entity among those of the same type.
* hash: 64-bit signed number needed to use this entity with the API.
The string representation of this class is considered to be stable, for as long as
Telegram doesn't need to add more fields to the entities. It can also be converted
to bytes with ``bytes(entity)``, for a more compact representation.
"""
__slots__ = ('ty', 'id', 'hash')
def __init__(
self,
ty: EntityType,
id: int,
hash: int
):
self.ty = ty
self.id = id
self.hash = hash
@property
def is_user(self):
"""
``True`` if the entity is either a user or a bot.
"""
return self.ty in (EntityType.USER, EntityType.BOT)
@property
def is_group(self):
"""
``True`` if the entity is a small group chat or `megagroup`_.
.. _megagroup: https://telegram.org/blog/supergroups5k
"""
return self.ty in (EntityType.GROUP, EntityType.MEGAGROUP)
@property
def is_broadcast(self):
"""
``True`` if the entity is a broadcast channel or `broadcast group`_.
.. _broadcast group: https://telegram.org/blog/autodelete-inv2#groups-with-unlimited-members
"""
return self.ty in (EntityType.CHANNEL, EntityType.GIGAGROUP)
@classmethod
def from_str(cls, string: str):
"""
Convert the string into an `Entity`.
"""
try:
ty, id, hash = string.split('.')
ty, id, hash = ord(ty), int(id), int(hash)
except AttributeError:
raise TypeError(f'expected str, got {string!r}') from None
except (TypeError, ValueError):
raise ValueError(f'malformed entity str (must be T.id.hash), got {string!r}') from None
return cls(EntityType(ty), id, hash)
@classmethod
def from_bytes(cls, blob):
"""
Convert the bytes into an `Entity`.
"""
try:
ty, id, hash = struct.unpack('<Bqq', blob)
except struct.error:
raise ValueError(f'malformed entity data, got {string!r}') from None
return cls(EntityType(ty), id, hash)
def __str__(self):
return f'{chr(self.ty)}.{self.id}.{self.hash}'
def __bytes__(self):
return struct.pack('<Bqq', self.ty, self.id, self.hash)
def _as_input_peer(self):
if self.is_user:
return InputPeerUser(self.id, self.hash)
elif self.ty == EntityType.GROUP:
return InputPeerChat(self.id)
else:
return InputPeerChannel(self.id, self.hash)
def __repr__(self):
return repr({k: getattr(self, k) for k in self.__slots__})

View File

@ -19,6 +19,7 @@ from .messages import MessageMethods
from .chats import ChatMethods
from .dialogs import DialogMethods
from .downloads import DownloadMethods
from .account import AccountMethods
from .auth import AuthMethods
from .bots import BotMethods
from .telegramclient import TelegramClient

243
telethon/client/account.py Normal file
View File

@ -0,0 +1,243 @@
import functools
import inspect
import typing
from .users import _NOT_A_REQUEST
from .. import helpers, utils
from ..tl import functions, TLRequest
if typing.TYPE_CHECKING:
from .telegramclient import TelegramClient
# TODO Make use of :tl:`InvokeWithMessagesRange` somehow
# For that, we need to use :tl:`GetSplitRanges` first.
class _TakeoutClient:
"""
Proxy object over the client.
"""
__PROXY_INTERFACE = ('__enter__', '__exit__', '__aenter__', '__aexit__')
def __init__(self, finalize, client, request):
# We use the name mangling for attributes to make them inaccessible
# from within the shadowed client object and to distinguish them from
# its own attributes where needed.
self.__finalize = finalize
self.__client = client
self.__request = request
self.__success = None
@property
def success(self):
return self.__success
@success.setter
def success(self, value):
self.__success = value
async def __aenter__(self):
# Enter/Exit behaviour is "overrode", we don't want to call start.
client = self.__client
if client.session.takeout_id is None:
client.session.takeout_id = (await client(self.__request)).id
elif self.__request is not None:
raise ValueError("Can't send a takeout request while another "
"takeout for the current session still not been finished yet.")
return self
async def __aexit__(self, exc_type, exc_value, traceback):
if self.__success is None and self.__finalize:
self.__success = exc_type is None
if self.__success is not None:
result = await self(functions.account.FinishTakeoutSessionRequest(
self.__success))
if not result:
raise ValueError("Failed to finish the takeout.")
self.session.takeout_id = None
__enter__ = helpers._sync_enter
__exit__ = helpers._sync_exit
async def __call__(self, request, ordered=False):
takeout_id = self.__client.session.takeout_id
if takeout_id is None:
raise ValueError('Takeout mode has not been initialized '
'(are you calling outside of "with"?)')
single = not utils.is_list_like(request)
requests = ((request,) if single else request)
wrapped = []
for r in requests:
if not isinstance(r, TLRequest):
raise _NOT_A_REQUEST()
await r.resolve(self, utils)
wrapped.append(functions.InvokeWithTakeoutRequest(takeout_id, r))
return await self.__client(
wrapped[0] if single else wrapped, ordered=ordered)
def __getattribute__(self, name):
# We access class via type() because __class__ will recurse infinitely.
# Also note that since we've name-mangled our own class attributes,
# they'll be passed to __getattribute__() as already decorated. For
# example, 'self.__client' will be passed as '_TakeoutClient__client'.
# https://docs.python.org/3/tutorial/classes.html#private-variables
if name.startswith('__') and name not in type(self).__PROXY_INTERFACE:
raise AttributeError # force call of __getattr__
# Try to access attribute in the proxy object and check for the same
# attribute in the shadowed object (through our __getattr__) if failed.
return super().__getattribute__(name)
def __getattr__(self, name):
value = getattr(self.__client, name)
if inspect.ismethod(value):
# Emulate bound methods behavior by partially applying our proxy
# class as the self parameter instead of the client.
return functools.partial(
getattr(self.__client.__class__, name), self)
return value
def __setattr__(self, name, value):
if name.startswith('_{}__'.format(type(self).__name__.lstrip('_'))):
# This is our own name-mangled attribute, keep calm.
return super().__setattr__(name, value)
return setattr(self.__client, name, value)
class AccountMethods:
def takeout(
self: 'TelegramClient',
finalize: bool = True,
*,
contacts: bool = None,
users: bool = None,
chats: bool = None,
megagroups: bool = None,
channels: bool = None,
files: bool = None,
max_file_size: bool = None) -> 'TelegramClient':
"""
Returns a :ref:`telethon-client` which calls methods behind a takeout session.
It does so by creating a proxy object over the current client through
which making requests will use :tl:`InvokeWithTakeoutRequest` to wrap
them. In other words, returns the current client modified so that
requests are done as a takeout:
Some of the calls made through the takeout session will have lower
flood limits. This is useful if you want to export the data from
conversations or mass-download media, since the rate limits will
be lower. Only some requests will be affected, and you will need
to adjust the `wait_time` of methods like `client.iter_messages
<telethon.client.messages.MessageMethods.iter_messages>`.
By default, all parameters are `None`, and you need to enable those
you plan to use by setting them to either `True` or `False`.
You should ``except errors.TakeoutInitDelayError as e``, since this
exception will raise depending on the condition of the session. You
can then access ``e.seconds`` to know how long you should wait for
before calling the method again.
There's also a `success` property available in the takeout proxy
object, so from the `with` body you can set the boolean result that
will be sent back to Telegram. But if it's left `None` as by
default, then the action is based on the `finalize` parameter. If
it's `True` then the takeout will be finished, and if no exception
occurred during it, then `True` will be considered as a result.
Otherwise, the takeout will not be finished and its ID will be
preserved for future usage as `client.session.takeout_id
<telethon.sessions.abstract.Session.takeout_id>`.
Arguments
finalize (`bool`):
Whether the takeout session should be finalized upon
exit or not.
contacts (`bool`):
Set to `True` if you plan on downloading contacts.
users (`bool`):
Set to `True` if you plan on downloading information
from users and their private conversations with you.
chats (`bool`):
Set to `True` if you plan on downloading information
from small group chats, such as messages and media.
megagroups (`bool`):
Set to `True` if you plan on downloading information
from megagroups (channels), such as messages and media.
channels (`bool`):
Set to `True` if you plan on downloading information
from broadcast channels, such as messages and media.
files (`bool`):
Set to `True` if you plan on downloading media and
you don't only wish to export messages.
max_file_size (`int`):
The maximum file size, in bytes, that you plan
to download for each message with media.
Example
.. code-block:: python
from telethon import errors
try:
async with client.takeout() as takeout:
await client.get_messages('me') # normal call
await takeout.get_messages('me') # wrapped through takeout (less limits)
async for message in takeout.iter_messages(chat, wait_time=0):
... # Do something with the message
except errors.TakeoutInitDelayError as e:
print('Must wait', e.seconds, 'before takeout')
"""
request_kwargs = dict(
contacts=contacts,
message_users=users,
message_chats=chats,
message_megagroups=megagroups,
message_channels=channels,
files=files,
file_max_size=max_file_size
)
arg_specified = (arg is not None for arg in request_kwargs.values())
if self.session.takeout_id is None or any(arg_specified):
request = functions.account.InitTakeoutSessionRequest(
**request_kwargs)
else:
request = None
return _TakeoutClient(finalize, self, request)
async def end_takeout(self: 'TelegramClient', success: bool) -> bool:
"""
Finishes the current takeout session.
Arguments
success (`bool`):
Whether the takeout completed successfully or not.
Returns
`True` if the operation was successful, `False` otherwise.
Example
.. code-block:: python
await client.end_takeout(success=False)
"""
try:
async with _TakeoutClient(True, self, None) as takeout:
takeout.success = success
except ValueError:
return False
return True

View File

@ -1,48 +1,44 @@
import getpass
import hashlib
import inspect
import os
import sys
import typing
import warnings
from .messageparse import MessageParseMethods
from .users import UserMethods
from .. import utils, helpers, errors
from ..tl import types, functions
from .. import utils, helpers, errors, password as pwd_mod
from ..tl import types, functions, custom
from .._updates import SessionState
if typing.TYPE_CHECKING:
from .telegramclient import TelegramClient
class AuthMethods(MessageParseMethods, UserMethods):
class AuthMethods:
# region Public methods
def start(
self,
phone=lambda: input('Please enter your phone (or bot token): '),
password=lambda: getpass.getpass('Please enter your password: '),
self: 'TelegramClient',
phone: typing.Union[typing.Callable[[], str], str] = lambda: input('Please enter your phone (or bot token): '),
password: typing.Union[typing.Callable[[], str], str] = lambda: getpass.getpass('Please enter your password: '),
*,
bot_token=None, force_sms=False, code_callback=None,
first_name='New User', last_name='', max_attempts=3):
bot_token: str = None,
force_sms: bool = False,
code_callback: typing.Callable[[], typing.Union[str, int]] = None,
first_name: str = 'New User',
last_name: str = '',
max_attempts: int = 3) -> 'TelegramClient':
"""
Convenience method to interactively connect and sign in if required,
also taking into consideration that 2FA may be enabled in the account.
Starts the client (connects and logs in if necessary).
If the phone doesn't belong to an existing account (and will hence
`sign_up` for a new one), **you are agreeing to Telegram's
Terms of Service. This is required and your account
will be banned otherwise.** See https://telegram.org/tos
and https://core.telegram.org/api/terms.
Example usage:
>>> client = ...
>>> client.start(phone)
Please enter the code you received: 12345
Please enter your password: *******
(You are now logged in)
By default, this method will be interactive (asking for
user input if needed), and will handle 2FA if enabled too.
If the event loop is already running, this method returns a
coroutine that you should await on your own code; otherwise
the loop is ran until said coroutine completes.
Args:
Arguments
phone (`str` | `int` | `callable`):
The phone (or callable without arguments to get it)
to which the code will be sent. If a bot-token-like
@ -79,9 +75,27 @@ class AuthMethods(MessageParseMethods, UserMethods):
How many times the code/password callback should be
retried or switching between signing in and signing up.
Returns:
Returns
This `TelegramClient`, so initialization
can be chained with ``.start()``.
Example
.. code-block:: python
client = TelegramClient('anon', api_id, api_hash)
# Starting as a bot account
await client.start(bot_token=bot_token)
# Starting as a user account
await client.start(phone)
# Please enter the code you received: 12345
# Please enter your password: *******
# (You are now logged in)
# Starting using a context manager (this calls start()):
with client:
pass
"""
if code_callback is None:
def code_callback():
@ -115,12 +129,36 @@ class AuthMethods(MessageParseMethods, UserMethods):
)
async def _start(
self, phone, password, bot_token, force_sms,
self: 'TelegramClient', phone, password, bot_token, force_sms,
code_callback, first_name, last_name, max_attempts):
if not self.is_connected():
await self.connect()
if await self.is_user_authorized():
# Rather than using `is_user_authorized`, use `get_me`. While this is
# more expensive and needs to retrieve more data from the server, it
# enables the library to warn users trying to login to a different
# account. See #1172.
me = await self.get_me()
if me is not None:
# The warnings here are on a best-effort and may fail.
if bot_token:
# bot_token's first part has the bot ID, but it may be invalid
# so don't try to parse as int (instead cast our ID to string).
if bot_token[:bot_token.find(':')] != str(me.id):
warnings.warn(
'the session already had an authorized user so it did '
'not login to the bot account using the provided bot_token; '
'if you were expecting a different user, check whether '
'you are accidentally reusing an existing session'
)
elif phone and not callable(phone) and utils.parse_phone(phone) != me.phone:
warnings.warn(
'the session already had an authorized user so it did '
'not login to the user account using the provided phone; '
'if you were expecting a different user, check whether '
'you are accidentally reusing an existing session'
)
return self
if not bot_token:
@ -145,27 +183,25 @@ class AuthMethods(MessageParseMethods, UserMethods):
attempts = 0
two_step_detected = False
sent_code = await self.send_code_request(phone, force_sms=force_sms)
sign_up = not sent_code.phone_registered
await self.send_code_request(phone, force_sms=force_sms)
while attempts < max_attempts:
try:
value = code_callback()
if inspect.isawaitable(value):
value = await value
if sign_up:
me = await self.sign_up(value, first_name, last_name)
else:
# Raises SessionPasswordNeededError if 2FA enabled
me = await self.sign_in(phone, code=value)
# Since sign-in with no code works (it sends the code)
# we must double-check that here. Else we'll assume we
# logged in, and it will return None as the User.
if not value:
raise errors.PhoneCodeEmptyError(request=None)
# Raises SessionPasswordNeededError if 2FA enabled
me = await self.sign_in(phone, code=value)
break
except errors.SessionPasswordNeededError:
two_step_detected = True
break
except errors.PhoneNumberOccupiedError:
sign_up = False
except errors.PhoneNumberUnoccupiedError:
sign_up = True
except (errors.PhoneCodeEmptyError,
errors.PhoneCodeExpiredError,
errors.PhoneCodeHashEmptyError,
@ -199,29 +235,58 @@ class AuthMethods(MessageParseMethods, UserMethods):
print('Invalid password. Please try again',
file=sys.stderr)
else:
raise errors.PasswordHashInvalidError()
raise errors.PasswordHashInvalidError(request=None)
else:
me = await self.sign_in(phone=phone, password=password)
# We won't reach here if any step failed (exit by exception)
signed, name = 'Signed in successfully as', utils.get_display_name(me)
signed, name = 'Signed in successfully as ', utils.get_display_name(me)
tos = '; remember to not break the ToS or you will risk an account ban!'
try:
print(signed, name)
print(signed, name, tos, sep='')
except UnicodeEncodeError:
# Some terminals don't support certain characters
print(signed, name.encode('utf-8', errors='ignore')
.decode('ascii', errors='ignore'))
.decode('ascii', errors='ignore'), tos, sep='')
return self
async def sign_in(
self, phone=None, code=None, *, password=None,
bot_token=None, phone_code_hash=None):
def _parse_phone_and_hash(self, phone, phone_hash):
"""
Starts or completes the sign in process with the given phone number
or code that Telegram sent.
Helper method to both parse and validate phone and its hash.
"""
phone = utils.parse_phone(phone) or self._phone
if not phone:
raise ValueError(
'Please make sure to call send_code_request first.'
)
Args:
phone_hash = phone_hash or self._phone_code_hash.get(phone, None)
if not phone_hash:
raise ValueError('You also need to provide a phone_code_hash.')
return phone, phone_hash
async def sign_in(
self: 'TelegramClient',
phone: str = None,
code: typing.Union[str, int] = None,
*,
password: str = None,
bot_token: str = None,
phone_code_hash: str = None) -> 'typing.Union[types.User, types.auth.SentCode]':
"""
Logs in to Telegram to an existing user or bot account.
You should only use this if you are not authorized yet.
This method will send the code if it's not provided.
.. note::
In most cases, you should simply use `start()` and not this method.
Arguments
phone (`str` | `int`):
The phone to send the code to if no code was provided,
or to override the phone that was previously used with
@ -235,19 +300,29 @@ class AuthMethods(MessageParseMethods, UserMethods):
password (`str`):
2FA password, should be used if a previous call raised
SessionPasswordNeededError.
``SessionPasswordNeededError``.
bot_token (`str`):
Used to sign in as a bot. Not all requests will be available.
This should be the hash the @BotFather gave you.
This should be the hash the `@BotFather <https://t.me/BotFather>`_
gave you.
phone_code_hash (`str`):
The hash returned by .send_code_request. This can be set to None
to use the last hash known.
phone_code_hash (`str`, optional):
The hash returned by `send_code_request`. This can be left as
`None` to use the last hash known for the phone to be used.
Returns:
Returns
The signed in user, or the information about
:meth:`send_code_request`.
Example
.. code-block:: python
phone = '+34 123 123 123'
await client.sign_in(phone) # send code
code = input('enter code: ')
await client.sign_in(phone, code)
"""
me = await self.get_me()
if me:
@ -256,248 +331,347 @@ class AuthMethods(MessageParseMethods, UserMethods):
if phone and not code and not password:
return await self.send_code_request(phone)
elif code:
phone = utils.parse_phone(phone) or self._phone
phone_code_hash = \
phone_code_hash or self._phone_code_hash.get(phone, None)
if not phone:
raise ValueError(
'Please make sure to call send_code_request first.'
)
if not phone_code_hash:
raise ValueError('You also need to provide a phone_code_hash.')
phone, phone_code_hash = \
self._parse_phone_and_hash(phone, phone_code_hash)
# May raise PhoneCodeEmptyError, PhoneCodeExpiredError,
# PhoneCodeHashEmptyError or PhoneCodeInvalidError.
result = await self(functions.auth.SignInRequest(
phone, phone_code_hash, str(code)))
request = functions.auth.SignInRequest(
phone, phone_code_hash, str(code)
)
elif password:
salt = (await self(
functions.account.GetPasswordRequest())).current_salt
result = await self(functions.auth.CheckPasswordRequest(
helpers.get_password_hash(password, salt)
))
pwd = await self(functions.account.GetPasswordRequest())
request = functions.auth.CheckPasswordRequest(
pwd_mod.compute_check(pwd, password)
)
elif bot_token:
result = await self(functions.auth.ImportBotAuthorizationRequest(
request = functions.auth.ImportBotAuthorizationRequest(
flags=0, bot_auth_token=bot_token,
api_id=self.api_id, api_hash=self.api_hash
))
)
else:
raise ValueError(
'You must provide a phone and a code the first time, '
'and a password only if an RPCError was raised before.'
)
self._self_input_peer = utils.get_input_peer(
result.user, allow_self=False
)
try:
result = await self(request)
except errors.PhoneCodeExpiredError:
self._phone_code_hash.pop(phone, None)
raise
if isinstance(result, types.auth.AuthorizationSignUpRequired):
# Emulate pre-layer 104 behaviour
self._tos = result.terms_of_service
raise errors.PhoneNumberUnoccupiedError(request=request)
return await self._on_login(result.user)
async def sign_up(
self: 'TelegramClient',
code: typing.Union[str, int],
first_name: str,
last_name: str = '',
*,
phone: str = None,
phone_code_hash: str = None) -> 'types.User':
"""
This method can no longer be used, and will immediately raise a ``ValueError``.
See `issue #4050 <https://github.com/LonamiWebs/Telethon/issues/4050>`_ for context.
"""
raise ValueError('Third-party applications cannot sign up for Telegram. See https://github.com/LonamiWebs/Telethon/issues/4050 for details')
async def _on_login(self, user):
"""
Callback called whenever the login or sign up process completes.
Returns the input user parameter.
"""
self._mb_entity_cache.set_self_user(user.id, user.bot, user.access_hash)
self._authorized = True
return result.user
async def sign_up(self, code, first_name, last_name=''):
state = await self(functions.updates.GetStateRequest())
# the server may send an old qts in getState
difference = await self(functions.updates.GetDifferenceRequest(pts=state.pts, date=state.date, qts=state.qts))
if isinstance(difference, types.updates.Difference):
state = difference.state
elif isinstance(difference, types.updates.DifferenceSlice):
state = difference.intermediate_state
elif isinstance(difference, types.updates.DifferenceTooLong):
state.pts = difference.pts
self._message_box.load(SessionState(0, 0, 0, state.pts, state.qts, int(state.date.timestamp()), state.seq, 0), [])
return user
async def send_code_request(
self: 'TelegramClient',
phone: str,
*,
force_sms: bool = False,
_retry_count: int = 0) -> 'types.auth.SentCode':
"""
Signs up to Telegram if you don't have an account yet.
You must call .send_code_request(phone) first.
Sends the Telegram code needed to login to the given phone number.
**By using this method you're agreeing to Telegram's
Terms of Service. This is required and your account
will be banned otherwise.** See https://telegram.org/tos
and https://core.telegram.org/api/terms.
Args:
code (`str` | `int`):
The code sent by Telegram
first_name (`str`):
The first name to be used by the new account.
last_name (`str`, optional)
Optional last name.
Returns:
The new created :tl:`User`.
"""
me = await self.get_me()
if me:
return me
if self._tos and self._tos.text:
if self.parse_mode:
t = self.parse_mode.unparse(self._tos.text, self._tos.entities)
else:
t = self._tos.text
sys.stderr.write("{}\n".format(t))
sys.stderr.flush()
result = await self(functions.auth.SignUpRequest(
phone_number=self._phone,
phone_code_hash=self._phone_code_hash.get(self._phone, ''),
phone_code=str(code),
first_name=first_name,
last_name=last_name
))
if self._tos:
await self(
functions.help.AcceptTermsOfServiceRequest(self._tos.id))
self._self_input_peer = utils.get_input_peer(
result.user, allow_self=False
)
self._authorized = True
return result.user
async def send_code_request(self, phone, *, force_sms=False):
"""
Sends a code request to the specified phone number.
Args:
Arguments
phone (`str` | `int`):
The phone to which the code will be sent.
force_sms (`bool`, optional):
Whether to force sending as SMS.
Whether to force sending as SMS. This has been deprecated.
See `issue #4050 <https://github.com/LonamiWebs/Telethon/issues/4050>`_ for context.
Returns:
Returns
An instance of :tl:`SentCode`.
Example
.. code-block:: python
phone = '+34 123 123 123'
sent = await client.send_code_request(phone)
print(sent)
"""
if force_sms:
warnings.warn('force_sms has been deprecated and no longer works')
force_sms = False
result = None
phone = utils.parse_phone(phone) or self._phone
phone_hash = self._phone_code_hash.get(phone)
if not phone_hash:
try:
result = await self(functions.auth.SendCodeRequest(
phone, self.api_id, self.api_hash))
phone, self.api_id, self.api_hash, types.CodeSettings()))
except errors.AuthRestartError:
return self.send_code_request(phone, force_sms=force_sms)
if _retry_count > 2:
raise
return await self.send_code_request(
phone, force_sms=force_sms, _retry_count=_retry_count+1)
self._tos = result.terms_of_service
self._phone_code_hash[phone] = phone_hash = result.phone_code_hash
# TODO figure out when/if/how this can happen
if isinstance(result, types.auth.SentCodeSuccess):
raise RuntimeError('logged in right after sending the code')
# If we already sent a SMS, do not resend the code (hash may be empty)
if isinstance(result.type, types.auth.SentCodeTypeSms):
force_sms = False
# phone_code_hash may be empty, if it is, do not save it (#1283)
if result.phone_code_hash:
self._phone_code_hash[phone] = phone_hash = result.phone_code_hash
else:
force_sms = True
self._phone = phone
if force_sms:
result = await self(
functions.auth.ResendCodeRequest(phone, phone_hash))
try:
result = await self(
functions.auth.ResendCodeRequest(phone, phone_hash))
except errors.PhoneCodeExpiredError:
if _retry_count > 2:
raise
self._phone_code_hash.pop(phone, None)
self._log[__name__].info(
"Phone code expired in ResendCodeRequest, requesting a new code"
)
return await self.send_code_request(
phone, force_sms=False, _retry_count=_retry_count+1)
if isinstance(result, types.auth.SentCodeSuccess):
raise RuntimeError('logged in right after resending the code')
self._phone_code_hash[phone] = result.phone_code_hash
return result
async def log_out(self):
async def qr_login(self: 'TelegramClient', ignored_ids: typing.List[int] = None) -> custom.QRLogin:
"""
Initiates the QR login procedure.
Note that you must be connected before invoking this, as with any
other request.
It is up to the caller to decide how to present the code to the user,
whether it's the URL, using the token bytes directly, or generating
a QR code and displaying it by other means.
See the documentation for `QRLogin` to see how to proceed after this.
Arguments
ignored_ids (List[`int`]):
List of already logged-in user IDs, to prevent logging in
twice with the same user.
Returns
An instance of `QRLogin`.
Example
.. code-block:: python
def display_url_as_qr(url):
pass # do whatever to show url as a qr to the user
qr_login = await client.qr_login()
display_url_as_qr(qr_login.url)
# Important! You need to wait for the login to complete!
await qr_login.wait()
# If you have 2FA enabled, `wait` will raise `telethon.errors.SessionPasswordNeededError`.
# You should except that error and call `sign_in` with the password if this happens.
"""
qr_login = custom.QRLogin(self, ignored_ids or [])
await qr_login.recreate()
return qr_login
async def log_out(self: 'TelegramClient') -> bool:
"""
Logs out Telegram and deletes the current ``*.session`` file.
Returns:
``True`` if the operation was successful.
The client is unusable after logging out and a new instance should be created.
Returns
`True` if the operation was successful.
Example
.. code-block:: python
# Note: you will need to login again!
await client.log_out()
"""
try:
await self(functions.auth.LogOutRequest())
except errors.RPCError:
return False
self._mb_entity_cache.set_self_user(None, None, None)
self._authorized = False
await self.disconnect()
self.session.delete()
self._authorized = False
self.session = None
return True
async def edit_2fa(
self, current_password=None, new_password=None,
*, hint='', email=None):
self: 'TelegramClient',
current_password: str = None,
new_password: str = None,
*,
hint: str = '',
email: str = None,
email_code_callback: typing.Callable[[int], str] = None) -> bool:
"""
Changes the 2FA settings of the logged in user, according to the
passed parameters. Take note of the parameter explanations.
Changes the 2FA settings of the logged in user.
Review carefully the parameter explanations before using this method.
Note that this method may be *incredibly* slow depending on the
prime numbers that must be used during the process to make sure
that everything is safe.
Has no effect if both current and new password are omitted.
current_password (`str`, optional):
The current password, to authorize changing to ``new_password``.
Must be set if changing existing 2FA settings.
Must **not** be set if 2FA is currently disabled.
Passing this by itself will remove 2FA (if correct).
Arguments
current_password (`str`, optional):
The current password, to authorize changing to ``new_password``.
Must be set if changing existing 2FA settings.
Must **not** be set if 2FA is currently disabled.
Passing this by itself will remove 2FA (if correct).
new_password (`str`, optional):
The password to set as 2FA.
If 2FA was already enabled, ``current_password`` **must** be set.
Leaving this blank or ``None`` will remove the password.
new_password (`str`, optional):
The password to set as 2FA.
If 2FA was already enabled, ``current_password`` **must** be set.
Leaving this blank or `None` will remove the password.
hint (`str`, optional):
Hint to be displayed by Telegram when it asks for 2FA.
Leaving unspecified is highly discouraged.
Has no effect if ``new_password`` is not set.
hint (`str`, optional):
Hint to be displayed by Telegram when it asks for 2FA.
Leaving unspecified is highly discouraged.
Has no effect if ``new_password`` is not set.
email (`str`, optional):
Recovery and verification email. Raises ``EmailUnconfirmedError``
if value differs from current one, and has no effect if
``new_password`` is not set.
email (`str`, optional):
Recovery and verification email. If present, you must also
set `email_code_callback`, else it raises ``ValueError``.
Returns:
``True`` if successful, ``False`` otherwise.
email_code_callback (`callable`, optional):
If an email is provided, a callback that returns the code sent
to it must also be set. This callback may be asynchronous.
It should return a string with the code. The length of the
code will be passed to the callback as an input parameter.
If the callback returns an invalid code, it will raise
``CodeInvalidError``.
Returns
`True` if successful, `False` otherwise.
Example
.. code-block:: python
# Setting a password for your account which didn't have
await client.edit_2fa(new_password='I_<3_Telethon')
# Removing the password
await client.edit_2fa(current_password='I_<3_Telethon')
"""
if new_password is None and current_password is None:
return False
pass_result = await self(functions.account.GetPasswordRequest())
if isinstance(
pass_result, types.account.NoPassword) and current_password:
if email and not callable(email_code_callback):
raise ValueError('email present without email_code_callback')
pwd = await self(functions.account.GetPasswordRequest())
pwd.new_algo.salt1 += os.urandom(32)
assert isinstance(pwd, types.account.Password)
if not pwd.has_password and current_password:
current_password = None
salt_random = os.urandom(8)
salt = pass_result.new_salt + salt_random
if not current_password:
current_password_hash = salt
if current_password:
password = pwd_mod.compute_check(pwd, current_password)
else:
current_password = (
pass_result.current_salt
+ current_password.encode()
+ pass_result.current_salt
)
current_password_hash = hashlib.sha256(current_password).digest()
password = types.InputCheckPasswordEmpty()
if new_password: # Setting new password
new_password = salt + new_password.encode('utf-8') + salt
new_password_hash = hashlib.sha256(new_password).digest()
new_settings = types.account.PasswordInputSettings(
new_salt=salt,
new_password_hash=new_password_hash,
hint=hint
)
if email: # If enabling 2FA or changing email
new_settings.email = email # TG counts empty string as None
return await self(functions.account.UpdatePasswordSettingsRequest(
current_password_hash, new_settings=new_settings
))
else: # Removing existing password
return await self(functions.account.UpdatePasswordSettingsRequest(
current_password_hash,
if new_password:
new_password_hash = pwd_mod.compute_digest(
pwd.new_algo, new_password)
else:
new_password_hash = b''
try:
await self(functions.account.UpdatePasswordSettingsRequest(
password=password,
new_settings=types.account.PasswordInputSettings(
new_salt=bytes(),
new_password_hash=bytes(),
hint=hint
new_algo=pwd.new_algo,
new_password_hash=new_password_hash,
hint=hint,
email=email,
new_secure_settings=None
)
))
except errors.EmailUnconfirmedError as e:
code = email_code_callback(e.code_length)
if inspect.isawaitable(code):
code = await code
code = str(code)
await self(functions.account.ConfirmPasswordEmailRequest(code))
return True
# endregion
# region with blocks
def __enter__(self):
return self.start()
async def __aenter__(self):
return await self.start()
def __exit__(self, *args):
if self._loop.is_running():
self._loop.create_task(self.disconnect())
elif inspect.iscoroutinefunction(self.disconnect):
self._loop.run_until_complete(self.disconnect())
else:
self.disconnect()
async def __aexit__(self, *args):
await self.disconnect()
__enter__ = helpers._sync_enter
__exit__ = helpers._sync_exit
# endregion

View File

@ -1,23 +1,40 @@
from .users import UserMethods
import typing
from .. import hints
from ..tl import types, functions, custom
if typing.TYPE_CHECKING:
from .telegramclient import TelegramClient
class BotMethods(UserMethods):
async def inline_query(self, bot, query, *, offset=None, geo_point=None):
class BotMethods:
async def inline_query(
self: 'TelegramClient',
bot: 'hints.EntityLike',
query: str,
*,
entity: 'hints.EntityLike' = None,
offset: str = None,
geo_point: 'types.GeoPoint' = None) -> custom.InlineResults:
"""
Makes the given inline query to the specified bot
i.e. ``@vote My New Poll`` would be as follows:
Makes an inline query to the specified bot (``@vote New Poll``).
>>> client = ...
>>> client.inline_query('vote', 'My New Poll')
Args:
Arguments
bot (`entity`):
The bot entity to which the inline query should be made.
query (`str`):
The query that should be made to the bot.
entity (`entity`, optional):
The entity where the inline query is being made from. Certain
bots use this to display different results depending on where
it's used, such as private chats, groups or channels.
If specified, it will also be the default entity where the
message will be sent after clicked. Otherwise, the "empty
peer" will be used, which some bots may not handle correctly.
offset (`str`, optional):
The string offset to use for the bot.
@ -25,17 +42,31 @@ class BotMethods(UserMethods):
The geo point location information to send to the bot
for localised results. Available under some bots.
Returns:
Returns
A list of `custom.InlineResult
<telethon.tl.custom.inlineresult.InlineResult>`.
Example
.. code-block:: python
# Make an inline query to @like
results = await client.inline_query('like', 'Do you like Telethon?')
# Send the first result to some chat
message = await results[0].click('TelethonOffTopic')
"""
bot = await self.get_input_entity(bot)
if entity:
peer = await self.get_input_entity(entity)
else:
peer = types.InputPeerEmpty()
result = await self(functions.messages.GetInlineBotResultsRequest(
bot=bot,
peer=types.InputPeerEmpty(),
peer=peer,
query=query,
offset=offset or '',
geo_point=geo_point
))
return custom.InlineResults(self, result)
return custom.InlineResults(self, result, entity=peer if entity else None)

Some files were not shown because too many files have changed in this diff Show More