mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
952 Commits
Author | SHA1 | Date | |
---|---|---|---|
f42ec4fe0d | |||
e9f925e14c | |||
0304e6507f | |||
0f387db8d2 | |||
30664f396a | |||
cdffeba829 | |||
8e82d1c462 | |||
b5a4276282 | |||
d43292e42b | |||
99de5490a0 | |||
53ccef5e5e | |||
29b432e65a | |||
4f4c0891d9 | |||
03b1531bd7 | |||
ab496f995e | |||
44872ce87d | |||
c24d1e2d0b | |||
38694a9173 | |||
1494946d02 | |||
5facf7de92 | |||
f7008d4d99 | |||
32dc03ec44 | |||
dbff7cbb3e | |||
27e2cbc7ea | |||
592dcbfedf | |||
03b02561a5 | |||
783fe56566 | |||
2368421332 | |||
046276b491 | |||
3de8140c0b | |||
e5ad9ab383 | |||
d04e708438 | |||
200c6ccd07 | |||
75a018e18b | |||
aacc494a55 | |||
ee00d0458d | |||
a60253bf60 | |||
a80927baf9 | |||
8be9bcc8ed | |||
1824637617 | |||
df640966c2 | |||
2849e67029 | |||
d02de07142 | |||
a56fb8cc54 | |||
c5e5af96d1 | |||
5d388f7ec4 | |||
6c45511605 | |||
d8a08638a7 | |||
e2d70da694 | |||
6e1cf24946 | |||
be0fe94ee8 | |||
ef81868ebc | |||
57fb8d2fad | |||
c2590ab5ed | |||
24deb8a51d | |||
d7ebaa5bb3 | |||
601b570b85 | |||
bdaabc4752 | |||
72d088940c | |||
f1a960c56b | |||
bcc3a1afb4 | |||
d0edf44774 | |||
9c87ed3679 | |||
67cfa04737 | |||
be5d7bb73d | |||
f3a65ef9b3 | |||
99c63e9eba | |||
e89a552e06 | |||
bb4f6a7190 | |||
197dd2a582 | |||
dc3df70f9f | |||
aac9ce45a3 | |||
24e984adf8 | |||
1ed3bc2a53 | |||
ce11b6f523 | |||
8c7c7b31b2 | |||
39e3be6673 | |||
b1b2726ef6 | |||
da924dbaeb | |||
7966def331 | |||
aab560b4ee | |||
646bbb8330 | |||
339a5c01c1 | |||
615402e4f8 | |||
51b1fb7695 | |||
0881e34381 | |||
3aec66bc0d | |||
e7e7c58133 | |||
003c5db37f | |||
286188f380 | |||
dd726b0759 | |||
1bd9f5187c | |||
68330c9a07 | |||
b912e4dbaf | |||
dab80d421b | |||
247fe6e947 | |||
995814d846 | |||
36a228da92 | |||
ec86182f62 | |||
2c385bf077 | |||
56cbc2ff93 | |||
932ac9477b | |||
1e242f2263 | |||
862f17c716 | |||
ed7cf30034 | |||
100f6d77f6 | |||
d2f9c51a5a | |||
12547efa08 | |||
9410a3d310 | |||
a4e5a09ab2 | |||
ebfba49a8f | |||
6e6420a331 | |||
7ca629dc10 | |||
3ecb7cef3b | |||
e789407774 | |||
8f32dec5dd | |||
c57cfa3949 | |||
dfac26706e | |||
385fa98bc6 | |||
d68e89fc9a | |||
6023bae728 | |||
b323a868f0 | |||
583021d114 | |||
b1e5d00821 | |||
aa02ddb573 | |||
760ea5a2f0 | |||
9b279dc562 | |||
5cd97ebc96 | |||
b5ba2445d3 | |||
7adec8bd90 | |||
0603a0df4c | |||
59810b5e2a | |||
b999fea2ac | |||
a41dabf73c | |||
5407801f62 | |||
2efb33fc29 | |||
620ea5dee0 | |||
eaf44f1a6b | |||
8c62b99057 | |||
e3b126807e | |||
769ff8008e | |||
0e0e2d97c0 | |||
bb199024fd | |||
86644c05f7 | |||
3a3bab5b92 | |||
bf844ed202 | |||
fefb9d4555 | |||
a413a51221 | |||
a71030dcdd | |||
68db599790 | |||
a749acde15 | |||
5935a378ca | |||
1dd94d6e6d | |||
2fb0f3fb4b | |||
575fb9da7f | |||
c6358f35d2 | |||
20b87f2242 | |||
f4c215b0b8 | |||
1a30a9a249 | |||
e644ed910a | |||
8cb2da3775 | |||
f8e7c0f819 | |||
f241ef1eac | |||
8f8276314e | |||
9e30cfbda6 | |||
6fb9e18385 | |||
f0835a1a14 | |||
be3b6f88e8 | |||
87e811ce3e | |||
151880f391 | |||
bf91829088 | |||
56f0b0a0d4 | |||
2b8e77f749 | |||
fba425265e | |||
23069ac729 | |||
7ab93f55a6 | |||
ba2705dc82 | |||
3a1bdc2899 | |||
4e57adbcb6 | |||
600002e158 | |||
3c62e9d391 | |||
63df69aeb8 | |||
a8cf9f4ae5 | |||
b10e45f714 | |||
9624b45314 | |||
e26ad07965 | |||
55c7b6373c | |||
ceceeb7d8c | |||
b76a69e036 | |||
e5700380bd | |||
47b53b8812 | |||
2d0ebde481 | |||
271b7e0642 | |||
f516438360 | |||
7dc9abffc6 | |||
2285d0466e | |||
31dbe30489 | |||
c77307881d | |||
1a58731fb7 | |||
99df992a66 | |||
79e6a3166d | |||
8005ca2f6c | |||
b82ed70ec9 | |||
18e37f3d20 | |||
ceea457cf1 | |||
4131b05733 | |||
ad4be5c0ae | |||
a946b79839 | |||
4eeca78f2f | |||
2d6c2a345f | |||
e62eeb7ff2 | |||
76fc8fbe5e | |||
584955962e | |||
b8f442d06b | |||
891988be93 | |||
8636b282d7 | |||
2c57c5c01c | |||
07b82dc9b0 | |||
a850a0d94f | |||
d4f1444503 | |||
bab9f7bbb9 | |||
d9ace2adc8 | |||
36621bb22a | |||
99466017c5 | |||
feec1dde56 | |||
54eba946be | |||
65a272b901 | |||
6a4c7e731b | |||
2b3c86b647 | |||
e419214b49 | |||
fe6959c38e | |||
7dd53b1396 | |||
421118d9d8 | |||
cf69a06ab8 | |||
8ac6e664c5 | |||
12dbcb56d3 | |||
a46975d038 | |||
494b535a91 | |||
74f75884f3 | |||
4eae469528 | |||
41f7c07959 | |||
35ea2a2b7e | |||
522b2b487b | |||
5035e0ce80 | |||
7061091c1c | |||
5c199bd246 | |||
44dd89881d | |||
8634e65249 | |||
76dbb05259 | |||
578a9383b2 | |||
85093bded5 | |||
27d442fabf | |||
f251def304 | |||
2b822f782d | |||
8bc5b74495 | |||
70426ac274 | |||
a3a2bd5793 | |||
c3b6ee9dc0 | |||
4079772fd3 | |||
9547a8d7b1 | |||
c8b2b14157 | |||
3d5ef5b1d8 | |||
776a699a8d | |||
78afd045d8 | |||
06faed887c | |||
bc855f7610 | |||
893d5386c5 | |||
909d570dca | |||
424c77fd2c | |||
333949683f | |||
fa038c2e42 | |||
d61de35a32 | |||
13df7b5908 | |||
1de356dcc3 | |||
47e6dfd6bc | |||
3c890a7846 | |||
17971ff48b | |||
b989b7601b | |||
8c574a786a | |||
7e5f51e4ab | |||
018e4597a2 | |||
7df6b3d4c9 | |||
4facc5f7d7 | |||
4bcfc34a50 | |||
b1d5cb2129 | |||
00c8dcc19b | |||
ed7e33b4c6 | |||
74a952846c | |||
e99fb8f84f | |||
49aee14fca | |||
9267da205d | |||
9c79ba2f87 | |||
3be21ae361 | |||
42343c3a7f | |||
5a102ed8fa | |||
e1e109bef1 | |||
b5a217013a | |||
3ba9799b98 | |||
91f213ff34 | |||
8f55460924 | |||
f6b999053d | |||
99ff104a3f | |||
662c2c8797 | |||
72a0199a2f | |||
989cae597b | |||
5dd88f8223 | |||
28111bdf4e | |||
10ec897fb5 | |||
ffe3a0c3d7 | |||
f5f48db6ba | |||
183230e927 | |||
7957bc45a8 | |||
373d4d37ff | |||
0d0e37dae5 | |||
36d088dfbf | |||
9ae20b4815 | |||
e761e1e1d9 | |||
cb0256b37d | |||
ff3cbaf45b | |||
d231b1fbaa | |||
7f47f11444 | |||
0422e62f65 | |||
82e252ec46 | |||
c11a9f810c | |||
d17d28a144 | |||
dadcd5a577 | |||
afc9abc269 | |||
e01f17e3a0 | |||
48e6757686 | |||
8495229ce1 | |||
3b60f7ca67 | |||
a1930e05c2 | |||
bc067662dc | |||
518c49f23a | |||
903b1dfd50 | |||
2e199a5684 | |||
55302cb972 | |||
f47653d2e4 | |||
94b4a25980 | |||
afac177d7d | |||
2637e29dbe | |||
6d180e30f0 | |||
e2ed4cf065 | |||
8b2dea1d56 | |||
41e31de034 | |||
ae074fd5c9 | |||
60596a95b8 | |||
44531bcedf | |||
8aa8fa5986 | |||
f0e64b3653 | |||
8444ea588a | |||
b2f376a906 | |||
d0b4bb7c69 | |||
c300195b49 | |||
2493b200a4 | |||
8528ca9e4e | |||
e1a3ccadb7 | |||
a43f037bc9 | |||
7ac246b801 | |||
47624a556e | |||
8bdbc24014 | |||
e45ced958a | |||
46c803bf55 | |||
3986f33d3a | |||
c327be5a03 | |||
d8587419e1 | |||
35d7293ebd | |||
8e71a612a6 | |||
5f8d99664e | |||
aaa968c27f | |||
600c014515 | |||
0a80fafd76 | |||
4a11bb60b4 | |||
7d37374667 | |||
6ad56eb30f | |||
211f1c607d | |||
be786021dc | |||
15d287919d | |||
af991ea76e | |||
064b84ad3d | |||
39b4f0a068 | |||
246e7e31d7 | |||
48bfb7b84f | |||
dcddedcd24 | |||
2e743b4b86 | |||
af70313721 | |||
aefd666062 | |||
23d66afbb0 | |||
ed29f9316f | |||
e92dc3717e | |||
c91ce6036b | |||
cb60a1256f | |||
96569cbdac | |||
feec19b7f4 | |||
1a80fc5a0e | |||
488fb745b7 | |||
08d6ab549d | |||
f718c36ea7 | |||
ed88939110 | |||
0632cfb9b0 | |||
4979589faf | |||
d2f694516a | |||
5f8ed347a1 | |||
514880fe22 | |||
f97bb2f615 | |||
38af4f441b | |||
662a834138 | |||
25a37db2bb | |||
3e04df7080 | |||
6af3067a12 | |||
32c2178b29 | |||
6786f87d66 | |||
ebdd7d107e | |||
3713b093b6 | |||
242456d92b | |||
328cabead6 | |||
e834903bc2 | |||
556a04ca8b | |||
4b165ba3f1 | |||
4e1d5b83b6 | |||
cb4521f497 | |||
52e50f1286 | |||
3f626d37ba | |||
d6aaf0716a | |||
777a3afaaa | |||
0b1ae6ad8b | |||
7aca24a18b | |||
754ca77394 | |||
5ffd9c5755 | |||
639218b3bf | |||
b2449e64c2 | |||
708635e420 | |||
84b1aca939 | |||
9025be0ef2 | |||
a8e60b28e0 | |||
cf287af549 | |||
2d10793686 | |||
3a10c90799 | |||
f8fed5c942 | |||
12791e1366 | |||
5ed333492b | |||
9134e8dd1a | |||
43a30e7777 | |||
3f5596ddce | |||
443d81d4db | |||
6cda8d052c | |||
5969a6644c | |||
791a183af5 | |||
35214b1270 | |||
50ea288c9e | |||
34047c0121 | |||
1a70c2d613 | |||
6d18a2c22f | |||
89f515b120 | |||
11f0733974 | |||
b91eeb2752 | |||
f7cfb98b60 | |||
8bf226e6bf | |||
450ef42a83 | |||
4fc83a85ee | |||
8dca85b1f2 | |||
fc65b30e3a | |||
e138d2e1ef | |||
d29c816b79 | |||
f220a68c00 | |||
94d723cf7b | |||
662c69e09c | |||
43f026dc64 | |||
2c631b2973 | |||
401a848927 | |||
7f31f8dde8 | |||
19b8b4d2bf | |||
6515c7c494 | |||
76a48ffe82 | |||
f8f0e0c343 | |||
8129b95118 | |||
2b7e9a6180 | |||
a84c0b984b | |||
a356c9a325 | |||
4c4e8deaee | |||
f7fc538bd8 | |||
6c770d81f9 | |||
d6af33fef7 | |||
b6fee07089 | |||
a5d6b541a5 | |||
ecad88ad00 | |||
be87e4b2b9 | |||
1215eee167 | |||
9fe8565d53 | |||
1058822f85 | |||
57a57c8aca | |||
12e7879325 | |||
d14bd9a36b | |||
c168feea32 | |||
a06551daaf | |||
eadff07f79 | |||
b5e27d0fea | |||
509fae6792 | |||
b0bc49c803 | |||
e555da86dd | |||
9a5e8302be | |||
30ed6e37d3 | |||
27a79c4be5 | |||
c99bb16619 | |||
856af72599 | |||
8c8be81bb9 | |||
b2cd3c9716 | |||
1c9a9b9622 | |||
f413ccf3fb | |||
9aaa00c8fd | |||
11c2505d50 | |||
91076d0b59 | |||
1691e84d01 | |||
903de2a72c | |||
9ddc529b28 | |||
d9ca776e2c | |||
2c497edca6 | |||
bf6634bc36 | |||
2455d7013c | |||
7a6bb4dcc8 | |||
e342b9fa6b | |||
2e8151cb7d | |||
2af9209005 | |||
34b0a2404e | |||
f6d5358d1a | |||
d2e1acde6a | |||
aad9251d48 | |||
54ed2038aa | |||
1b767215b5 | |||
6f8ebbae89 | |||
d1498979d4 | |||
702763edd6 | |||
ffa0ea449b | |||
d53a881ac4 | |||
67583d3639 | |||
0f3398ed2f | |||
08dd7d1593 | |||
7e94810ece | |||
11aa5fcb85 | |||
8d65856dec | |||
740d7f44cf | |||
b8e5c43598 | |||
795a00f92c | |||
de740be506 | |||
cd89de5a9a | |||
4dc2d5f1db | |||
1b0a872619 | |||
82cf5535dd | |||
48002f280b | |||
057d130baa | |||
88e49bdaef | |||
acdc2058c5 | |||
a5ed76018d | |||
bac269d48a | |||
e69790a8fc | |||
590b27ca8a | |||
404f81fd43 | |||
89cf2658ae | |||
ea92e8696e | |||
6da88c9751 | |||
ff5f6f727a | |||
315400de47 | |||
ca33801565 | |||
c61b82ace6 | |||
8a4d2000d2 | |||
f9d7f6905b | |||
124b550de5 | |||
653c892b33 | |||
b79c3165a1 | |||
303406020b | |||
3dcd59c22e | |||
d847cfb9fa | |||
4205e46608 | |||
2044fba29a | |||
29ef0e74af | |||
46fc8da79e | |||
b9a0c3e511 | |||
a4ac83aec2 | |||
70fa5b405a | |||
c7d2731559 | |||
527351385b | |||
1b47e5cc62 | |||
eb4d58bec1 | |||
57486b18cd | |||
25ad3f055f | |||
cdb6d6760d | |||
5bd8e9d3f5 | |||
fab2f324d0 | |||
9bf4be2caf | |||
d701fd6e1d | |||
8e3c9d8d24 | |||
0b9f91c6fb | |||
468a535257 | |||
d2e7f4d8f2 | |||
ddd3664329 | |||
9e397c41cf | |||
b528b0bcf0 | |||
2f20d70e89 | |||
ac740e4755 | |||
2cf8ffcbc0 | |||
87ad4a62ff | |||
6b255deed7 | |||
ce24aa25f2 | |||
a0f25089e0 | |||
1bfdf7cf35 | |||
5b1de9f339 | |||
e956800e34 | |||
b9a57f630a | |||
851d76888b | |||
05f1c87c7d | |||
59b19fbbc1 | |||
e87907f0b8 | |||
d84aa796c0 | |||
8c20f63022 | |||
f9c7497c5c | |||
3413669a23 | |||
1a45b4844a | |||
8a9d89591b | |||
234dd8cf9f | |||
6a98d27f1a | |||
04df139efb | |||
747f14216b | |||
aee9255568 | |||
fbaf88c237 | |||
3ebc47de8b | |||
8017c8d919 | |||
2545724a6f | |||
b8770e2c04 | |||
1da5d1e0e2 | |||
bc73f345b2 | |||
a824ff967b | |||
0c420ee5e4 | |||
16838c30b6 | |||
022cdada49 | |||
fb1052824c | |||
769bb55bbe | |||
655940dcf8 | |||
3b52e5c49b | |||
700b869555 | |||
5906d632d6 | |||
bd513cd343 | |||
0cb99b42e4 | |||
dc1869167d | |||
ef22fafa7e | |||
94d1b3f7a8 | |||
ef68bed01f | |||
0eb21aa673 | |||
e6105d851c | |||
7ec33d43b3 | |||
0a856f4e83 | |||
bbe53b5e17 | |||
84257b6ba9 | |||
5b234920c7 | |||
16d4c64b00 | |||
4fb85e25f6 | |||
0ae20e1a49 | |||
e913635755 | |||
225cbf2c61 | |||
ae63b8029f | |||
e47790ded8 | |||
d0666071d8 | |||
eb8e87fdec | |||
1c98e1108c | |||
8a0bf8ca7e | |||
7490f63f25 | |||
22ab50989e | |||
1e320a5b0d | |||
96e06c248b | |||
ae92d1998e | |||
d87e0821c9 | |||
ee52241cc2 | |||
a6d35fd1de | |||
016819cd44 | |||
036441b8f6 | |||
228683aeda | |||
4fe4061a0f | |||
f873658aac | |||
13327c371a | |||
4dc7af71a0 | |||
209764a5d7 | |||
c1247249c7 | |||
a6b0e9598c | |||
7958264d64 | |||
74c3a3545d | |||
7b007dab99 | |||
8316a57845 | |||
2eb914d329 | |||
2449a3ea64 | |||
a2d91808ef | |||
23dd31752b | |||
7770fcbb33 | |||
c64fdf149e | |||
17184697ea | |||
78d86752fb | |||
7669781737 | |||
ddc1a4d66f | |||
3d9e012c40 | |||
9b9c0287ec | |||
292191038f | |||
f0e157213c | |||
18330275bd | |||
d173681a7c | |||
0830f2a0f9 | |||
45d6f8980c | |||
28417d18af | |||
e0ae119512 | |||
e851f37712 | |||
40cf8d6903 | |||
e507463741 | |||
3a055734f9 | |||
16ec573c9e | |||
2cd19a1a39 | |||
5f9cf881e6 | |||
baacafe3c0 | |||
734767177d | |||
1e7f71029c | |||
02c22c990b | |||
00d6481818 | |||
a74ed6e3ab | |||
38eb2ba833 | |||
93dc1cd92e | |||
a7469e6b14 | |||
b8f251140d | |||
e2e6b07fdb | |||
a2ceffecc8 | |||
805e3554de | |||
94d34747d1 | |||
8278eef7f3 | |||
94f1bbd402 | |||
c706a7aba3 | |||
c214f8000e | |||
81a201f19f | |||
0f0d76ca82 | |||
fa6f16ca5e | |||
a1267a7670 | |||
79c46bccae | |||
e912546680 | |||
7eeda3bc4d | |||
b5680a1c1f | |||
7514c617fc | |||
32f3a64720 | |||
3a08111f10 | |||
eeff4e7bf8 | |||
c37b3625aa | |||
a3519ff539 | |||
43ad415320 | |||
fb7c79fdfb | |||
413f081b19 | |||
cea0604ede | |||
be639ef4dd | |||
528b23770f | |||
4e472dda48 | |||
17dec34923 | |||
a17b301f1c | |||
cf830a0fb4 | |||
7e9f5b09cf | |||
969c5e76ef | |||
7346326bc3 | |||
f355796b01 | |||
5175803d0b | |||
bf9939d40e | |||
07960fe348 | |||
f4be18e082 | |||
8e9837a587 | |||
361c043872 | |||
50432dbef1 | |||
37277d74cc | |||
9ee341f5e6 | |||
0bd284afc7 | |||
4ffdada427 | |||
d8effd3f9f | |||
303e15b88d | |||
3273aa9afa | |||
9e8b11051c | |||
b9d458e643 | |||
1e6361dd57 | |||
ca2019b8f1 | |||
a230665424 | |||
8eb6e034fe | |||
d6552eb4c6 | |||
117c5a1141 | |||
3f335c37ce | |||
e7e681928d | |||
855ff40070 | |||
29a42a398b | |||
b801728924 | |||
d14e9051d4 | |||
60ca1751ca | |||
941b8ac5d0 | |||
7a08102fad | |||
325061ee96 | |||
8839f36706 | |||
1c53955d5a | |||
9cd28b88be | |||
2fb2cd6f20 | |||
036d000b95 | |||
11d4bb02a8 | |||
f4b0b61b9d | |||
4b8989f3d2 | |||
19b4e35ee5 | |||
61b4ca8a37 | |||
1038d4fafa | |||
fd1f16598b | |||
07c28830db | |||
a8fccdbeb3 | |||
c213b7732b | |||
2ca5c0d6f3 | |||
2d0c54a7f2 | |||
bb0d028750 | |||
e7b4e95fbc | |||
85ffe9936d | |||
99f6829ede | |||
55053fe413 | |||
f2971c5d6c | |||
89288b166e | |||
88bd6dcbb0 | |||
710fc273d6 | |||
9f04f0ece2 | |||
cc7ab58ed8 | |||
edf1694606 | |||
f9cbcf9b65 | |||
3a148c6e85 | |||
4522b37b9d | |||
149c54cc31 | |||
93aa37768a | |||
da729069c2 | |||
f7c4b14b77 | |||
2d5cb4fd75 | |||
f2f556ed12 | |||
b1e659da28 | |||
4526c4f8a7 | |||
efc98c084f | |||
cf61577e3e | |||
3c8faa155f | |||
6f34a22c4b | |||
d118e9edcc | |||
c33c116488 | |||
e4bb2ff4f9 | |||
92fb982a57 | |||
e734e0c974 | |||
bf91d605ea | |||
e736c59896 | |||
94844054e4 | |||
00f7c1ddc0 | |||
7f68bbbdb9 | |||
2221b59b6f | |||
3b61c7cd8a | |||
f2664d8232 | |||
4ed7be19ad | |||
93f1d9e6f8 | |||
2a103e5bbc | |||
645270861c | |||
ee4061561e | |||
b7a4d3f0b3 | |||
ee0ff8b1fb | |||
8412be1336 | |||
596df2ef2c | |||
5c6de8aa04 | |||
7dc1c5095a | |||
b17831b726 | |||
1517979bf2 | |||
571a0fb320 | |||
c3300af656 | |||
cb4a58a1e8 | |||
57ed31da71 | |||
fb3b63d441 | |||
f06bec1a44 | |||
b25435f29a | |||
a12ea63858 | |||
98f126d249 | |||
821a63e3a7 | |||
b745088a05 | |||
8abd3fe140 | |||
004f6917aa | |||
d546a4f1e9 | |||
9f6a3bfb26 | |||
a82fece950 | |||
8f13b636ed | |||
740090640a | |||
9219404a9a | |||
d5c1601bf0 | |||
83ab8a39c9 | |||
3ffdd427dd | |||
f41cc8134a | |||
20e3f731f7 | |||
97f2c0c110 | |||
b484baea37 | |||
2647777b2b | |||
b468e8c943 | |||
7b919852fa | |||
60a7595b72 | |||
893b414e33 | |||
0932e603fb | |||
2047b04a4c | |||
f513270971 | |||
53769f9e8c | |||
ba1693dcf0 | |||
f7168770ab | |||
43e1d26696 | |||
99b82b6219 | |||
968d30e246 | |||
f183575d10 | |||
edddab8956 | |||
766b45e223 | |||
dd94b8624d | |||
1350e19391 | |||
6bdc313fa6 | |||
4870764b66 | |||
754d0ad558 | |||
dbeec88ddf | |||
621b191e8d | |||
634f88323c | |||
55f6844d1f | |||
d5b7c2b49b | |||
1544efbbda | |||
185b3e007e | |||
aeeb94d386 | |||
56d1dfc045 | |||
6c8d30b063 | |||
c1bb900d91 | |||
0ee4fc8528 | |||
bded9e021d | |||
913f01dfe8 | |||
7144c79efe | |||
a9b51e039a | |||
e5a17caa9f | |||
292bb307f0 | |||
4a3e989391 | |||
09a39fcb89 | |||
efdf35796d | |||
bbf8b5d5f9 | |||
30e3fbaa1a | |||
8cf8661b7e | |||
2e1143e3d8 | |||
f6a0037f8d | |||
3a574b4596 | |||
45544d7b54 | |||
a82f4d780f | |||
814dbd2666 | |||
da176b770c | |||
b316e699e1 | |||
e095a2ffe9 | |||
3d678707ac | |||
9b3f5f5ba0 | |||
d71fdc633d | |||
0a2d1394b2 | |||
7dae316789 | |||
bc181572d9 | |||
e061aa051a | |||
666ab7609d | |||
0af4051dfd | |||
645c0b7fb4 |
7
.github/ISSUE_TEMPLATE
vendored
Normal file
7
.github/ISSUE_TEMPLATE
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Please answer these questions before submitting your issue. Thanks!
|
||||
|
||||
1. What version of pyTelegramBotAPI are you using?
|
||||
|
||||
2. What OS are you using?
|
||||
|
||||
3. What version of python are you using?
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -58,4 +58,7 @@ docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
testMain.py
|
||||
testMain.py
|
||||
|
||||
#VS Code
|
||||
.vscode/
|
||||
|
12
.travis.yml
12
.travis.yml
@ -1,7 +1,11 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
script: cd tests && py.test
|
||||
script:
|
||||
- python setup.py install
|
||||
- cd tests && py.test
|
||||
|
692
README.md
692
README.md
@ -1,14 +1,53 @@
|
||||
# pyTelegramBotAPI
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
A Python implementation for the Telegram Bot API.
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
|
||||
|
||||
See [https://core.telegram.org/bots/api](https://core.telegram.org/bots/api)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
|
||||
## How to install
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [A simple echo bot](#a-simple-echo-bot)
|
||||
* [General API Documentation](#general-api-documentation)
|
||||
* [Types](#types)
|
||||
* [Methods](#methods)
|
||||
* [General use of the API](#general-use-of-the-api)
|
||||
* [Message handlers](#message-handlers)
|
||||
* [Callback Query handlers](#callback-query-handler)
|
||||
* [Middleware handlers](#middleware-handler)
|
||||
* [TeleBot](#telebot)
|
||||
* [Reply markup](#reply-markup)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Advanced use of the API](#advanced-use-of-the-api)
|
||||
* [Asynchronous delivery of messages](#asynchronous-delivery-of-messages)
|
||||
* [Sending large text messages](#sending-large-text-messages)
|
||||
* [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot)
|
||||
* [The listener mechanism](#the-listener-mechanism)
|
||||
* [Using web hooks](#using-web-hooks)
|
||||
* [Logging](#logging)
|
||||
* [Proxy](#proxy)
|
||||
* [API conformance](#api-conformance)
|
||||
* [Change log](#change-log)
|
||||
* [F.A.Q.](#faq)
|
||||
* [Bot 2.0](#bot-20)
|
||||
* [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)
|
||||
* [The Telegram Chat Group](#the-telegram-chat-group)
|
||||
* [More examples](#more-examples)
|
||||
* [Bots using this API](#bots-using-this-api)
|
||||
|
||||
Python 2 or Python 3 is required.
|
||||
## Getting started.
|
||||
|
||||
* Install from source
|
||||
This API is tested with Python 2.6, Python 2.7, Python 3.4, Pypy and Pypy 3.
|
||||
There are two ways to install the library:
|
||||
|
||||
* Installation using pip (a Python package manager)*:
|
||||
|
||||
```
|
||||
$ pip install pyTelegramBotAPI
|
||||
```
|
||||
* Installation from source (requires git):
|
||||
|
||||
```
|
||||
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
@ -16,92 +55,266 @@ $ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
```
|
||||
|
||||
* or install with pip
|
||||
It is generally recommended to use the first option.
|
||||
|
||||
```
|
||||
$ pip install pyTelegramBotAPI
|
||||
```
|
||||
**While the API is production-ready, it is still under development and it has regular updates, do not forget to update it regularly by calling `pip install pytelegrambotapi --upgrade`*
|
||||
|
||||
## Example
|
||||
## Writing your first bot
|
||||
|
||||
* Sending a message.
|
||||
### Prerequisites
|
||||
|
||||
It is presumed that you [have obtained an API token with @BotFather](https://core.telegram.org/bots#botfather). We will call this token `TOKEN`.
|
||||
Furthermore, you have basic knowledge of the Python programming language and more importantly [the Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
|
||||
### A simple echo bot
|
||||
|
||||
The TeleBot class (defined in \__init__.py) encapsulates all API calls in a single class. It provides functions such as `send_xyz` (`send_message`, `send_document` etc.) and several ways to listen for incoming messages.
|
||||
|
||||
Create a file called `echo_bot.py`.
|
||||
Then, open the file and create an instance of the TeleBot class.
|
||||
```python
|
||||
import telebot
|
||||
|
||||
TOKEN = '<token string>'
|
||||
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
# tb.send_message(chatid, message)
|
||||
tb.send_message(281281, 'gogo power ranger')
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
```
|
||||
*Note: Make sure to actually replace TOKEN with your own API token.*
|
||||
|
||||
* Echo Bot
|
||||
After that declaration, we need to register some so-called message handlers. Message handlers define filters which a message must pass. If a message passes the filter, the decorated function is called and the incoming message is passed as an argument.
|
||||
|
||||
Let's define a message handler which handles incoming `/start` and `/help` commands.
|
||||
```python
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
```
|
||||
A function which is decorated by a message handler __can have an arbitrary name, however, it must have only one parameter (the message)__.
|
||||
|
||||
Let's add another handler:
|
||||
```python
|
||||
@bot.message_handler(func=lambda m: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
```
|
||||
This one echoes all incoming text messages back to the sender. It uses a lambda function to test a message. If the lambda returns True, the message is handled by the decorated function. Since we want all messages to be handled by this function, we simply always return True.
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
|
||||
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
|
||||
```python
|
||||
bot.polling()
|
||||
```
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
```
|
||||
To start the bot, simply open up a terminal and enter `python echo_bot.py` to run the bot! Test it by sending commands ('/start' and '/help') and arbitrary text messages.
|
||||
|
||||
## General API Documentation
|
||||
|
||||
### Types
|
||||
|
||||
All types are defined in types.py. They are all completely in line with the [Telegram API's definition of the types](https://core.telegram.org/bots/api#available-types), except for the Message's `from` field, which is renamed to `from_user` (because `from` is a Python reserved token). Thus, attributes such as `message_id` can be accessed directly with `message.message_id`. Note that `message.chat` can be either an instance of `User` or `GroupChat` (see [How can I distinguish a User and a GroupChat in message.chat?](#how-can-i-distinguish-a-user-and-a-groupchat-in-messagechat)).
|
||||
|
||||
The Message object also has a `content_type`attribute, which defines the type of the Message. `content_type` can be one of the following strings:
|
||||
`text`, `audio`, `document`, `photo`, `sticker`, `video`, `video_note`, `voice`, `location`, `contact`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `migrate_to_chat_id`, `migrate_from_chat_id`, `pinned_message`.
|
||||
|
||||
You can use some types in one function. Example:
|
||||
|
||||
```content_types=["text", "sticker", "pinned_message", "photo", "audio"]```
|
||||
|
||||
### Methods
|
||||
|
||||
All [API methods](https://core.telegram.org/bots/api#available-methods) are located in the TeleBot class. They are renamed to follow common Python naming conventions. E.g. `getMe` is renamed to `get_me` and `sendMessage` to `send_message`.
|
||||
|
||||
### General use of the API
|
||||
|
||||
Outlined below are some general use cases of the API.
|
||||
|
||||
#### Message handlers
|
||||
A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters.
|
||||
Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
|
||||
```python
|
||||
@bot.message_handler(filters)
|
||||
def function_name(message):
|
||||
bot.reply_to(message, "This is a message handler")
|
||||
```
|
||||
`function_name` is not bound to any restrictions. Any function name is permitted with message handlers. The function must accept at most one argument, which will be the message that the function must handle.
|
||||
`filters` is a list of keyword arguments.
|
||||
A filter is declared in the following manner: `name=argument`. One handler may have multiple filters.
|
||||
TeleBot supports the following filters:
|
||||
|
||||
|name|argument(s)|Condition|
|
||||
|:---:|---| ---|
|
||||
|content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.|
|
||||
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))|
|
||||
|commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.|
|
||||
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
|
||||
|
||||
Here are some examples of using the filters and message handlers:
|
||||
|
||||
```python
|
||||
import telebot
|
||||
import time
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
# Handles all text messages that contains the commands '/start' or '/help'.
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def handle_start_help(message):
|
||||
pass
|
||||
|
||||
# Handles all sent documents and audio files
|
||||
@bot.message_handler(content_types=['document', 'audio'])
|
||||
def handle_docs_audio(message):
|
||||
pass
|
||||
|
||||
def listener(*messages):
|
||||
"""
|
||||
When new messages arrive TeleBot will call this function.
|
||||
"""
|
||||
for m in messages:
|
||||
chatid = m.chat.id
|
||||
if m.content_type == 'text':
|
||||
text = m.text
|
||||
tb.send_message(chatid, text)
|
||||
# Handles all text messages that match the regular expression
|
||||
@bot.message_handler(regexp="SOME_REGEXP")
|
||||
def handle_message(message):
|
||||
pass
|
||||
|
||||
# Handles all messages for which the lambda returns True
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def handle_text_doc(message):
|
||||
pass
|
||||
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
tb.set_update_listener(listener) #register listener
|
||||
tb.polling()
|
||||
# Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plain'
|
||||
|
||||
while True: # Don't let the main Thread end.
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message):
|
||||
pass
|
||||
|
||||
# Handlers can be stacked to create a function which will be called if either message_handler is eligible
|
||||
# This handler will be called if the message starts with '/hello' OR is some emoji
|
||||
@bot.message_handler(commands=['hello'])
|
||||
@bot.message_handler(func=lambda msg: msg.text.encode("utf-8") == SOME_FANCY_EMOJI)
|
||||
def send_something(message):
|
||||
pass
|
||||
```
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
|
||||
## TeleBot API usage
|
||||
#### Edited Message handlers
|
||||
|
||||
@bot.edited_message_handler(filters)
|
||||
|
||||
#### channel_post_handler
|
||||
|
||||
@bot.channel_post_handler(filters)
|
||||
|
||||
#### edited_channel_post_handler
|
||||
|
||||
@bot.edited_channel_post_handler(filters)
|
||||
|
||||
#### Callback Query Handler
|
||||
|
||||
In bot2.0 update. You can get `callback_query` in update object. In telebot use `callback_query_handler` to process callback queries.
|
||||
|
||||
```python
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def test_callback(call):
|
||||
logger.info(call)
|
||||
```
|
||||
#### Middleware Handler
|
||||
|
||||
A middleware handler is a function that allows you to modify requests or the bot context as they pass through the
|
||||
Telegram to the bot. You can imagine middleware as a chain of logic connection handled before any other handlers are
|
||||
executed.
|
||||
|
||||
```python
|
||||
@bot.middleware_handler(update_types=['message'])
|
||||
def modify_message(bot_instance, message):
|
||||
# modifying the message before it reaches any other handler
|
||||
message.another_text = message.text + ':changed'
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
# the message is already modified when it reaches message handler
|
||||
assert message.another_text == message.text + ':changed'
|
||||
```
|
||||
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
|
||||
|
||||
#### TeleBot
|
||||
```python
|
||||
import telebot
|
||||
import time
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
|
||||
|
||||
# Upon calling this function, TeleBot starts polling the Telegram servers for new messages.
|
||||
# - none_stop: True/False (default False) - Don't stop polling when receiving an error from the Telegram servers
|
||||
# - interval: True/False (default False) - The interval between polling requests
|
||||
# Note: Editing this parameter harms the bot's response time
|
||||
# - timeout: integer (default 20) - Timeout in seconds for long polling.
|
||||
tb.polling(none_stop=False, interval=0, timeout=20)
|
||||
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# setWebhook
|
||||
tb.set_webhook(url="http://example.com", certificate=open('mycert.pem'))
|
||||
# unset webhook
|
||||
tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chatid, text)
|
||||
tb.send_message(chat_id, text)
|
||||
|
||||
# forwardMessage
|
||||
# tb.forward_message(10894,926,3)
|
||||
tb.forward_message(to_chat_id, from_chat_id, message_id)
|
||||
|
||||
# All send_xyz functions which can take a file as an argument, can also take a file_id instead of a file.
|
||||
# sendPhoto
|
||||
photo = open('/tmp/photo.png', 'rb')
|
||||
tb.send_photo(chat_id, photo)
|
||||
tb.send_photo(chat_id, "FILEID")
|
||||
|
||||
# sendAudio
|
||||
audio = open('/tmp/audio.ogg', 'rb')
|
||||
audio = open('/tmp/audio.mp3', 'rb')
|
||||
tb.send_audio(chat_id, audio)
|
||||
tb.send_audio(chat_id, "FILEID")
|
||||
|
||||
## sendAudio with duration, performer and title.
|
||||
tb.send_audio(CHAT_ID, file_data, 1, 'eternnoir', 'pyTelegram')
|
||||
|
||||
# sendVoice
|
||||
voice = open('/tmp/voice.ogg', 'rb')
|
||||
tb.send_voice(chat_id, voice)
|
||||
tb.send_voice(chat_id, "FILEID")
|
||||
|
||||
# sendDocument
|
||||
doc = open('/tmp/file.txt', 'rb')
|
||||
tb.send_document(chat_id, doc)
|
||||
tb.send_document(chat_id, "FILEID")
|
||||
|
||||
# sendSticker
|
||||
sti = open('/tmp/sti.webp', 'rb')
|
||||
tb.send_sticker(chat_id, sti)
|
||||
tb.send_sticker(chat_id, "FILEID")
|
||||
|
||||
# sendVideo
|
||||
video = open('/tmp/video.mp4', 'rb')
|
||||
tb.send_video(chat_id, video)
|
||||
tb.send_video(chat_id, "FILEID")
|
||||
|
||||
# sendVideoNote
|
||||
videonote = open('/tmp/videonote.mp4', 'rb')
|
||||
tb.send_video_note(chat_id, videonote)
|
||||
tb.send_video_note(chat_id, "FILEID")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
@ -111,123 +324,332 @@ tb.send_location(chat_id, lat, lon)
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# Use the ReplyKeyboardMarkup class.
|
||||
# Thanks pevdh.
|
||||
# getFile
|
||||
# Downloading a file is straightforward
|
||||
# Returns a File object
|
||||
import requests
|
||||
file_info = tb.get_file(file_id)
|
||||
|
||||
file = requests.get('https://api.telegram.org/file/bot{0}/{1}'.format(API_TOKEN, file_info.file_path))
|
||||
|
||||
|
||||
```
|
||||
#### Reply markup
|
||||
All `send_xyz` functions of TeleBot take an optional `reply_markup` argument. This argument must be an instance of `ReplyKeyboardMarkup`, `ReplyKeyboardRemove` or `ForceReply`, which are defined in types.py.
|
||||
|
||||
```python
|
||||
from telebot import types
|
||||
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add('a', 'v', 'd')
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
# Using the ReplyKeyboardMarkup class
|
||||
# It's constructor can take the following optional arguments:
|
||||
# - resize_keyboard: True/False (default False)
|
||||
# - one_time_keyboard: True/False (default False)
|
||||
# - selective: True/False (default False)
|
||||
# - row_width: integer (default 3)
|
||||
# row_width is used in combination with the add() function.
|
||||
# It defines how many buttons are fit on each row before continuing on the next row.
|
||||
markup = types.ReplyKeyboardMarkup(row_width=2)
|
||||
itembtn1 = types.KeyboardButton('a')
|
||||
itembtn2 = types.KeyboardButton('v')
|
||||
itembtn3 = types.KeyboardButton('d')
|
||||
markup.add(itembtn1, itembtn2, itembtn3)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
# or add KeyboardButton one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
itembtna = types.KeyboardButton('a')
|
||||
itembtnv = types.KeyboardButton('v')
|
||||
itembtnc = types.KeyboardButton('c')
|
||||
itembtnd = types.KeyboardButton('d')
|
||||
itembtne = types.KeyboardButton('e')
|
||||
markup.row(itembtna, itembtnv)
|
||||
markup.row(itembtnc, itembtnd, itembtne)
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
```
|
||||
The last example yields this result:
|
||||
|
||||

|
||||
|
||||
```python
|
||||
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardRemove(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
```
|
||||
|
||||
## Creating a Telegram bot with the pyTelegramBotAPI
|
||||
There are two ways to define a Telegram Bot with the pyTelegramBotAPI.
|
||||
```python
|
||||
# ForceReply: forces a user to reply to a message
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ForceReply(selective=False)
|
||||
tb.send_message(chat_id, "Send me another word:", reply_markup=markup)
|
||||
```
|
||||
ForceReply:
|
||||
|
||||

|
||||
|
||||
### Inline Mode
|
||||
|
||||
More information about [Inline mode](https://core.telegram.org/bots/inline).
|
||||
|
||||
#### inline_handler
|
||||
|
||||
Now, you can use inline_handler to get inline queries in telebot.
|
||||
|
||||
```python
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
# Query message is text
|
||||
```
|
||||
|
||||
|
||||
#### chosen_inline_handler
|
||||
|
||||
Use chosen_inline_handler to get chosen_inline_result in telebot. Don't forgot add the /setinlinefeedback
|
||||
command for @Botfather.
|
||||
|
||||
More information : [collecting-feedback](https://core.telegram.org/bots/inline#collecting-feedback)
|
||||
|
||||
```python
|
||||
@bot.chosen_inline_handler(func=lambda chosen_inline_result: True)
|
||||
def test_chosen(chosen_inline_result):
|
||||
# Process all chosen_inline_result.
|
||||
```
|
||||
|
||||
#### answer_inline_query
|
||||
|
||||
```python
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result', types.InputTextMessageContent('Result message.'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('Result message2.'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
```
|
||||
### Working with entities:
|
||||
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
|
||||
Attributes:
|
||||
* `type`
|
||||
* `url`
|
||||
* `offset`
|
||||
* `length`
|
||||
* `user`
|
||||
|
||||
|
||||
**Here's an Example:**`message.entities[num].<attribute>`<br>
|
||||
Here `num` is the entity number or order of entity in a reply, for if incase there are multiple entities in the reply/message.<br>
|
||||
`message.entities` returns a list of entities object. <br>
|
||||
`message.entities[0].type` would give the type of the first entity<br>
|
||||
Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra details
|
||||
|
||||
## Advanced use of the API
|
||||
|
||||
### Asynchronous delivery of messages
|
||||
There exists an implementation of TeleBot which executes all `send_xyz` and the `get_me` functions asynchronously. This can speed up you bot __significantly__, but it has unwanted side effects if used without caution.
|
||||
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
|
||||
```python
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
```
|
||||
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
task = tb.get_me() # Execute an API call
|
||||
# Do some other operations...
|
||||
a = 0
|
||||
for a in range(100):
|
||||
a += 10
|
||||
|
||||
result = task.wait() # Get the result of the execution
|
||||
```
|
||||
*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.*
|
||||
|
||||
### Sending large text messages
|
||||
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
|
||||
```python
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
|
||||
# Split the text each 3000 characters.
|
||||
# split_string returns a list with the splitted text.
|
||||
splitted_text = util.split_string(large_text, 3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
```
|
||||
### Controlling the amount of Threads used by TeleBot
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
- threaded: True/False (default True). A flag to indicate whether
|
||||
TeleBot should execute message handlers on it's polling Thread.
|
||||
|
||||
### The listener mechanism
|
||||
* First, create a TeleBot instance.
|
||||
```python
|
||||
import telebot
|
||||
As an alternative to the message handlers, one can also register a function as a listener to TeleBot.
|
||||
|
||||
TOKEN = '<token string>'
|
||||
NOTICE: handlers won't disappear! Your message will be processed both by handlers and listeners. Also, it's impossible to predict which will work at first because of threading. If you use threaded=False, custom listeners will work earlier, after them handlers will be called.
|
||||
Example:
|
||||
```python
|
||||
def handle_messages(messages):
|
||||
for message in messages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
```
|
||||
* Then, define a listener function.
|
||||
```python
|
||||
def echo_messages(*messages):
|
||||
"""
|
||||
Echoes all incoming messages of content_type 'text'.
|
||||
"""
|
||||
for m in messages:
|
||||
chatid = m.chat.id
|
||||
if m.content_type == 'text':
|
||||
text = m.text
|
||||
bot.send_message(chatid, text)
|
||||
```
|
||||
* Now, register your listener with the TeleBot instance and call TeleBot#polling()
|
||||
```python
|
||||
bot.set_update_listener(echo_messages)
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
while True: # Don't let the main Thread end.
|
||||
pass
|
||||
```
|
||||
* use Message's content_type attribute to check the type of Message. Now Message supports content types:
|
||||
* text
|
||||
* audio
|
||||
* document
|
||||
* photo
|
||||
* sticker
|
||||
* video
|
||||
* location
|
||||
* contact
|
||||
* new_chat_participant
|
||||
* left_chat_participant
|
||||
* new_chat_title
|
||||
* new_chat_photo
|
||||
* delete_chat_photo
|
||||
* group_chat_created
|
||||
* That's it!
|
||||
|
||||
### The decorator mechanism
|
||||
* First, create a TeleBot instance.
|
||||
### Using web hooks
|
||||
When using webhooks telegram sends one Update per call, for processing it you should call process_new_messages([update.message]) when you recieve it.
|
||||
|
||||
There are some examples using webhooks in the *examples/webhook_examples* directory.
|
||||
|
||||
### Logging
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
|
||||
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
|
||||
|
||||
```python
|
||||
import telebot
|
||||
import logging
|
||||
|
||||
TOKEN = '<token string>'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
```
|
||||
* Next, define all of your so-called message handlers and decorate them with @bot.message_handler
|
||||
|
||||
### Proxy
|
||||
|
||||
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
|
||||
|
||||
```python
|
||||
# Handle /start and /help
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def command_help(message):
|
||||
bot.reply_to(message, "Hello, did someone call for help?")
|
||||
|
||||
# Handles all messages which text matches the regex regexp.
|
||||
# See https://en.wikipedia.org/wiki/Regular_expression
|
||||
# This regex matches all sent url's.
|
||||
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
bot.reply_to(message, "I shouldn't open that url, should I?")
|
||||
from telebot import apihelper
|
||||
|
||||
# Handle all sent documents of type 'text/plain'.
|
||||
@bot.message_handler(func=lambda message: message.document.mime_type == 'text/plain', content_types=['document'])
|
||||
def command_handle_document(message):
|
||||
bot.reply_to(message, "Document received, sir!")
|
||||
|
||||
# Default command handler. A lambda expression which always returns True is used for this purpose.
|
||||
@bot.message_handler(func=lambda message: True, content_types=['audio', 'video', 'document', 'text', 'location', 'contact', 'sticker'])
|
||||
def default_command(message):
|
||||
bot.reply_to(message, "This is the default command handler.")
|
||||
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
|
||||
```
|
||||
* And finally, call bot.polling()
|
||||
|
||||
If you want to use socket5 proxy you need install dependency `pip install requests[socks]` and make sure, that you have the latest version of `gunicorn`, `PySocks`, `pyTelegramBotAPI`, `requests` and `urllib3`.
|
||||
|
||||
```python
|
||||
bot.polling()
|
||||
|
||||
while True: # Don't end the main thread.
|
||||
pass
|
||||
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
|
||||
```
|
||||
Use whichever mechanism fits your purpose! It is even possible to mix and match.
|
||||
|
||||
## TODO
|
||||
|
||||
- [x] getMe
|
||||
- [x] sendMessage
|
||||
- [x] forwardMessage
|
||||
- [x] sendPhoto
|
||||
- [x] sendAudio
|
||||
- [x] sendDocument
|
||||
- [x] sendSticker
|
||||
- [x] sendVideo
|
||||
- [x] sendLocation
|
||||
- [x] sendChatAction
|
||||
- [x] getUserProfilePhotos
|
||||
- [x] getUpdate
|
||||
## API conformance
|
||||
|
||||
_Checking is in progress..._
|
||||
|
||||
✅ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017) _- To be checked..._
|
||||
|
||||
* ✔ [Bot API 3.4](https://core.telegram.org/bots/api-changelog#october-11-2017)
|
||||
* ✔ [Bot API 3.3](https://core.telegram.org/bots/api-changelog#august-23-2017)
|
||||
* ✔ [Bot API 3.2](https://core.telegram.org/bots/api-changelog#july-21-2017)
|
||||
* ✔ [Bot API 3.1](https://core.telegram.org/bots/api-changelog#june-30-2017)
|
||||
* ✔ [Bot API 3.0](https://core.telegram.org/bots/api-changelog#may-18-2017)
|
||||
* ✔ [Bot API 2.3.1](https://core.telegram.org/bots/api-changelog#december-4-2016)
|
||||
* ✔ [Bot API 2.3](https://core.telegram.org/bots/api-changelog#november-21-2016)
|
||||
* ✔ [Bot API 2.2](https://core.telegram.org/bots/api-changelog#october-3-2016)
|
||||
* ✔ [Bot API 2.1](https://core.telegram.org/bots/api-changelog#may-22-2016)
|
||||
* ✔ [Bot API 2.0](https://core.telegram.org/bots/api-changelog#april-9-2016)
|
||||
|
||||
|
||||
## Change log
|
||||
|
||||
27.04.2020 - Poll and Dice are up to date.
|
||||
Python2 conformance is not checked any more due to EOL.
|
||||
|
||||
11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking.
|
||||
|
||||
06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### Bot 2.0
|
||||
|
||||
April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface.
|
||||
|
||||
[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130)
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
-
|
||||
```python
|
||||
if message.chat.type == "private":
|
||||
# private chat message
|
||||
|
||||
if message.chat.type == "group":
|
||||
# group chat message
|
||||
|
||||
if message.chat.type == "supergroup":
|
||||
# supergroup chat message
|
||||
|
||||
if message.chat.type == "channel":
|
||||
# channel message
|
||||
|
||||
```
|
||||
|
||||
## The Telegram Chat Group
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
|
||||
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
|
||||
|
||||
## More examples
|
||||
|
||||
* [Echo Bot](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/echo_bot.py)
|
||||
* [Deep Linking](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/deep_linking.py)
|
||||
* [next_step_handler Example](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
|
||||
|
||||
## Bots using this API
|
||||
* [SiteAlert bot](https://telegram.me/SiteAlert_bot) ([source](https://github.com/ilteoood/SiteAlert-Python)) by *ilteoood* - Monitors websites and sends a notification on changes
|
||||
* [TelegramLoggingBot](https://github.com/aRandomStranger/TelegramLoggingBot) by *aRandomStranger*
|
||||
* [Send to Kindle Bot](https://telegram.me/Send2KindleBot) by *GabrielRF* - Send to Kindle files or links to files.
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) ([source](https://github.com/GabrielRF/telegram-lmgtfy_bot)) by *GabrielRF* - Let me Google that for you.
|
||||
* [Telegram UrlProBot](https://github.com/GabrielRF/telegram-urlprobot) ([source](https://github.com/GabrielRF/telegram-urlprobot)) by *GabrielRF* - URL shortener and URL expander.
|
||||
* [Telegram Proxy Bot](https://bitbucket.org/master_groosha/telegram-proxy-bot) by *Groosha* - A simple BITM (bot-in-the-middle) for Telegram acting as some kind of "proxy".
|
||||
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte* - `Credits for the original version of this bot goes to` **Groosha** `, simply added certain features which I thought were needed`.
|
||||
* [RadRetroRobot](https://github.com/Tronikart/RadRetroRobot) by *Tronikart* - Multifunctional Telegram Bot RadRetroRobot.
|
||||
* [League of Legends bot](https://telegram.me/League_of_Legends_bot) ([source](https://github.com/i32ropie/lol)) by *i32ropie*
|
||||
* [NeoBot](https://github.com/neoranger/NeoBot) by [@NeoRanger](https://github.com/neoranger)
|
||||
* [TagAlertBot](https://github.com/pitasi/TagAlertBot) by *pitasi*
|
||||
* [ComedoresUGRbot](http://telegram.me/ComedoresUGRbot) ([source](https://github.com/alejandrocq/ComedoresUGRbot)) by [*alejandrocq*](https://github.com/alejandrocq) - Telegram bot to check the menu of Universidad de Granada dining hall.
|
||||
* [picpingbot](https://web.telegram.org/#/im?p=%40picpingbot) - Fun anonymous photo exchange by Boogie Muffin.
|
||||
* [TheZigZagProject](https://github.com/WebShark025/TheZigZagProject) - The 'All In One' bot for Telegram! by WebShark025
|
||||
* [proxybot](https://github.com/p-hash/proxybot) - Simple Proxy Bot for Telegram. by p-hash
|
||||
* [DonantesMalagaBot](https://github.com/vfranch/DonantesMalagaBot)- DonantesMalagaBot facilitates information to Malaga blood donors about the places where they can donate today or in the incoming days. It also records the date of the last donation so that it helps the donors to know when they can donate again. - by vfranch
|
||||
* [DuttyBot](https://github.com/DmytryiStriletskyi/DuttyBot) by *Dmytryi Striletskyi* - Timetable for one university in Kiev.
|
||||
* [dailypepebot](https://telegram.me/dailypepebot) by [*Jaime*](https://github.com/jiwidi/Dailypepe) - Get's you random pepe images and gives you their id, then you can call this image with the number.
|
||||
* [DailyQwertee](https://t.me/DailyQwertee) by [*Jaime*](https://github.com/jiwidi/DailyQwertee) - Bot that manages a channel that sends qwertee daily tshirts every day at 00:00
|
||||
* [wat-bridge](https://github.com/rmed/wat-bridge) by [*rmed*](https://github.com/rmed) - Send and receive messages to/from WhatsApp through Telegram
|
||||
* [flibusta_bot](https://github.com/Kurbezz/flibusta_bot) by [*Kurbezz*](https://github.com/Kurbezz)
|
||||
* [EmaProject](https://github.com/halkliff/emaproject) by [*halkliff*](https://github.com/halkliff) - Ema - Eastern Media Assistant was made thinking on the ease-to-use feature. Coding here is simple, as much as is fast and powerful.
|
||||
* [filmratingbot](http://t.me/filmratingbot)([source](https://github.com/jcolladosp/film-rating-bot)) by [*jcolladosp*](https://github.com/jcolladosp) - Telegram bot using the Python API that gets films rating from IMDb and metacritic
|
||||
* [you2mp3bot](http://t.me/you2mp3bot)([link](https://storebot.me/bot/you2mp3bot)) - This bot can convert a Youtube video to Mp3. All you need is send the URL video.
|
||||
* [Send2Kindlebot](http://t.me/Send2KindleBot) ([source](https://github.com/GabrielRF/Send2KindleBot)) by *GabrielRF* - Send to Kindle service.
|
||||
* [RastreioBot](http://t.me/RastreioBot) ([source](https://github.com/GabrielRF/RastreioBot)) by *GabrielRF* - Bot used to track packages on the Brazilian Mail Service.
|
||||
* [filex_bot](http://t.me/filex_bot)([link](https://github.com/victor141516/FileXbot-telegram))
|
||||
* [Spbu4UBot](http://t.me/Spbu4UBot)([link](https://github.com/EeOneDown/spbu4u)) by *EeOneDown* - Bot with timetables for SPbU students.
|
||||
* [SmartySBot](http://t.me/ZDU_bot)([link](https://github.com/0xVK/SmartySBot)) by *0xVK* - Telegram timetable bot, for Zhytomyr Ivan Franko State University students.
|
||||
* [yandex_music_bot](http://t.me/yandex_music_bot)- Downloads tracks/albums/public playlists from Yandex.Music streaming service for free.
|
||||
* [LearnIt](https://t.me/LearnItbot)([link](https://github.com/tiagonapoli/LearnIt)) - A Telegram Bot created to help people to memorize other languages’ vocabulary.
|
||||
* [MusicQuiz_bot](https://t.me/MusicQuiz_bot) by [Etoneja](https://github.com/Etoneja) - Listen to audio samples and try to name the performer of the song.
|
||||
* [Bot-Telegram-Shodan ](https://github.com/rubenleon/Bot-Telegram-Shodan) by [rubenleon](https://github.com/rubenleon)
|
||||
* [MandangoBot](https://t.me/MandangoBot) by @Alvaricias - Bot for managing Marvel Strike Force alliances (only in spanish, atm).
|
||||
* [ManjaroBot](https://t.me/ManjaroBot) by [@NeoRanger](https://github.com/neoranger) - Bot for Manjaro Linux Spanish group with a lot of info for Manjaro Newbies.
|
||||
* [VigoBusTelegramBot](https://t.me/vigobusbot) ([GitHub](https://github.com/Pythoneiro/VigoBus-TelegramBot)) - Bot that provides buses coming to a certain stop and their remaining time for the city of Vigo (Galicia - Spain)
|
||||
* [kaishnik-bot](https://t.me/kaishnik_bot) ([source](https://github.com/airatk/kaishnik-bot)) by *airatk* - bot which shows all the necessary information to KNTRU-KAI students.
|
||||
* [Creation Date](https://t.me/creationdatebot) by @karipov - interpolates account creation dates based on telegram given ID’s
|
||||
* [m0xbot](https://t.me/m0xbot) by [kor0p](https://github.com/kor0p) - tic-tac-toe.
|
||||
* [kboardbot](https://t.me/kboardbot) by [kor0p](https://github.com/kor0p) - inline switches keyboard layout (English, Hebrew, Ukrainian, Russian).
|
||||
* [Robbie](https://t.me/romdeliverybot) ([source](https://github.com/FacuM/romdeliverybot_support)) by @FacuM - Support Telegram bot for developers and maintainers.
|
||||
* [AsadovBot](https://t.me/asadov_bot) ([source](https://github.com/desexcile/BotApi)) by @DesExcile - Сatalog of poems by Eduard Asadov.
|
||||
* [thesaurus_com_bot](https://t.me/thesaurus_com_bot) ([source](https://github.com/LeoSvalov/words-i-learn-bot)) by @LeoSvalov - words and synonyms from [dictionary.com](https://www.dictionary.com) and [thesaurus.com](https://www.thesaurus.com) in the telegram.
|
||||
* [InfoBot](https://t.me/info2019_bot) ([source](https://github.com/irevenko/info-bot)) by @irevenko - An all-round bot that displays some statistics (weather, time, crypto etc...)
|
||||
* [FoodBot](https://t.me/ChensonUz_bot) ([source](https://github.com/Fliego/old_restaurant_telegram_chatbot)) by @Fliego - a simple bot for food ordering
|
||||
* [Sporty](https://t.me/SportydBot) ([source](https://github.com/0xnu/sporty)) by @0xnu - Telegram bot for displaying the latest news, sports schedules and injury updates.
|
||||
* [Neural style transfer](https://t.me/ebanyivolshebnikBot) ([source](https://github.com/timbyxty/StyleTransfer-tgbot)) by @timbyxty - bot for transferring style from one picture to another based on neural network.
|
||||
|
||||
Want to have your bot listed here? Send a Telegram message to @eternnoir or @pevdh.
|
||||
|
77
examples/deep_linking.py
Normal file
77
examples/deep_linking.py
Normal file
@ -0,0 +1,77 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This example shows how to implement deep linking (https://core.telegram.org/bots#deep-linking)
|
||||
# with the pyTelegramBotAPI.
|
||||
# Note: This is not a working, production-ready sample.
|
||||
#
|
||||
# In this example we are connecting a user account on a website with a Telegram bot.
|
||||
# Implementing this will enable you to push notifications (and other content) to your users' Telegram account.
|
||||
# In this explanation the word 'database' can refer to any form of key-value storage.
|
||||
# The deep linking explained:
|
||||
#
|
||||
# 1. Let the user log in on an actual website with actual username-password authentication.
|
||||
#
|
||||
# 2. Generate a unique hashcode (we will call it unique_code)
|
||||
#
|
||||
# 3. Save unique_code->username to the database.
|
||||
#
|
||||
# 4. Show the user the URL https://telegram.me/YOURBOTNAME?start=unique_code
|
||||
#
|
||||
# 5. Now as soon as the user opens this URL in Telegram and presses 'Start',
|
||||
# your bot will receive a text message containing '/start unique_code',
|
||||
# where unique_code is of course replaced by the actual hashcode.
|
||||
#
|
||||
# 6. Let the bot retrieve the username by querying the database for unique_code.
|
||||
#
|
||||
# 7. Save chat_id->username to the database.
|
||||
#
|
||||
# 8. Now when your bot receives another message, it can query message.chat.id in the database
|
||||
# to check if the message is from this specific user. (And handle accordingly) or
|
||||
# you can push messages to the user using his chat id.
|
||||
#
|
||||
# Steps 1 to 4 will have to be implemented in a web server, using a language such as PHP, Python, C# or Java. These
|
||||
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
def extract_unique_code(text):
|
||||
# Extracts the unique_code from the sent /start command.
|
||||
return text.split()[1] if len(text.split()) > 1 else None
|
||||
|
||||
|
||||
def in_storage(unique_code):
|
||||
# (pseudo-code) Should check if a unique code exists in storage
|
||||
return True
|
||||
|
||||
|
||||
def get_username_from_storage(unique_code):
|
||||
# (pseudo-code) Does a query to the storage, retrieving the associated username
|
||||
# Should be replaced by a real database-lookup.
|
||||
return "ABC" if in_storage(unique_code) else None
|
||||
|
||||
|
||||
def save_chat_id(chat_id, username):
|
||||
# (pseudo-code) Save the chat_id->username to storage
|
||||
# Should be replaced by a real database query.
|
||||
pass
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def send_welcome(message):
|
||||
unique_code = extract_unique_code(message.text)
|
||||
if unique_code: # if the '/start' command contains a unique_code
|
||||
username = get_username_from_storage(unique_code)
|
||||
if username: # if the username exists in our database
|
||||
save_chat_id(message.chat.id, username)
|
||||
reply = "Hello {0}, how are you?".format(username)
|
||||
else:
|
||||
reply = "I have no clue who you are..."
|
||||
else:
|
||||
reply = "Please visit me via a provided URL from the website."
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
|
||||
bot.polling()
|
133
examples/detailed_example/detailed_example.py
Normal file
133
examples/detailed_example/detailed_example.py
Normal file
@ -0,0 +1,133 @@
|
||||
"""
|
||||
This is a detailed example using almost every command of the API
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
|
||||
knownUsers = [] # todo: save these in a file,
|
||||
userStep = {} # so they won't reset every time the bot restarts
|
||||
|
||||
commands = { # command description used in the "help" command
|
||||
'start' : 'Get used to the bot',
|
||||
'help' : 'Gives you information about the available commands',
|
||||
'sendLongText': 'A test using the \'send_chat_action\' command',
|
||||
'getImage' : 'A test using multi-stage messages, custom keyboard, and media sending'
|
||||
}
|
||||
|
||||
imageSelect = types.ReplyKeyboardMarkup(one_time_keyboard=True) # create the image selection keyboard
|
||||
imageSelect.add('Mickey', 'Minnie')
|
||||
|
||||
hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard
|
||||
|
||||
|
||||
# error handling if user isn't known yet
|
||||
# (obsolete once known users are saved to file, because all users
|
||||
# had to use the /start command and are therefore known to the bot)
|
||||
def get_user_step(uid):
|
||||
if uid in userStep:
|
||||
return userStep[uid]
|
||||
else:
|
||||
knownUsers.append(uid)
|
||||
userStep[uid] = 0
|
||||
print("New user detected, who hasn't used \"/start\" yet")
|
||||
return 0
|
||||
|
||||
|
||||
# only used for console output now
|
||||
def listener(messages):
|
||||
"""
|
||||
When new messages arrive TeleBot will call this function.
|
||||
"""
|
||||
for m in messages:
|
||||
if m.content_type == 'text':
|
||||
# print the sent message to the console
|
||||
print(str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text)
|
||||
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
bot.set_update_listener(listener) # register listener
|
||||
|
||||
|
||||
# handle the "/start" command
|
||||
@bot.message_handler(commands=['start'])
|
||||
def command_start(m):
|
||||
cid = m.chat.id
|
||||
if cid not in knownUsers: # if user hasn't used the "/start" command yet:
|
||||
knownUsers.append(cid) # save user id, so you could brodcast messages to all users of this bot later
|
||||
userStep[cid] = 0 # save user id and his current "command level", so he can use the "/getImage" command
|
||||
bot.send_message(cid, "Hello, stranger, let me scan you...")
|
||||
bot.send_message(cid, "Scanning complete, I know you now")
|
||||
command_help(m) # show the new user the help page
|
||||
else:
|
||||
bot.send_message(cid, "I already know you, no need for me to scan you again!")
|
||||
|
||||
|
||||
# help page
|
||||
@bot.message_handler(commands=['help'])
|
||||
def command_help(m):
|
||||
cid = m.chat.id
|
||||
help_text = "The following commands are available: \n"
|
||||
for key in commands: # generate help text out of the commands dictionary defined at the top
|
||||
help_text += "/" + key + ": "
|
||||
help_text += commands[key] + "\n"
|
||||
bot.send_message(cid, help_text) # send the generated help page
|
||||
|
||||
|
||||
# chat_action example (not a good one...)
|
||||
@bot.message_handler(commands=['sendLongText'])
|
||||
def command_long_text(m):
|
||||
cid = m.chat.id
|
||||
bot.send_message(cid, "If you think so...")
|
||||
bot.send_chat_action(cid, 'typing') # show the bot "typing" (max. 5 secs)
|
||||
time.sleep(3)
|
||||
bot.send_message(cid, ".")
|
||||
|
||||
|
||||
# user can chose an image (multi-stage command example)
|
||||
@bot.message_handler(commands=['getImage'])
|
||||
def command_image(m):
|
||||
cid = m.chat.id
|
||||
bot.send_message(cid, "Please choose your image now", reply_markup=imageSelect) # show the keyboard
|
||||
userStep[cid] = 1 # set the user to the next step (expecting a reply in the listener now)
|
||||
|
||||
|
||||
# if the user has issued the "/getImage" command, process the answer
|
||||
@bot.message_handler(func=lambda message: get_user_step(message.chat.id) == 1)
|
||||
def msg_image_select(m):
|
||||
cid = m.chat.id
|
||||
text = m.text
|
||||
|
||||
# for some reason the 'upload_photo' status isn't quite working (doesn't show at all)
|
||||
bot.send_chat_action(cid, 'typing')
|
||||
|
||||
if text == 'Mickey': # send the appropriate image based on the reply to the "/getImage" command
|
||||
bot.send_photo(cid, open('rooster.jpg', 'rb'),
|
||||
reply_markup=hideBoard) # send file and hide keyboard, after image is sent
|
||||
userStep[cid] = 0 # reset the users step back to 0
|
||||
elif text == 'Minnie':
|
||||
bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard)
|
||||
userStep[cid] = 0
|
||||
else:
|
||||
bot.send_message(cid, "Please, use the predefined keyboard!")
|
||||
bot.send_message(cid, "Please try again")
|
||||
|
||||
|
||||
# filter on a specific message
|
||||
@bot.message_handler(func=lambda message: message.text == "hi")
|
||||
def command_text_hi(m):
|
||||
bot.send_message(m.chat.id, "I love you too!")
|
||||
|
||||
|
||||
# default handler for every other text
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def command_default(m):
|
||||
# this is the standard reply to a normal message
|
||||
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
|
||||
|
||||
|
||||
bot.polling()
|
BIN
examples/detailed_example/kitten.jpg
Normal file
BIN
examples/detailed_example/kitten.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
examples/detailed_example/rooster.jpg
Normal file
BIN
examples/detailed_example/rooster.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
15
examples/download_file_example.py
Normal file
15
examples/download_file_example.py
Normal file
@ -0,0 +1,15 @@
|
||||
import telebot
|
||||
|
||||
TOKEN = 'YOUR BOT TOKEN'
|
||||
CHAT_ID = 'YOUR CHAT ID'
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
|
||||
ret_msg = bot.send_voice(CHAT_ID, open('tests/test_data/record.ogg', 'rb'))
|
||||
|
||||
file_info = bot.get_file(ret_msg.voice.file_id)
|
||||
|
||||
downloaded_file = bot.download_file(file_info.file_path)
|
||||
|
||||
with open('new_file.ogg', 'wb') as new_file:
|
||||
new_file.write(downloaded_file)
|
@ -1,3 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This is a simple echo bot using the decorator mechanism.
|
||||
# It echoes any incoming text messages.
|
||||
|
||||
@ -9,7 +11,7 @@ bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help, start'])
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message, """\
|
||||
Hi there, I am EchoBot.
|
||||
@ -22,7 +24,5 @@ I am here to echo your kind words back to you. Just say anything nice and I'll s
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
|
||||
while True:
|
||||
pass
|
||||
bot.polling()
|
||||
|
74
examples/inline_example.py
Normal file
74
examples/inline_example.py
Normal file
@ -0,0 +1,74 @@
|
||||
# This example show how to write an inline mode telegram bot use pyTelegramBotAPI.
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
API_TOKEN = '<TOKEN>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
telebot.logger.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'text')
|
||||
def query_text(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'Result1', types.InputTextMessageContent('hi'))
|
||||
r2 = types.InlineQueryResultArticle('2', 'Result2', types.InputTextMessageContent('hi'))
|
||||
bot.answer_inline_query(inline_query.id, [r, r2])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'photo1')
|
||||
def query_photo(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultPhoto('1',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/kitten.jpg',
|
||||
input_message_content=types.InputTextMessageContent('hi'))
|
||||
r2 = types.InlineQueryResultPhoto('2',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg')
|
||||
bot.answer_inline_query(inline_query.id, [r, r2], cache_time=1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: query.query == 'video')
|
||||
def query_video(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultVideo('1',
|
||||
'https://github.com/eternnoir/pyTelegramBotAPI/blob/master/tests/test_data/test_video.mp4?raw=true',
|
||||
'video/mp4', 'Video',
|
||||
'https://raw.githubusercontent.com/eternnoir/pyTelegramBotAPI/master/examples/detailed_example/rooster.jpg',
|
||||
'Title'
|
||||
)
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
@bot.inline_handler(lambda query: len(query.query) == 0)
|
||||
def default_query(inline_query):
|
||||
try:
|
||||
r = types.InlineQueryResultArticle('1', 'default', types.InputTextMessageContent('default'))
|
||||
bot.answer_inline_query(inline_query.id, [r])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main_loop()
|
||||
except KeyboardInterrupt:
|
||||
print('\nExiting by user request.\n')
|
||||
sys.exit(0)
|
27
examples/inline_keyboard_example.py
Normal file
27
examples/inline_keyboard_example.py
Normal file
@ -0,0 +1,27 @@
|
||||
# This example show how to use inline keyboards and process button presses
|
||||
import telebot
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
TELEGRAM_TOKEN = '<TOKEN>'
|
||||
|
||||
bot = telebot.TeleBot(TELEGRAM_TOKEN)
|
||||
|
||||
def gen_markup():
|
||||
markup = InlineKeyboardMarkup()
|
||||
markup.row_width = 2
|
||||
markup.add(InlineKeyboardButton("Yes", callback_data="cb_yes"),
|
||||
InlineKeyboardButton("No", callback_data="cb_no"))
|
||||
return markup
|
||||
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def callback_query(call):
|
||||
if call.data == "cb_yes":
|
||||
bot.answer_callback_query(call.id, "Answer is Yes")
|
||||
elif call.data == "cb_no":
|
||||
bot.answer_callback_query(call.id, "Answer is No")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
def message_handler(message):
|
||||
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
|
||||
|
||||
bot.polling(none_stop=True)
|
53
examples/middleware/i18n.py
Normal file
53
examples/middleware/i18n.py
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This example shows how to implement i18n (internationalization) l10n (localization) to create
|
||||
# multi-language bots with middleware handler.
|
||||
#
|
||||
# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use
|
||||
# better i18n systems (gettext and etc) for handling multilingual translations.
|
||||
# This is not a working, production-ready sample and it is highly recommended not to use it in production.
|
||||
#
|
||||
# In this example let's imagine we want to introduce localization or internationalization into our project and
|
||||
# we need some global function to activate the language once and to use that language in all other message
|
||||
# handler functions for not repeatedly activating it.
|
||||
# The middleware (i18n and l10n) is explained:
|
||||
|
||||
import telebot
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
|
||||
TRANSLATIONS = {
|
||||
'hello': {
|
||||
'en': 'hello',
|
||||
'ru': 'привет',
|
||||
'uz': 'salom'
|
||||
}
|
||||
}
|
||||
|
||||
_lang = 'en'
|
||||
|
||||
|
||||
def activate(lang):
|
||||
global _lang
|
||||
_lang = lang
|
||||
|
||||
|
||||
def _(string):
|
||||
return TRANSLATIONS[string][_lang]
|
||||
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
@bot.middleware_handler(update_types=['message'])
|
||||
def activate_language(bot_instance, message):
|
||||
activate(message.from_user.language_code)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
bot.send_message(message.chat.id, _('hello'))
|
||||
|
||||
|
||||
bot.polling()
|
61
examples/middleware/session.py
Normal file
61
examples/middleware/session.py
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This example shows how to implement session creation and retrieval based on user id with middleware handler.
|
||||
#
|
||||
# Note: For the sake of simplicity of this example no extra library is used. However, it is recommended to use
|
||||
# in-memory or on-disk storage implementations (redis, mysql, postgres and etc) for storing and retrieving structures.
|
||||
# This is not a working, production-ready sample and it is highly recommended not to use it in production.
|
||||
#
|
||||
# In this example let's imagine we want to create a session for each user who communicates with the bot to store
|
||||
# different kind of temporary data while session is active. As an example we want to track the state of the user
|
||||
# with the help of this session. So, we need a way to store this session data somewhere globally to enable other
|
||||
# message handler functions to be able to use it.
|
||||
# The middleware session is explained:
|
||||
|
||||
import telebot
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
|
||||
INFO_STATE = 'ON_INFO_MENU'
|
||||
MAIN_STATE = 'ON_MAIN_MENU'
|
||||
|
||||
SESSIONS = {
|
||||
-10000: {
|
||||
'state': INFO_STATE
|
||||
},
|
||||
-11111: {
|
||||
'state': MAIN_STATE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_or_create_session(user_id):
|
||||
try:
|
||||
return SESSIONS[user_id]
|
||||
except KeyError:
|
||||
SESSIONS[user_id] = {'state': MAIN_STATE}
|
||||
return SESSIONS[user_id]
|
||||
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
@bot.middleware_handler(update_types=['message'])
|
||||
def set_session(bot_instance, message):
|
||||
bot_instance.session = get_or_create_session(message.from_user.id)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
bot.session['state'] = MAIN_STATE
|
||||
bot.send_message(message.chat.id, bot.session['state'])
|
||||
|
||||
|
||||
@bot.message_handler(commands=['info'])
|
||||
def start(message):
|
||||
bot.session['state'] = INFO_STATE
|
||||
bot.send_message(message.chat.id, bot.session['state'])
|
||||
|
||||
|
||||
bot.polling()
|
82
examples/payments_example.py
Normal file
82
examples/payments_example.py
Normal file
@ -0,0 +1,82 @@
|
||||
import telebot
|
||||
from telebot.types import LabeledPrice, ShippingOption
|
||||
|
||||
token = '1234567890:AAAABBBBCCCCDDDDeeeeFFFFgggGHHHH'
|
||||
provider_token = '1234567890:TEST:AAAABBBBCCCCDDDD' # @BotFather -> Bot Settings -> Payments
|
||||
bot = telebot.TeleBot(token)
|
||||
|
||||
# More about Payments: https://core.telegram.org/bots/payments
|
||||
|
||||
prices = [LabeledPrice(label='Working Time Machine', amount=5750), LabeledPrice('Gift wrapping', 500)]
|
||||
|
||||
shipping_options = [
|
||||
ShippingOption(id='instant', title='WorldWide Teleporter').add_price(LabeledPrice('Teleporter', 1000)),
|
||||
ShippingOption(id='pickup', title='Local pickup').add_price(LabeledPrice('Pickup', 300))]
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def command_start(message):
|
||||
bot.send_message(message.chat.id,
|
||||
"Hello, I'm the demo merchant bot."
|
||||
" I can sell you a Time Machine."
|
||||
" Use /buy to order one, /terms for Terms and Conditions")
|
||||
|
||||
|
||||
@bot.message_handler(commands=['terms'])
|
||||
def command_terms(message):
|
||||
bot.send_message(message.chat.id,
|
||||
'Thank you for shopping with our demo bot. We hope you like your new time machine!\n'
|
||||
'1. If your time machine was not delivered on time, please rethink your concept of time and try again.\n'
|
||||
'2. If you find that your time machine is not working, kindly contact our future service workshops on Trappist-1e.'
|
||||
' They will be accessible anywhere between May 2075 and November 4000 C.E.\n'
|
||||
'3. If you would like a refund, kindly apply for one yesterday and we will have sent it to you immediately.')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['buy'])
|
||||
def command_pay(message):
|
||||
bot.send_message(message.chat.id,
|
||||
"Real cards won't work with me, no money will be debited from your account."
|
||||
" Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`"
|
||||
"\n\nThis is your demo invoice:", parse_mode='Markdown')
|
||||
bot.send_invoice(message.chat.id, title='Working Time Machine',
|
||||
description='Want to visit your great-great-great-grandparents?'
|
||||
' Make a fortune at the races?'
|
||||
' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?'
|
||||
' Order our Working Time Machine today!',
|
||||
provider_token=provider_token,
|
||||
currency='usd',
|
||||
photo_url='http://erkelzaar.tsudao.com/models/perrotta/TIME_MACHINE.jpg',
|
||||
photo_height=512, # !=0/None or picture won't be shown
|
||||
photo_width=512,
|
||||
photo_size=512,
|
||||
is_flexible=False, # True If you need to set up Shipping Fee
|
||||
prices=prices,
|
||||
start_parameter='time-machine-example',
|
||||
invoice_payload='HAPPY FRIDAYS COUPON')
|
||||
|
||||
|
||||
@bot.shipping_query_handler(func=lambda query: True)
|
||||
def shipping(shipping_query):
|
||||
print(shipping_query)
|
||||
bot.answer_shipping_query(shipping_query.id, ok=True, shipping_options=shipping_options,
|
||||
error_message='Oh, seems like our Dog couriers are having a lunch right now. Try again later!')
|
||||
|
||||
|
||||
@bot.pre_checkout_query_handler(func=lambda query: True)
|
||||
def checkout(pre_checkout_query):
|
||||
bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True,
|
||||
error_message="Aliens tried to steal your card's CVV, but we successfully protected your credentials,"
|
||||
" try to pay again in a few minutes, we need a small rest.")
|
||||
|
||||
|
||||
@bot.message_handler(content_types=['successful_payment'])
|
||||
def got_payment(message):
|
||||
bot.send_message(message.chat.id,
|
||||
'Hoooooray! Thanks for payment! We will proceed your order for `{} {}` as fast as possible! '
|
||||
'Stay in touch.\n\nUse /buy again to get a Time Machine for your friend!'.format(
|
||||
message.successful_payment.total_amount / 100, message.successful_payment.currency),
|
||||
parse_mode='Markdown')
|
||||
|
||||
|
||||
bot.skip_pending = True
|
||||
bot.polling(none_stop=True, interval=0)
|
69
examples/serverless/azure_functions
Normal file
69
examples/serverless/azure_functions
Normal file
@ -0,0 +1,69 @@
|
||||
# Using Azure Functions for serverless bots.
|
||||
# (Thanks to twitter.com/masyan for the idea)
|
||||
|
||||
# By default the Azure Functions url is https://.../api/HttpTrigger for HttpTrigger type.
|
||||
# In this example we will use clear webhook url without /api/ -> https://.../HttpTrigger.
|
||||
# Also we set "authLevel": "anonymous".
|
||||
|
||||
# For HttpTrigger type set "route" and "authLevel" in functions.json
|
||||
# {
|
||||
# "bindings": [
|
||||
# ...
|
||||
# "authLevel": "anonymous"
|
||||
# "route": "HttpTrigger"
|
||||
# ]
|
||||
# }
|
||||
|
||||
# To avoid using /api/ in url set "routePrefix":"" in host.json
|
||||
# {
|
||||
# ...
|
||||
# "extensions": {
|
||||
# "http": {
|
||||
# "routePrefix": ""
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
import logging
|
||||
|
||||
import azure.functions as func
|
||||
import telebot
|
||||
from telebot import apihelper, types
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Set bot token
|
||||
TOKEN = ''
|
||||
|
||||
# Uncomment this for using proxy for request
|
||||
# PROXY = ''
|
||||
# apihelper.proxy = {'https': PROXY}
|
||||
|
||||
# Set WEBHOOK as your Azure Functions url (https://...azurewebsites.net/HttpTrigger)
|
||||
WEBHOOK = ''
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
|
||||
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
# To avoid "error 429 too many request" set webhook only once. Or use time.sleep(1).
|
||||
def main(req: func.HttpRequest) -> func.HttpResponse:
|
||||
bot.set_webhook(url=WEBHOOK)
|
||||
request_body_dict = req.get_json()
|
||||
update = telebot.types.Update.de_json(request_body_dict)
|
||||
bot.process_new_messages([update.message])
|
||||
return func.HttpResponse(body='', status_code=200)
|
||||
|
||||
# Sometimes "requests" version is important.
|
||||
# azure-functions==1.0.4
|
||||
# PySocks==1.7.1
|
||||
# pyTelegramBotAPI==3.6.6
|
||||
# requests==2.10.0
|
||||
|
86
examples/step_example.py
Normal file
86
examples/step_example.py
Normal file
@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you how to use register_next_step handler.
|
||||
"""
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
user_dict = {}
|
||||
|
||||
|
||||
class User:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.age = None
|
||||
self.sex = None
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
msg = bot.reply_to(message, """\
|
||||
Hi there, I am Example bot.
|
||||
What's your name?
|
||||
""")
|
||||
bot.register_next_step_handler(msg, process_name_step)
|
||||
|
||||
|
||||
def process_name_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
name = message.text
|
||||
user = User(name)
|
||||
user_dict[chat_id] = user
|
||||
msg = bot.reply_to(message, 'How old are you?')
|
||||
bot.register_next_step_handler(msg, process_age_step)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
def process_age_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
age = message.text
|
||||
if not age.isdigit():
|
||||
msg = bot.reply_to(message, 'Age should be a number. How old are you?')
|
||||
bot.register_next_step_handler(msg, process_age_step)
|
||||
return
|
||||
user = user_dict[chat_id]
|
||||
user.age = age
|
||||
markup = types.ReplyKeyboardMarkup(one_time_keyboard=True)
|
||||
markup.add('Male', 'Female')
|
||||
msg = bot.reply_to(message, 'What is your gender', reply_markup=markup)
|
||||
bot.register_next_step_handler(msg, process_sex_step)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
def process_sex_step(message):
|
||||
try:
|
||||
chat_id = message.chat.id
|
||||
sex = message.text
|
||||
user = user_dict[chat_id]
|
||||
if (sex == u'Male') or (sex == u'Female'):
|
||||
user.sex = sex
|
||||
else:
|
||||
raise Exception()
|
||||
bot.send_message(chat_id, 'Nice to meet you ' + user.name + '\n Age:' + str(user.age) + '\n Sex:' + user.sex)
|
||||
except Exception as e:
|
||||
bot.reply_to(message, 'oooops')
|
||||
|
||||
|
||||
# Enable saving next step handlers to file "./.handlers-saves/step.save".
|
||||
# Delay=2 means that after any change in next step handlers (e.g. calling register_next_step_handler())
|
||||
# saving will hapen after delay 2 seconds.
|
||||
bot.enable_save_next_step_handlers(delay=2)
|
||||
|
||||
# Load next_step_handlers from save file (default "./.handlers-saves/step.save")
|
||||
# WARNING It will work only if enable_save_next_step_handlers was called!
|
||||
bot.load_next_step_handlers()
|
||||
|
||||
bot.polling()
|
84
examples/telebot_bot/telebot_bot.py
Normal file
84
examples/telebot_bot/telebot_bot.py
Normal file
@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This bot was made specifically for the pyTelegramAPI Telegram chat,
|
||||
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
|
||||
# WARNING: Tested with Python 2.7
|
||||
|
||||
import os
|
||||
|
||||
import telebot
|
||||
|
||||
text_messages = {
|
||||
'welcome':
|
||||
u'Please welcome {name}!\n\n'
|
||||
u'This chat is intended for questions about and discussion of the pyTelegramBotAPI.\n'
|
||||
u'To enable group members to answer your questions fast and accurately, please make sure to study the '
|
||||
u'project\'s documentation (https://github.com/eternnoir/pyTelegramBotAPI/blob/master/README.md) and the '
|
||||
u'examples (https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples) first.\n\n'
|
||||
u'I hope you enjoy your stay here!',
|
||||
|
||||
'info':
|
||||
u'My name is TeleBot,\n'
|
||||
u'I am a bot that assists these wonderful bot-creating people of this bot library group chat.\n'
|
||||
u'Also, I am still under development. Please improve my functionality by making a pull request! '
|
||||
u'Suggestions are also welcome, just drop them in this group chat!',
|
||||
|
||||
'wrong_chat':
|
||||
u'Hi there!\nThanks for trying me out. However, this bot can only be used in the pyTelegramAPI group chat.\n'
|
||||
u'Join us!\n\n'
|
||||
u'https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b'
|
||||
}
|
||||
|
||||
if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
|
||||
raise AssertionError("Please configure TELEBOT_BOT_TOKEN and GROUP_CHAT_ID as environment variables")
|
||||
|
||||
bot = telebot.AsyncTeleBot(os.environ["TELEBOT_BOT_TOKEN"])
|
||||
GROUP_CHAT_ID = int(os.environ["GROUP_CHAT_ID"])
|
||||
|
||||
|
||||
def is_api_group(chat_id):
|
||||
return chat_id == GROUP_CHAT_ID
|
||||
|
||||
|
||||
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
|
||||
def on_user_joins(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
return
|
||||
|
||||
name = message.new_chat_participant.first_name
|
||||
if hasattr(message.new_chat_participant, 'last_name') and message.new_chat_participant.last_name is not None:
|
||||
name += u" {}".format(message.new_chat_participant.last_name)
|
||||
|
||||
if hasattr(message.new_chat_participant, 'username') and message.new_chat_participant.username is not None:
|
||||
name += u" (@{})".format(message.new_chat_participant.username)
|
||||
|
||||
bot.reply_to(message, text_messages['welcome'].format(name=name))
|
||||
|
||||
|
||||
@bot.message_handler(commands=['info', 'help'])
|
||||
def on_info(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
bot.reply_to(message, text_messages['wrong_chat'])
|
||||
return
|
||||
|
||||
bot.reply_to(message, text_messages['info'])
|
||||
|
||||
|
||||
@bot.message_handler(commands=["ping"])
|
||||
def on_ping(message):
|
||||
bot.reply_to(message, "Still alive and kicking!")
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def on_start(message):
|
||||
if not is_api_group(message.chat.id):
|
||||
bot.reply_to(message, text_messages['wrong_chat'])
|
||||
return
|
||||
|
||||
|
||||
def listener(messages):
|
||||
for m in messages:
|
||||
print(str(m))
|
||||
|
||||
|
||||
bot.set_update_listener(listener)
|
||||
bot.polling()
|
45
examples/webhook_examples/README.md
Normal file
45
examples/webhook_examples/README.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Webhook examples using pyTelegramBotAPI
|
||||
|
||||
There are 4 examples in this directory using different libraries:
|
||||
|
||||
* **Python (CPython):** *webhook_cpython_echo_bot.py*
|
||||
* **Pros:**
|
||||
* Official python libraries, it works out of the box (doesn't require to
|
||||
install anything).
|
||||
* Works with Python 2 and Python 3 (need to be converted with 2to3).
|
||||
* **Cons:**
|
||||
* Ugly code.
|
||||
* Many things to handle yourself, this can lead to errors.
|
||||
* Not powerful, do the trick but the performance is low.
|
||||
|
||||
* **CherryPy (3.8.0):** *webhook_cherrypy_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses objects for defining
|
||||
the web application.
|
||||
* Very good performance.
|
||||
* The project seems to be active, latest version is recent.
|
||||
* Works with Python 2 and Python 3.
|
||||
* **Cons:**
|
||||
* Some things are not very intuitive, reading the doc is a must.
|
||||
|
||||
* **Flask (0.10.1):** *webhook_flask_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework, cleaner code, uses decorator which can
|
||||
be nice.
|
||||
* Good performance.
|
||||
* It's intuitive if you know how web application works.
|
||||
* **Cons:**
|
||||
* The project seems not to be very active, latest version dates 2013.
|
||||
* They don't recommend to use it with Python 3, but may work.
|
||||
* May be a oversized for just handling webhook petitions.
|
||||
|
||||
* **aiohttp (1.2.0):** *webhook_aiohttp_echo_bot.py*
|
||||
* **Pros:**
|
||||
* It's a web application framework
|
||||
* Python 3 compatible
|
||||
* Asynchronous, excellent perfomance
|
||||
* Utilizes new async/await syntax
|
||||
* **Cons:**
|
||||
* Requires Python 3.4.2+, don't work with Python 2
|
||||
|
||||
*Latest update of this document: 2017-01-30*
|
87
examples/webhook_examples/webhook_aiohttp_echo_bot.py
Normal file
87
examples/webhook_examples/webhook_aiohttp_echo_bot.py
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with aiohttp
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import logging
|
||||
import ssl
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
import telebot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://{}:{}".format(WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/{}/".format(API_TOKEN)
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
app = web.Application()
|
||||
|
||||
|
||||
# Process webhook calls
|
||||
async def handle(request):
|
||||
if request.match_info.get('token') == bot.token:
|
||||
request_body_dict = await request.json()
|
||||
update = telebot.types.Update.de_json(request_body_dict)
|
||||
bot.process_new_updates([update])
|
||||
return web.Response()
|
||||
else:
|
||||
return web.Response(status=403)
|
||||
|
||||
|
||||
app.router.add_post('/{token}/', handle)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Build ssl context
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
||||
context.load_cert_chain(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV)
|
||||
|
||||
# Start aiohttp server
|
||||
web.run_app(
|
||||
app,
|
||||
host=WEBHOOK_LISTEN,
|
||||
port=WEBHOOK_PORT,
|
||||
ssl_context=context,
|
||||
)
|
90
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
90
examples/webhook_examples/webhook_cherrypy_echo_bot.py
Normal file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with CherryPy
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import logging
|
||||
|
||||
import cherrypy
|
||||
|
||||
import telebot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookServer, process webhook calls
|
||||
class WebhookServer(object):
|
||||
@cherrypy.expose
|
||||
def index(self):
|
||||
if 'content-length' in cherrypy.request.headers and \
|
||||
'content-type' in cherrypy.request.headers and \
|
||||
cherrypy.request.headers['content-type'] == 'application/json':
|
||||
length = int(cherrypy.request.headers['content-length'])
|
||||
json_string = cherrypy.request.body.read(length).decode("utf-8")
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_updates([update])
|
||||
return ''
|
||||
else:
|
||||
raise cherrypy.HTTPError(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Disable CherryPy requests log
|
||||
access_log = cherrypy.log.access_log
|
||||
for handler in tuple(access_log.handlers):
|
||||
access_log.removeHandler(handler)
|
||||
|
||||
# Start cherrypy server
|
||||
cherrypy.config.update({
|
||||
'server.socket_host' : WEBHOOK_LISTEN,
|
||||
'server.socket_port' : WEBHOOK_PORT,
|
||||
'server.ssl_module' : 'builtin',
|
||||
'server.ssl_certificate': WEBHOOK_SSL_CERT,
|
||||
'server.ssl_private_key': WEBHOOK_SSL_PRIV
|
||||
})
|
||||
|
||||
cherrypy.quickstart(WebhookServer(), WEBHOOK_URL_PATH, {'/': {}})
|
106
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
106
examples/webhook_examples/webhook_cpython_echo_bot.py
Normal file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with BaseHTTPServer
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
try:
|
||||
# Python 2
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
import logging
|
||||
import ssl
|
||||
|
||||
import telebot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# WebhookHandler, process webhook calls
|
||||
class WebhookHandler(BaseHTTPRequestHandler):
|
||||
server_version = "WebhookHandler/1.0"
|
||||
|
||||
def do_HEAD(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
if self.path == WEBHOOK_URL_PATH and \
|
||||
'content-type' in self.headers and \
|
||||
'content-length' in self.headers and \
|
||||
self.headers['content-type'] == 'application/json':
|
||||
json_string = self.rfile.read(int(self.headers['content-length']))
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_messages([update.message])
|
||||
else:
|
||||
self.send_error(403)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
#bot.remove_webhook()
|
||||
|
||||
# Set webhook
|
||||
# Beacuse telegram bot api server will check webhook server is alive.
|
||||
# Here we need set webhook after server started manually.
|
||||
#bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
|
||||
# certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start server
|
||||
httpd = HTTPServer((WEBHOOK_LISTEN, WEBHOOK_PORT),
|
||||
WebhookHandler)
|
||||
|
||||
httpd.socket = ssl.wrap_socket(httpd.socket,
|
||||
certfile=WEBHOOK_SSL_CERT,
|
||||
keyfile=WEBHOOK_SSL_PRIV,
|
||||
server_side=True)
|
||||
|
||||
httpd.serve_forever()
|
87
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
87
examples/webhook_examples/webhook_flask_echo_bot.py
Normal file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This is a simple echo bot using decorators and webhook with flask
|
||||
# It echoes any incoming text messages and does not use the polling method.
|
||||
|
||||
import logging
|
||||
import time
|
||||
|
||||
import flask
|
||||
|
||||
import telebot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
|
||||
WEBHOOK_HOST = '<ip/host where the bot is running>'
|
||||
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
|
||||
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
|
||||
|
||||
WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate
|
||||
WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out webhook_pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key webhook_pkey.pem -out webhook_cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
|
||||
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.INFO)
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
|
||||
# Empty webserver index, return nothing, just http 200
|
||||
@app.route('/', methods=['GET', 'HEAD'])
|
||||
def index():
|
||||
return ''
|
||||
|
||||
|
||||
# Process webhook calls
|
||||
@app.route(WEBHOOK_URL_PATH, methods=['POST'])
|
||||
def webhook():
|
||||
if flask.request.headers.get('content-type') == 'application/json':
|
||||
json_string = flask.request.get_data().decode('utf-8')
|
||||
update = telebot.types.Update.de_json(json_string)
|
||||
bot.process_new_updates([update])
|
||||
return ''
|
||||
else:
|
||||
flask.abort(403)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
# Handle all other messages
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
# Remove webhook, it fails sometimes the set if there is a previous webhook
|
||||
bot.remove_webhook()
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Set webhook
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
|
||||
certificate=open(WEBHOOK_SSL_CERT, 'r'))
|
||||
|
||||
# Start flask server
|
||||
app.run(host=WEBHOOK_LISTEN,
|
||||
port=WEBHOOK_PORT,
|
||||
ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
|
||||
debug=True)
|
36
examples/webhook_examples/webhook_flask_heroku_echo.py
Normal file
36
examples/webhook_examples/webhook_flask_heroku_echo.py
Normal file
@ -0,0 +1,36 @@
|
||||
import os
|
||||
|
||||
from flask import Flask, request
|
||||
|
||||
import telebot
|
||||
|
||||
TOKEN = '<api_token>'
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
server = Flask(__name__)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
bot.reply_to(message, 'Hello, ' + message.from_user.first_name)
|
||||
|
||||
|
||||
@bot.message_handler(func=lambda message: True, content_types=['text'])
|
||||
def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
@server.route('/' + TOKEN, methods=['POST'])
|
||||
def getMessage():
|
||||
bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
|
||||
return "!", 200
|
||||
|
||||
|
||||
@server.route("/")
|
||||
def webhook():
|
||||
bot.remove_webhook()
|
||||
bot.set_webhook(url='https://your_heroku_project.com/' + TOKEN)
|
||||
return "!", 200
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
server.run(host="0.0.0.0", port=int(os.environ.get('PORT', 5000)))
|
105
examples/webhook_examples/webhook_tornado_echo_bot.py
Normal file
105
examples/webhook_examples/webhook_tornado_echo_bot.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This example shows webhook echo bot with Tornado web framework
|
||||
# Documenation to Tornado: http://tornadoweb.org
|
||||
|
||||
import signal
|
||||
|
||||
import tornado.httpserver
|
||||
import tornado.ioloop
|
||||
import tornado.options
|
||||
import tornado.web
|
||||
|
||||
import telebot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
WEBHOOK_CERT = "./cert.pem"
|
||||
WEBHOOK_PKEY = "./pkey.pem"
|
||||
WEBHOOK_HOST = "<domain_or_ip>"
|
||||
WEBHOOK_SECRET = "<secret_uri_for_updates"
|
||||
WEBHOOK_PORT = 88
|
||||
WEBHOOK_URL_BASE = "https://{0}:{1}/{2}".format(WEBHOOK_HOST, str(WEBHOOK_PORT), WEBHOOK_SECRET)
|
||||
|
||||
# Quick'n'dirty SSL certificate generation:
|
||||
#
|
||||
# openssl genrsa -out pkey.pem 2048
|
||||
# openssl req -new -x509 -days 3650 -key pkey.pem -out cert.pem
|
||||
#
|
||||
# When asked for "Common Name (e.g. server FQDN or YOUR name)" you should reply
|
||||
# with the same value in you put in WEBHOOK_HOST
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
class Root(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.write("Hi! This is webhook example!")
|
||||
self.finish()
|
||||
|
||||
|
||||
class webhook_serv(tornado.web.RequestHandler):
|
||||
def get(self):
|
||||
self.write("What are you doing here?")
|
||||
self.finish()
|
||||
|
||||
def post(self):
|
||||
if "Content-Length" in self.request.headers and \
|
||||
"Content-Type" in self.request.headers and \
|
||||
self.request.headers['Content-Type'] == "application/json":
|
||||
|
||||
# length = int(self.request.headers['Content-Length'])
|
||||
json_data = self.request.body.decode("utf-8")
|
||||
update = telebot.types.Update.de_json(json_data)
|
||||
bot.process_new_updates([update])
|
||||
self.write("")
|
||||
self.finish()
|
||||
else:
|
||||
self.write("What are you doing here?")
|
||||
self.finish()
|
||||
|
||||
|
||||
tornado.options.define("port", default=WEBHOOK_PORT, help="run on the given port", type=int)
|
||||
is_closing = False
|
||||
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
global is_closing
|
||||
print("Exiting...")
|
||||
is_closing = True
|
||||
|
||||
|
||||
def try_exit():
|
||||
global is_closing
|
||||
if is_closing:
|
||||
# clean up here
|
||||
tornado.ioloop.IOLoop.instance().stop()
|
||||
print("Exit success!")
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
bot.reply_to(message,
|
||||
("Hi there, I am EchoBot.\n"
|
||||
"I am here to echo your kind words back to you."))
|
||||
|
||||
|
||||
bot.remove_webhook()
|
||||
bot.set_webhook(url=WEBHOOK_URL_BASE,
|
||||
certificate=open(WEBHOOK_CERT, 'r'))
|
||||
tornado.options.options.logging = None
|
||||
tornado.options.parse_command_line()
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
application = tornado.web.Application([
|
||||
(r"/", Root),
|
||||
(r"/" + WEBHOOK_SECRET, webhook_serv)
|
||||
])
|
||||
|
||||
http_server = tornado.httpserver.HTTPServer(application, ssl_options={
|
||||
"certfile": WEBHOOK_CERT,
|
||||
"keyfile" : WEBHOOK_PKEY,
|
||||
})
|
||||
http_server.listen(tornado.options.options.port)
|
||||
tornado.ioloop.PeriodicCallback(try_exit, 100).start()
|
||||
tornado.ioloop.IOLoop.instance().start()
|
@ -1,4 +1,5 @@
|
||||
py==1.4.29
|
||||
pytest==2.7.2
|
||||
requests==2.7.0
|
||||
pytest==3.0.2
|
||||
requests==2.20.0
|
||||
six==1.9.0
|
||||
wheel==0.24.0
|
||||
|
22
setup.py
22
setup.py
@ -1,21 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
from setuptools import setup
|
||||
from io import open
|
||||
|
||||
def read(filename):
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
return file.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='0.2.0',
|
||||
version='3.7.2',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=read('README.md'),
|
||||
long_description_content_type="text/markdown",
|
||||
author='eternnoir',
|
||||
author_email='eternnoir@gmail.com',
|
||||
url='https://github.com/eternnoir/pyTelegramBotAPI',
|
||||
packages=['telebot'],
|
||||
license='GPL2',
|
||||
keywords='tools',
|
||||
install_requires=['pytest', 'requests'],
|
||||
keywords='telegram bot api tools',
|
||||
install_requires=['requests', 'six'],
|
||||
extras_require={
|
||||
'json': 'ujson',
|
||||
'redis': 'redis>=3.4.1'
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Environment :: Console',
|
||||
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
|
||||
|
2221
telebot/__init__.py
2221
telebot/__init__.py
File diff suppressed because it is too large
Load Diff
1116
telebot/apihelper.py
1116
telebot/apihelper.py
File diff suppressed because it is too large
Load Diff
145
telebot/handler_backends.py
Normal file
145
telebot/handler_backends.py
Normal file
@ -0,0 +1,145 @@
|
||||
import os
|
||||
import pickle
|
||||
import threading
|
||||
|
||||
from telebot import apihelper
|
||||
|
||||
|
||||
class HandlerBackend(object):
|
||||
"""
|
||||
Class for saving (next step|reply) handlers
|
||||
"""
|
||||
def __init__(self, handlers=None):
|
||||
if handlers is None:
|
||||
handlers = {}
|
||||
self.handlers = handlers
|
||||
|
||||
def register_handler(self, handler_group_id, handler):
|
||||
raise NotImplementedError()
|
||||
|
||||
def clear_handlers(self, handler_group_id):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_handlers(self, handler_group_id):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class MemoryHandlerBackend(HandlerBackend):
|
||||
def register_handler(self, handler_group_id, handler):
|
||||
if handler_group_id in self.handlers:
|
||||
self.handlers[handler_group_id].append(handler)
|
||||
else:
|
||||
self.handlers[handler_group_id] = [handler]
|
||||
|
||||
def clear_handlers(self, handler_group_id):
|
||||
self.handlers.pop(handler_group_id, [])
|
||||
|
||||
def get_handlers(self, handler_group_id):
|
||||
return self.handlers.pop(handler_group_id, [])
|
||||
|
||||
|
||||
class FileHandlerBackend(HandlerBackend):
|
||||
def __init__(self, handlers=None, filename='./.handler-saves/handlers.save', delay=120):
|
||||
super(FileHandlerBackend, self).__init__(handlers)
|
||||
self.filename = filename
|
||||
self.delay = delay
|
||||
self.timer = threading.Timer(delay, self.save_handlers)
|
||||
|
||||
def register_handler(self, handler_group_id, handler):
|
||||
if handler_group_id in self.handlers:
|
||||
self.handlers[handler_group_id].append(handler)
|
||||
else:
|
||||
self.handlers[handler_group_id] = [handler]
|
||||
|
||||
self.start_save_timer()
|
||||
|
||||
def clear_handlers(self, handler_group_id):
|
||||
self.handlers.pop(handler_group_id, [])
|
||||
|
||||
self.start_save_timer()
|
||||
|
||||
def get_handlers(self, handler_group_id):
|
||||
handlers = self.handlers.pop(handler_group_id, [])
|
||||
|
||||
self.start_save_timer()
|
||||
|
||||
return handlers
|
||||
|
||||
def start_save_timer(self):
|
||||
if not self.timer.is_alive():
|
||||
if self.delay <= 0:
|
||||
self.save_handlers()
|
||||
else:
|
||||
self.timer = threading.Timer(self.delay, self.save_handlers)
|
||||
self.timer.start()
|
||||
|
||||
def save_handlers(self):
|
||||
self.dump_handlers(self.handlers, self.filename)
|
||||
|
||||
def load_handlers(self, filename=None, del_file_after_loading=True):
|
||||
if not filename:
|
||||
filename = self.filename
|
||||
tmp = self.return_load_handlers(filename, del_file_after_loading=del_file_after_loading)
|
||||
if tmp is not None:
|
||||
self.handlers.update(tmp)
|
||||
|
||||
@staticmethod
|
||||
def dump_handlers(handlers, filename, file_mode="wb"):
|
||||
dirs = filename.rsplit('/', maxsplit=1)[0]
|
||||
os.makedirs(dirs, exist_ok=True)
|
||||
|
||||
with open(filename + ".tmp", file_mode) as file:
|
||||
if (apihelper.CUSTOM_SERIALIZER is None):
|
||||
pickle.dump(handlers, file)
|
||||
else:
|
||||
apihelper.CUSTOM_SERIALIZER.dump(handlers, file)
|
||||
|
||||
if os.path.isfile(filename):
|
||||
os.remove(filename)
|
||||
|
||||
os.rename(filename + ".tmp", filename)
|
||||
|
||||
@staticmethod
|
||||
def return_load_handlers(filename, del_file_after_loading=True):
|
||||
if os.path.isfile(filename) and os.path.getsize(filename) > 0:
|
||||
with open(filename, "rb") as file:
|
||||
if (apihelper.CUSTOM_SERIALIZER is None):
|
||||
handlers = pickle.load(file)
|
||||
else:
|
||||
handlers = apihelper.CUSTOM_SERIALIZER.load(file)
|
||||
|
||||
if del_file_after_loading:
|
||||
os.remove(filename)
|
||||
|
||||
return handlers
|
||||
|
||||
|
||||
class RedisHandlerBackend(HandlerBackend):
|
||||
def __init__(self, handlers=None, host='localhost', port=6379, db=0, prefix='telebot'):
|
||||
super(RedisHandlerBackend, self).__init__(handlers)
|
||||
from redis import Redis
|
||||
self.prefix = prefix
|
||||
self.redis = Redis(host, port, db)
|
||||
|
||||
def _key(self, handle_group_id):
|
||||
return ':'.join((self.prefix, str(handle_group_id)))
|
||||
|
||||
def register_handler(self, handler_group_id, handler):
|
||||
handlers = []
|
||||
value = self.redis.get(self._key(handler_group_id))
|
||||
if value:
|
||||
handlers = pickle.loads(value)
|
||||
handlers.append(handler)
|
||||
self.redis.set(self._key(handler_group_id), pickle.dumps(handlers))
|
||||
|
||||
def clear_handlers(self, handler_group_id):
|
||||
self.redis.delete(self._key(handler_group_id))
|
||||
|
||||
def get_handlers(self, handler_group_id):
|
||||
handlers = []
|
||||
value = self.redis.get(self._key(handler_group_id))
|
||||
if value:
|
||||
handlers = pickle.loads(value)
|
||||
self.clear_handlers(handler_group_id)
|
||||
|
||||
return handlers
|
2301
telebot/types.py
2301
telebot/types.py
File diff suppressed because it is too large
Load Diff
279
telebot/util.py
Normal file
279
telebot/util.py
Normal file
@ -0,0 +1,279 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import warnings
|
||||
import functools
|
||||
|
||||
import six
|
||||
from six import string_types
|
||||
|
||||
# Python3 queue support.
|
||||
|
||||
try:
|
||||
import Queue
|
||||
except ImportError:
|
||||
import queue as Queue
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('TeleBot')
|
||||
|
||||
thread_local = threading.local()
|
||||
|
||||
|
||||
class WorkerThread(threading.Thread):
|
||||
count = 0
|
||||
|
||||
def __init__(self, exception_callback=None, queue=None, name=None):
|
||||
if not name:
|
||||
name = "WorkerThread{0}".format(self.__class__.count + 1)
|
||||
self.__class__.count += 1
|
||||
if not queue:
|
||||
queue = Queue.Queue()
|
||||
|
||||
threading.Thread.__init__(self, name=name)
|
||||
self.queue = queue
|
||||
self.daemon = True
|
||||
|
||||
self.received_task_event = threading.Event()
|
||||
self.done_event = threading.Event()
|
||||
self.exception_event = threading.Event()
|
||||
self.continue_event = threading.Event()
|
||||
|
||||
self.exception_callback = exception_callback
|
||||
self.exc_info = None
|
||||
self._running = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
while self._running:
|
||||
try:
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.5)
|
||||
self.continue_event.clear()
|
||||
self.received_task_event.clear()
|
||||
self.done_event.clear()
|
||||
self.exception_event.clear()
|
||||
logger.debug("Received task")
|
||||
self.received_task_event.set()
|
||||
|
||||
task(*args, **kwargs)
|
||||
logger.debug("Task complete")
|
||||
self.done_event.set()
|
||||
except Queue.Empty:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(type(e).__name__ + " occurred, args=" + str(e.args) + "\n" + traceback.format_exc())
|
||||
self.exc_info = sys.exc_info()
|
||||
self.exception_event.set()
|
||||
|
||||
if self.exception_callback:
|
||||
self.exception_callback(self, self.exc_info)
|
||||
self.continue_event.wait()
|
||||
|
||||
def put(self, task, *args, **kwargs):
|
||||
self.queue.put((task, args, kwargs))
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
self.continue_event.set()
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
|
||||
class ThreadPool:
|
||||
|
||||
def __init__(self, num_threads=2):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [WorkerThread(self.on_exception, self.tasks) for _ in range(num_threads)]
|
||||
self.num_threads = num_threads
|
||||
|
||||
self.exception_event = threading.Event()
|
||||
self.exc_info = None
|
||||
|
||||
def put(self, func, *args, **kwargs):
|
||||
self.tasks.put((func, args, kwargs))
|
||||
|
||||
def on_exception(self, worker_thread, exc_info):
|
||||
self.exc_info = exc_info
|
||||
self.exception_event.set()
|
||||
worker_thread.continue_event.set()
|
||||
|
||||
def raise_exceptions(self):
|
||||
if self.exception_event.is_set():
|
||||
six.reraise(self.exc_info[0], self.exc_info[1], self.exc_info[2])
|
||||
|
||||
def clear_exceptions(self):
|
||||
self.exception_event.clear()
|
||||
|
||||
def close(self):
|
||||
for worker in self.workers:
|
||||
worker.stop()
|
||||
for worker in self.workers:
|
||||
worker.join()
|
||||
|
||||
|
||||
class AsyncTask:
|
||||
def __init__(self, target, *args, **kwargs):
|
||||
self.target = target
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
self.done = False
|
||||
self.thread = threading.Thread(target=self._run)
|
||||
self.thread.start()
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except:
|
||||
self.result = sys.exc_info()
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, BaseException):
|
||||
six.reraise(self.result[0], self.result[1], self.result[2])
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
def async_dec():
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
return AsyncTask(fn, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def is_string(var):
|
||||
return isinstance(var, string_types)
|
||||
|
||||
def is_command(text):
|
||||
"""
|
||||
Checks if `text` is a command. Telegram chat commands start with the '/' character.
|
||||
:param text: Text to check.
|
||||
:return: True if `text` is a command, else False.
|
||||
"""
|
||||
return text.startswith('/')
|
||||
|
||||
|
||||
def extract_command(text):
|
||||
"""
|
||||
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
|
||||
If `text` is not a command, this function returns None.
|
||||
|
||||
Examples:
|
||||
extract_command('/help'): 'help'
|
||||
extract_command('/help@BotName'): 'help'
|
||||
extract_command('/search black eyed peas'): 'search'
|
||||
extract_command('Good day to you'): None
|
||||
|
||||
:param text: String to extract the command from
|
||||
:return: the command if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
return text.split()[0].split('@')[0][1:] if is_command(text) else None
|
||||
|
||||
|
||||
def split_string(text, chars_per_string):
|
||||
"""
|
||||
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
|
||||
This is very useful for splitting one giant message into multiples.
|
||||
|
||||
:param text: The text to split
|
||||
:param chars_per_string: The number of characters per line the text is split into.
|
||||
:return: The splitted text as a list of strings.
|
||||
"""
|
||||
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
|
||||
|
||||
# CREDITS TO http://stackoverflow.com/questions/12317940#answer-12320352
|
||||
def or_set(self):
|
||||
self._set()
|
||||
self.changed()
|
||||
|
||||
|
||||
def or_clear(self):
|
||||
self._clear()
|
||||
self.changed()
|
||||
|
||||
|
||||
def orify(e, changed_callback):
|
||||
e._set = e.set
|
||||
e._clear = e.clear
|
||||
e.changed = changed_callback
|
||||
e.set = lambda: or_set(e)
|
||||
e.clear = lambda: or_clear(e)
|
||||
|
||||
def OrEvent(*events):
|
||||
or_event = threading.Event()
|
||||
def changed():
|
||||
bools = [e.is_set() for e in events]
|
||||
if any(bools):
|
||||
or_event.set()
|
||||
else:
|
||||
or_event.clear()
|
||||
|
||||
def busy_wait():
|
||||
while not or_event.is_set():
|
||||
or_event._wait(3)
|
||||
|
||||
for e in events:
|
||||
orify(e, changed)
|
||||
or_event._wait = or_event.wait
|
||||
or_event.wait = busy_wait
|
||||
changed()
|
||||
return or_event
|
||||
|
||||
def extract_arguments(text):
|
||||
"""
|
||||
Returns the argument after the command.
|
||||
|
||||
Examples:
|
||||
extract_arguments("/get name"): 'name'
|
||||
extract_arguments("/get"): ''
|
||||
extract_arguments("/get@botName name"): 'name'
|
||||
|
||||
:param text: String to extract the arguments from a command
|
||||
:return: the arguments if `text` is a command (according to is_command), else None.
|
||||
"""
|
||||
regexp = re.compile(r"/\w*(@\w*)*\s*([\s\S]*)",re.IGNORECASE)
|
||||
result = regexp.match(text)
|
||||
return result.group(2) if is_command(text) else None
|
||||
|
||||
|
||||
def per_thread(key, construct_value, reset=False):
|
||||
if reset or not hasattr(thread_local, key):
|
||||
value = construct_value()
|
||||
setattr(thread_local, key, value)
|
||||
|
||||
return getattr(thread_local, key)
|
||||
|
||||
|
||||
def generate_random_token():
|
||||
return ''.join(random.sample(string.ascii_letters, 16))
|
||||
|
||||
|
||||
def deprecated(func):
|
||||
"""This is a decorator which can be used to mark functions
|
||||
as deprecated. It will result in a warning being emitted
|
||||
when the function is used."""
|
||||
# https://stackoverflow.com/a/30253848/441814
|
||||
@functools.wraps(func)
|
||||
def new_func(*args, **kwargs):
|
||||
warnings.simplefilter('always', DeprecationWarning) # turn off filter
|
||||
warnings.warn("Call to deprecated function {}.".format(func.__name__),
|
||||
category=DeprecationWarning,
|
||||
stacklevel=2)
|
||||
warnings.simplefilter('default', DeprecationWarning) # reset filter
|
||||
return func(*args, **kwargs)
|
||||
return new_func
|
BIN
tests/test_data/record.mp3
Normal file
BIN
tests/test_data/record.mp3
Normal file
Binary file not shown.
BIN
tests/test_data/record.ogg
Normal file
BIN
tests/test_data/record.ogg
Normal file
Binary file not shown.
BIN
tests/test_data/record.wav
Normal file
BIN
tests/test_data/record.wav
Normal file
Binary file not shown.
BIN
tests/test_data/test_video.mp4
Normal file
BIN
tests/test_data/test_video.mp4
Normal file
Binary file not shown.
270
tests/test_handler_backends.py
Normal file
270
tests/test_handler_backends.py
Normal file
@ -0,0 +1,270 @@
|
||||
import sys
|
||||
|
||||
sys.path.append('../')
|
||||
|
||||
REDIS_TESTS = False
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot.handler_backends import MemoryHandlerBackend, FileHandlerBackend
|
||||
|
||||
if REDIS_TESTS:
|
||||
from telebot.handler_backends import RedisHandlerBackend
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def telegram_bot():
|
||||
return telebot.TeleBot('', threaded=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def private_chat():
|
||||
return types.Chat(id=11, type='private')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user():
|
||||
return types.User(id=10, is_bot=False, first_name='Some User')
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def message(user, private_chat):
|
||||
params = {'text': '/start'}
|
||||
return types.Message(
|
||||
message_id=1, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string=""
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reply_to_message(user, private_chat, message):
|
||||
params = {'text': '/start'}
|
||||
reply_message = types.Message(
|
||||
message_id=2, from_user=user, date=None, chat=private_chat, content_type='text', options=params, json_string=""
|
||||
)
|
||||
reply_message.reply_to_message = message
|
||||
return reply_message
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def update_type(message):
|
||||
edited_message = None
|
||||
channel_post = None
|
||||
edited_channel_post = None
|
||||
inline_query = None
|
||||
chosen_inline_result = None
|
||||
callback_query = None
|
||||
shipping_query = None
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
return types.Update(1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reply_to_message_update_type(reply_to_message):
|
||||
edited_message = None
|
||||
channel_post = None
|
||||
edited_channel_post = None
|
||||
inline_query = None
|
||||
chosen_inline_result = None
|
||||
callback_query = None
|
||||
shipping_query = None
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
return types.Update(1001234038284, reply_to_message, edited_message, channel_post, edited_channel_post,
|
||||
inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
|
||||
|
||||
def next_handler(message):
|
||||
message.text = 'entered next_handler'
|
||||
|
||||
|
||||
def test_memory_handler_backend_default_backend(telegram_bot):
|
||||
assert telegram_bot.reply_backend.__class__ == MemoryHandlerBackend
|
||||
assert telegram_bot.next_step_backend.__class__ == MemoryHandlerBackend
|
||||
|
||||
|
||||
def test_memory_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered next_handler'
|
||||
|
||||
assert private_chat.id not in telegram_bot.next_step_backend.handlers
|
||||
|
||||
|
||||
def test_memory_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
|
||||
|
||||
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
|
||||
|
||||
assert private_chat.id not in telegram_bot.next_step_backend.handlers
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
|
||||
def test_memory_handler_backend_register_reply_handler(telegram_bot, private_chat, update_type,
|
||||
reply_to_message_update_type):
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1
|
||||
|
||||
telegram_bot.process_new_updates([reply_to_message_update_type])
|
||||
assert reply_to_message_update_type.message.text == 'entered next_handler'
|
||||
|
||||
assert private_chat.id not in telegram_bot.reply_backend.handlers
|
||||
|
||||
|
||||
def test_memory_handler_backend_clear_reply_handler(telegram_bot, private_chat, update_type,
|
||||
reply_to_message_update_type):
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_for_reply_by_message_id(message.message_id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
assert len(telegram_bot.reply_backend.handlers[update_type.message.message_id]) == 1
|
||||
|
||||
telegram_bot.clear_reply_handlers_by_message_id(update_type.message.message_id)
|
||||
|
||||
assert update_type.message.message_id not in telegram_bot.reply_backend.handlers
|
||||
|
||||
telegram_bot.process_new_updates([reply_to_message_update_type])
|
||||
assert reply_to_message_update_type.message.text == 'entered start'
|
||||
|
||||
|
||||
def test_file_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step1.save', delay=0.1)
|
||||
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
assert os.path.exists(telegram_bot.next_step_backend.filename)
|
||||
|
||||
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
|
||||
|
||||
telegram_bot.next_step_backend.handlers = {}
|
||||
|
||||
telegram_bot.next_step_backend.load_handlers()
|
||||
|
||||
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered next_handler'
|
||||
|
||||
assert private_chat.id not in telegram_bot.next_step_backend.handlers
|
||||
|
||||
time.sleep(0.2)
|
||||
if os.path.exists(telegram_bot.next_step_backend.filename):
|
||||
os.remove(telegram_bot.next_step_backend.filename)
|
||||
|
||||
|
||||
def test_file_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
telegram_bot.next_step_backend=FileHandlerBackend(filename='./.handler-saves/step2.save', delay=0.1)
|
||||
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
assert len(telegram_bot.next_step_backend.handlers[private_chat.id]) == 1
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
assert os.path.exists(telegram_bot.next_step_backend.filename)
|
||||
|
||||
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
telegram_bot.next_step_backend.load_handlers()
|
||||
|
||||
assert private_chat.id not in telegram_bot.next_step_backend.handlers
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
time.sleep(0.2)
|
||||
if os.path.exists(telegram_bot.next_step_backend.filename):
|
||||
os.remove(telegram_bot.next_step_backend.filename)
|
||||
|
||||
|
||||
def test_redis_handler_backend_register_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
if not REDIS_TESTS:
|
||||
pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS')
|
||||
|
||||
telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend1')
|
||||
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered next_handler'
|
||||
|
||||
|
||||
def test_redis_handler_backend_clear_next_step_handler(telegram_bot, private_chat, update_type):
|
||||
if not REDIS_TESTS:
|
||||
pytest.skip('please install redis and configure redis server, then enable REDIS_TESTS')
|
||||
|
||||
telegram_bot.next_step_backend = RedisHandlerBackend(prefix='pyTelegramBotApi:step_backend2')
|
||||
|
||||
@telegram_bot.message_handler(commands=['start'])
|
||||
def start(message):
|
||||
message.text = 'entered start'
|
||||
telegram_bot.register_next_step_handler_by_chat_id(message.chat.id, next_handler)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
||||
|
||||
telegram_bot.clear_step_handler_by_chat_id(private_chat.id)
|
||||
|
||||
telegram_bot.process_new_updates([update_type])
|
||||
assert update_type.message.text == 'entered start'
|
561
tests/test_telebot.py
Normal file
561
tests/test_telebot.py
Normal file
@ -0,0 +1,561 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
sys.path.append('../')
|
||||
|
||||
import time
|
||||
import pytest
|
||||
import os
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot import util
|
||||
|
||||
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
|
||||
|
||||
if not should_skip:
|
||||
TOKEN = os.environ['TOKEN']
|
||||
CHAT_ID = os.environ['CHAT_ID']
|
||||
GROUP_ID = os.environ['GROUP_ID']
|
||||
|
||||
|
||||
@pytest.mark.skipif(should_skip, reason="No environment variables configured")
|
||||
class TestTeleBot:
|
||||
def test_message_listener(self):
|
||||
msg_list = []
|
||||
for x in range(100):
|
||||
msg_list.append(self.create_text_message('Message ' + str(x)))
|
||||
|
||||
def listener(messages):
|
||||
assert len(messages) == 100
|
||||
|
||||
tb = telebot.TeleBot('')
|
||||
tb.set_update_listener(listener)
|
||||
|
||||
def test_message_handler(self):
|
||||
tb = telebot.TeleBot('')
|
||||
msg = self.create_text_message('/help')
|
||||
|
||||
@tb.message_handler(commands=['help', 'start'])
|
||||
def command_handler(message):
|
||||
message.text = 'got'
|
||||
|
||||
tb.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_reg(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'https://web.telegram.org/')
|
||||
|
||||
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'lambda_text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert msg.text == 'got'
|
||||
|
||||
def test_message_handler_lambda_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'text')
|
||||
|
||||
@bot.message_handler(func=lambda message: r'lambda' in message.text)
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert not msg.text == 'got'
|
||||
|
||||
def test_message_handler_reg_fail(self):
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'web.telegram.org/')
|
||||
|
||||
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
bot.process_new_messages([msg])
|
||||
time.sleep(1)
|
||||
assert not msg.text == 'got'
|
||||
|
||||
def test_send_message_with_markdown(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markdown = """
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
"""
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown")
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_disable_notification(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markdown = """
|
||||
*bold text*
|
||||
_italic text_
|
||||
[text](URL)
|
||||
"""
|
||||
ret_msg = tb.send_message(CHAT_ID, markdown, parse_mode="Markdown", disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, caption="Test")
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_video_more_params_dis_noti(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, 1, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_file_exception(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
try:
|
||||
tb.send_document(CHAT_ID, None)
|
||||
assert False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
assert True
|
||||
|
||||
def test_send_photo(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_photo_dis_noti(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_audio(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram')
|
||||
assert ret_msg.content_type == 'audio'
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_audio_dis_noti(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, 1, performer='eternnoir', title='pyTelegram',
|
||||
disable_notification=True)
|
||||
assert ret_msg.content_type == 'audio'
|
||||
assert ret_msg.audio.performer == 'eternnoir'
|
||||
assert ret_msg.audio.title == 'pyTelegram'
|
||||
|
||||
def test_send_voice(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_send_voice_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_get_file(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_get_file_dis_noti(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, disable_notification=True)
|
||||
file_id = ret_msg.voice.file_id
|
||||
file_info = tb.get_file(file_id)
|
||||
assert file_info.file_id == file_id
|
||||
|
||||
def test_send_message(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_dice(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_dice(CHAT_ID, emoji='🎯')
|
||||
assert ret_msg.message_id
|
||||
assert ret_msg.content_type == 'dice'
|
||||
|
||||
def test_send_message_dis_noti(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_markup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add(types.KeyboardButton("1"))
|
||||
markup.add(types.KeyboardButton("2"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_markup_use_string(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add("1")
|
||||
markup.add("2")
|
||||
markup.add("3")
|
||||
markup.add("4")
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_message_with_inlinemarkup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_forward_message(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_forward_message_dis_noti(self):
|
||||
text = 'CI forward_message Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.forward_message(CHAT_ID, CHAT_ID, msg.message_id, disable_notification=True)
|
||||
assert ret_msg.forward_from
|
||||
|
||||
def test_reply_to(self):
|
||||
text = 'CI reply_to Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text)
|
||||
ret_msg = tb.reply_to(msg, text + ' REPLY')
|
||||
assert ret_msg.reply_to_message.message_id == msg.message_id
|
||||
|
||||
def test_register_for_reply(self):
|
||||
text = 'CI reply_to Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, text, reply_markup=types.ForceReply())
|
||||
reply_msg = tb.reply_to(msg, text + ' REPLY')
|
||||
|
||||
def process_reply(message):
|
||||
assert msg.message_id == message.reply_to_message.message_id
|
||||
|
||||
tb.register_for_reply(msg, process_reply)
|
||||
|
||||
tb.process_new_messages([reply_msg])
|
||||
|
||||
def test_send_location(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_location(CHAT_ID, lat, lon)
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_send_location_dis_noti(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_location(CHAT_ID, lat, lon, disable_notification=True)
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def test_send_venue(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address")
|
||||
assert ret_msg.venue.title == "Test Venue"
|
||||
assert int(lat) == int(ret_msg.venue.location.latitude)
|
||||
|
||||
def test_send_venue_dis_noti(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
lat = 26.3875591
|
||||
lon = -161.2901042
|
||||
ret_msg = tb.send_venue(CHAT_ID, lat, lon, "Test Venue", "1123 Test Venue address", disable_notification=True)
|
||||
assert ret_msg.venue.title == "Test Venue"
|
||||
|
||||
def test_Chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
me = tb.get_me()
|
||||
msg = tb.send_message(CHAT_ID, 'Test')
|
||||
assert me.id == msg.from_user.id
|
||||
assert msg.chat.id == int(CHAT_ID)
|
||||
|
||||
def test_edit_message_text(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_message(CHAT_ID, 'Test')
|
||||
new_msg = tb.edit_message_text('Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
|
||||
assert new_msg.text == 'Edit test'
|
||||
|
||||
def test_edit_message_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_document(CHAT_ID, file_data, caption="Test")
|
||||
new_msg = tb.edit_message_caption(caption='Edit test', chat_id=CHAT_ID, message_id=msg.message_id)
|
||||
assert new_msg.caption == 'Edit test'
|
||||
|
||||
def test_edit_message_media(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
file_data_2 = open('../examples/detailed_example/rooster.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
msg = tb.send_photo(CHAT_ID, file_data)
|
||||
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
|
||||
media=types.InputMediaPhoto(file_data_2, caption='Test editMessageMedia 0'))
|
||||
assert type(new_msg) != bool
|
||||
|
||||
new_msg = tb.edit_message_media(chat_id=CHAT_ID, message_id=msg.message_id,
|
||||
media=types.InputMediaPhoto(msg.photo[0].file_id, caption='Test editMessageMedia'))
|
||||
assert type(new_msg) != bool
|
||||
assert new_msg.caption == 'Test editMessageMedia'
|
||||
|
||||
def test_get_chat(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ch = tb.get_chat(GROUP_ID)
|
||||
assert str(ch.id) == GROUP_ID
|
||||
|
||||
def test_get_chat_administrators(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
cas = tb.get_chat_administrators(GROUP_ID)
|
||||
assert len(cas) > 0
|
||||
|
||||
def test_get_chat_members_count(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
cn = tb.get_chat_members_count(GROUP_ID)
|
||||
assert cn > 1
|
||||
|
||||
def test_edit_markup(self):
|
||||
text = 'CI Test Message'
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
ret_msg = tb.send_message(CHAT_ID, text, disable_notification=True, reply_markup=markup)
|
||||
markup.add(types.InlineKeyboardButton("Google2", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo2", url="http://www.yahoo.com"))
|
||||
new_msg = tb.edit_message_reply_markup(chat_id=CHAT_ID, message_id=ret_msg.message_id, reply_markup=markup)
|
||||
assert new_msg.message_id
|
||||
|
||||
@staticmethod
|
||||
def create_text_message(text):
|
||||
params = {'text': text}
|
||||
chat = types.User(11, False, 'test')
|
||||
return types.Message(1, None, None, chat, 'text', params, "")
|
||||
|
||||
@staticmethod
|
||||
def create_message_update(text):
|
||||
params = {'text': text}
|
||||
chat = types.User(11, False, 'test')
|
||||
message = types.Message(1, None, None, chat, 'text', params, "")
|
||||
edited_message = None
|
||||
channel_post = None
|
||||
edited_channel_post = None
|
||||
inline_query = None
|
||||
chosen_inline_result = None
|
||||
callback_query = None
|
||||
shipping_query = None
|
||||
pre_checkout_query = None
|
||||
poll = None
|
||||
poll_answer = None
|
||||
return types.Update(-1001234038283, message, edited_message, channel_post, edited_channel_post, inline_query,
|
||||
chosen_inline_result, callback_query, shipping_query, pre_checkout_query, poll, poll_answer)
|
||||
|
||||
def test_is_string_unicode(self):
|
||||
s1 = u'string'
|
||||
assert util.is_string(s1)
|
||||
|
||||
def test_is_string_string(self):
|
||||
s1 = 'string'
|
||||
assert util.is_string(s1)
|
||||
|
||||
def test_not_string(self):
|
||||
i1 = 10
|
||||
assert not util.is_string(i1)
|
||||
|
||||
def test_send_video_note(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video_note(CHAT_ID, file_data)
|
||||
assert ret_msg.message_id
|
||||
|
||||
def test_send_media_group(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
img1 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
img2 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
medias = [types.InputMediaPhoto(img1, "View"), types.InputMediaPhoto(img2, "Dog")]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[0].media_group_id == result[1].media_group_id
|
||||
|
||||
def test_send_media_group_local_files(self):
|
||||
photo = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
video = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
medias = [types.InputMediaPhoto(photo, "View"),
|
||||
types.InputMediaVideo(video)]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[1].media_group_id is not None
|
||||
|
||||
def test_send_photo_formating_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_photo(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_video_formatting_caption(self):
|
||||
file_data = open('./test_data/test_video.mp4', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_video(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_audio_formatting_caption(self):
|
||||
file_data = open('./test_data/record.mp3', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_audio(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
|
||||
assert ret_msg.caption_entities[0].type == 'bold'
|
||||
|
||||
def test_send_voice_formatting_caprion(self):
|
||||
file_data = open('./test_data/record.ogg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_voice(CHAT_ID, file_data, caption='<b>bold</b>', parse_mode='HTML')
|
||||
assert ret_msg.caption_entities[0].type == 'bold'
|
||||
assert ret_msg.voice.mime_type == 'audio/ogg'
|
||||
|
||||
def test_send_media_group_formatting_caption(self):
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
img1 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
img2 = 'https://i.imgur.com/CjXjcnU.png'
|
||||
medias = [types.InputMediaPhoto(img1, "*View*", parse_mode='Markdown'),
|
||||
types.InputMediaPhoto(img2, "_Dog_", parse_mode='Markdown')]
|
||||
result = tb.send_media_group(CHAT_ID, medias)
|
||||
assert len(result) == 2
|
||||
assert result[0].media_group_id is not None
|
||||
assert result[0].caption_entities[0].type == 'bold'
|
||||
assert result[1].caption_entities[0].type == 'italic'
|
||||
|
||||
def test_send_document_formating_caption(self):
|
||||
file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
ret_msg = tb.send_document(CHAT_ID, file_data, caption='_italic_', parse_mode='Markdown')
|
||||
assert ret_msg.caption_entities[0].type == 'italic'
|
||||
|
||||
def test_typed_middleware_handler(self):
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
|
||||
tb = telebot.TeleBot('')
|
||||
update = self.create_message_update('/help')
|
||||
|
||||
@tb.middleware_handler(update_types=['message'])
|
||||
def middleware(tb_instance, message):
|
||||
message.text = 'got'
|
||||
|
||||
@tb.message_handler(func=lambda m: m.text == 'got')
|
||||
def command_handler(message):
|
||||
message.text = message.text + message.text
|
||||
|
||||
tb.process_new_updates([update])
|
||||
time.sleep(1)
|
||||
assert update.message.text == 'got' * 2
|
||||
|
||||
def test_default_middleware_handler(self):
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
|
||||
tb = telebot.TeleBot('')
|
||||
update = self.create_message_update('/help')
|
||||
|
||||
@tb.middleware_handler()
|
||||
def middleware(tb_instance, update):
|
||||
update.message.text = 'got'
|
||||
|
||||
@tb.message_handler(func=lambda m: m.text == 'got')
|
||||
def command_handler(message):
|
||||
message.text = message.text + message.text
|
||||
|
||||
tb.process_new_updates([update])
|
||||
time.sleep(1)
|
||||
assert update.message.text == 'got' * 2
|
||||
|
||||
def test_chat_permissions(self):
|
||||
return # CHAT_ID is private chat, no permissions can be set
|
||||
tb = telebot.TeleBot(TOKEN)
|
||||
permissions = types.ChatPermissions(can_send_messages=True, can_send_polls=False)
|
||||
msg = tb.set_chat_permissions(CHAT_ID, permissions)
|
@ -6,23 +6,32 @@ from telebot import types
|
||||
|
||||
|
||||
def test_json_user():
|
||||
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot"}'
|
||||
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","username":"rdss_bot","is_bot":true}'
|
||||
u = types.User.de_json(jsonstring)
|
||||
assert u.id == 101176298
|
||||
|
||||
|
||||
def test_json_message():
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"chat":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir"},"date":1435296025,"text":"HIHI"}'
|
||||
jsonstring = r'{"message_id":1,"from":{"id":108929734,"first_name":"Frank","last_name":"Wang","username":"eternnoir","is_bot":true},"chat":{"id":1734,"first_name":"F","type":"private","last_name":"Wa","username":"oir"},"date":1435296025,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(jsonstring)
|
||||
assert msg.text == 'HIHI'
|
||||
|
||||
|
||||
def test_json_message_with_dice():
|
||||
jsonstring = r'{"message_id":5560,"from":{"id":879343317,"is_bot":false,"first_name":"George","last_name":"Forse","username":"dr_fxrse","language_code":"ru"},"chat":{"id":879343317,"first_name":"George","last_name":"Forse","username":"dr_fxrse","type":"private"},"date":1586926330,"dice":{"value": 4, "emoji": "\ud83c\udfaf"}}'
|
||||
msg = types.Message.de_json(jsonstring)
|
||||
assert msg.content_type == 'dice'
|
||||
assert isinstance(msg.dice, types.Dice)
|
||||
assert msg.dice.value == 4
|
||||
assert msg.dice.emoji == '🎯'
|
||||
|
||||
|
||||
def test_json_message_group():
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG"},"chat":{"id":-866,"title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
json_string = r'{"message_id":10,"from":{"id":12345,"first_name":"g","last_name":"G","username":"GG","is_bot":true},"chat":{"id":-866,"type":"private","title":"\u4ea4"},"date":1435303157,"text":"HIHI"}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.text == 'HIHI'
|
||||
assert len(msg.chat.title) != 0
|
||||
assert msg.fromUser.username == 'GG'
|
||||
assert msg.from_user.username == 'GG'
|
||||
|
||||
|
||||
def test_json_GroupChat():
|
||||
@ -39,36 +48,46 @@ def test_json_Document():
|
||||
|
||||
|
||||
def test_json_Message_Audio():
|
||||
json_string = r'{"message_id":100,"from":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":10734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481343,"audio":{"duration":3,"mime_type":"audio\/ogg","file_id":"ddg","file_size":8249}}'
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd","is_bot":true },"chat":{"id":10834,"first_name":"dd","type":"private","type":"private","last_name":"dd","username":"dd"},"date":1439978364,"audio":{"duration":1,"mime_type":"audio\/mpeg","title":"pyTelegram","performer":"eternnoir","file_id":"BQADBQADDH1JaB8-1KyWUss2-Ag","file_size":20096}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.audio.duration == 3
|
||||
assert msg.audio.duration == 1
|
||||
assert msg.content_type == 'audio'
|
||||
assert msg.audio.performer == 'eternnoir'
|
||||
assert msg.audio.title == 'pyTelegram'
|
||||
|
||||
|
||||
def test_json_Message_Sticker():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"thumb":{"file_id":"AAQFABPJLB0sAAQq17w-li3bzoIfAAIC","file_size":1822,"width":90,"height":60},"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb.height == 60
|
||||
assert msg.content_type == 'sticker'
|
||||
|
||||
|
||||
def test_json_Message_Sticker_without_thumb():
|
||||
json_string = r'{"message_id":98,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"is_animated":true,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb is None
|
||||
assert msg.content_type == 'sticker'
|
||||
|
||||
|
||||
def test_json_Message_Document():
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
json_string = r'{"message_id":97,"from":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10,"first_name":"Fd","type":"private","last_name":"Wd","username":"dd"},"date":1435478744,"document":{"file_name":"Text File","thumb":{},"file_id":"BQADBQADMwIAAsYifgZ_CEh0u682xwI","file_size":446}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.document.file_name == 'Text File'
|
||||
assert msg.content_type == 'document'
|
||||
|
||||
|
||||
def test_json_Message_Photo():
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd"},"chat":{"id":10734,"first_name":"Fd","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
json_string = r'{"message_id":96,"from":{"id":109734,"first_name":"Fd","last_name":"Wd","username":"dd","is_bot":true },"chat":{"id":10734,"first_name":"Fd","type":"private","last_name":"dd","username":"dd"},"date":1435478191,"photo":[{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":615,"width":90,"height":67},{"file_id":"AgADBQADIagxG8YifgYv8yLSj76i-dd","file_size":10174,"width":320,"height":240},{"file_id":"dd-A_LsTIABFNx-FUOaEa_3AABAQABAg","file_size":53013,"width":759,"height":570}]}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert len(msg.photo) == 3
|
||||
assert msg.content_type == 'photo'
|
||||
|
||||
|
||||
def test_json_Message_Video():
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
json_string = r'{"message_id":101,"from":{"id":109734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":109734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1435481960,"video":{"duration":3,"caption":"","width":360,"height":640,"thumb":{"file_id":"AAQFABPiYnBjkDwMAAIC","file_size":1597,"width":50,"height":90},"file_id":"BAADBQADNifgb_TOPEKErGoQI","file_size":260699}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.video
|
||||
assert msg.video.duration == 3
|
||||
@ -77,7 +96,7 @@ def test_json_Message_Video():
|
||||
|
||||
|
||||
def test_json_Message_Location():
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd"},"chat":{"id":1089734,"first_name":"dd","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
json_string = r'{"message_id":102,"from":{"id":108734,"first_name":"dd","last_name":"dd","username":"dd","is_bot":true },"chat":{"id":1089734,"first_name":"dd","type":"private","last_name":"dd","username":"dd"},"date":1535482469,"location":{"longitude":127.479471,"latitude":26.090577}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.location.latitude == 26.090577
|
||||
assert msg.content_type == 'location'
|
||||
@ -95,3 +114,85 @@ def test_json_contact():
|
||||
contact = types.Contact.de_json(json_string)
|
||||
assert contact.first_name == 'dd'
|
||||
assert contact.last_name == 'ddl'
|
||||
|
||||
|
||||
def test_json_voice():
|
||||
json_string = r'{"duration": 0,"mime_type": "audio/ogg","file_id": "AwcccccccDH1JaB7w_gyFjYQxVAg","file_size": 10481}'
|
||||
voice = types.Voice.de_json(json_string)
|
||||
assert voice.duration == 0
|
||||
assert voice.file_size == 10481
|
||||
|
||||
|
||||
def test_json_update():
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"is_bot":true,"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","type":"private","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
update = types.Update.de_json(json_string)
|
||||
assert update.update_id == 938203
|
||||
assert update.message.message_id == 241
|
||||
assert update.message.from_user.id == 9734
|
||||
|
||||
|
||||
def test_json_chat():
|
||||
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}'
|
||||
chat = types.Chat.de_json(json_string)
|
||||
assert chat.id == -111111
|
||||
assert chat.type == 'group'
|
||||
assert chat.title == 'Test Title'
|
||||
|
||||
|
||||
def test_InlineQueryResultCachedPhoto():
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid')
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'caption' not in json_str
|
||||
|
||||
|
||||
def test_InlineQueryResultCachedPhoto_with_title():
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid', title='Title')
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'Title' in json_str
|
||||
assert 'caption' not in json_str
|
||||
|
||||
|
||||
def test_InlineQueryResultCachedPhoto_with_markup():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton("Google", url="http://www.google.com"))
|
||||
markup.add(types.InlineKeyboardButton("Yahoo", url="http://www.yahoo.com"))
|
||||
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid', title='Title', reply_markup=markup)
|
||||
json_str = iq.to_json()
|
||||
assert 'aa' in json_str
|
||||
assert 'Fileid' in json_str
|
||||
assert 'Title' in json_str
|
||||
assert 'caption' not in json_str
|
||||
assert 'reply_markup' in json_str
|
||||
|
||||
|
||||
def test_json_poll_1():
|
||||
jsonstring = r'{"message_id": 395020,"from": {"id": 111,"is_bot": false,"first_name": "FN","last_name": "LN","username": "Badiboy","language_code": "ru"},"chat": {"id": 111,"first_name": "FN","last_name": "LN","username": "Badiboy","type": "private"},"date": 1587841239,"poll": {"id": "5272018969396510722","question": "Test poll 1","options": [{"text": "Answer 1","voter_count": 0},{"text": "Answer 2","voter_count": 0}],"total_voter_count": 0,"is_closed": false,"is_anonymous": true,"type": "regular","allows_multiple_answers": true}}'
|
||||
msg = types.Message.de_json(jsonstring)
|
||||
assert msg.poll is not None
|
||||
assert isinstance(msg.poll, types.Poll)
|
||||
assert msg.poll.id == '5272018969396510722'
|
||||
assert msg.poll.question is not None
|
||||
assert msg.poll.options is not None
|
||||
assert len(msg.poll.options) == 2
|
||||
assert msg.poll.allows_multiple_answers is True
|
||||
|
||||
|
||||
def test_json_poll_answer():
|
||||
jsonstring = r'{"poll_id": "5895675970559410186", "user": {"id": 329343347, "is_bot": false, "first_name": "Test", "username": "test_user", "last_name": "User", "language_code": "en"}, "option_ids": [1]}'
|
||||
__import__('pprint').pprint(__import__('json').loads(jsonstring))
|
||||
poll_answer = types.PollAnswer.de_json(jsonstring)
|
||||
assert poll_answer.poll_id == '5895675970559410186'
|
||||
assert isinstance(poll_answer.user, types.User)
|
||||
assert poll_answer.options_ids == [1]
|
||||
|
||||
|
||||
def test_KeyboardButtonPollType():
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.add(types.KeyboardButton('send me a poll', request_poll=types.KeyboardButtonPollType(type='quiz')))
|
||||
json_str = markup.to_json()
|
||||
assert 'request_poll' in json_str
|
||||
assert 'quiz' in json_str
|
||||
|
Reference in New Issue
Block a user