mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
806 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
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/
|
||||
|
@ -1,9 +1,9 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.4"
|
||||
- "pypy"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
script:
|
||||
|
334
README.md
334
README.md
@ -1,9 +1,10 @@
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the [Telegram Bot API](https://core.telegram.org/bots/api).
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
|
||||
|
||||
|
||||
<p align="center">[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
|
||||
* [Getting started.](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
@ -14,8 +15,11 @@
|
||||
* [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)
|
||||
@ -23,10 +27,15 @@
|
||||
* [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)
|
||||
|
||||
## Getting started.
|
||||
|
||||
@ -118,8 +127,12 @@ To start the bot, simply open up a terminal and enter `python echo_bot.py` to ru
|
||||
|
||||
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', 'location', 'contact', 'new_chat_participant', 'left_chat_participant', 'new_chat_title', 'new_chat_photo', 'delete_chat_photo', 'group_chat_created'.
|
||||
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
|
||||
|
||||
@ -145,11 +158,12 @@ 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)|
|
||||
|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
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
@ -169,20 +183,68 @@ def handle_docs_audio(message):
|
||||
def handle_message(message):
|
||||
pass
|
||||
|
||||
#Handles all messages for which the lambda returns True
|
||||
# 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
|
||||
|
||||
#Which could also be defined as:
|
||||
# Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plan'
|
||||
return message.document.mime_type == 'text/plain'
|
||||
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
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**
|
||||
|
||||
#### 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
|
||||
@ -194,18 +256,23 @@ tb = telebot.TeleBot(TOKEN) #create a new Telegram Bot object
|
||||
# - 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
|
||||
# - block: True/False (default True) - Blocks upon calling this function
|
||||
tb.polling(none_stop=False, interval=0, block=True)
|
||||
# - 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(to_chat_id, from_chat_id, message_id)
|
||||
@ -244,6 +311,11 @@ 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)
|
||||
|
||||
@ -251,9 +323,19 @@ tb.send_location(chat_id, lat, lon)
|
||||
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
# 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`, `ReplyKeyboardHide` or `ForceReply`, which are defined in types.py.
|
||||
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
|
||||
@ -267,23 +349,31 @@ from telebot import types
|
||||
# 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)
|
||||
markup.add('a', 'v', 'd')
|
||||
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')
|
||||
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
|
||||
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
|
||||
# ReplyKeyboardRemove: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardHide(selective=False)
|
||||
markup = types.ReplyKeyboardRemove(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
```
|
||||
|
||||
@ -295,7 +385,65 @@ 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
|
||||
|
||||
@ -335,15 +483,17 @@ for text in splitted_text:
|
||||
### Controlling the amount of Threads used by TeleBot
|
||||
The TeleBot constructor takes the following optional arguments:
|
||||
|
||||
- create_threads: True/False (default True). A flag to indicate whether
|
||||
- threaded: True/False (default True). A flag to indicate whether
|
||||
TeleBot should execute message handlers on it's polling Thread.
|
||||
- num_threads: integer (default 4). Controls the amount of WorkerThreads created for the internal thread pool that TeleBot uses to execute message handlers. Is not used when create_threads is False.
|
||||
|
||||
### The listener mechanism
|
||||
As an alternative to the message handlers, one can also register a function as a listener to TeleBot. Example:
|
||||
As an alternative to the message handlers, one can also register a function as a listener to TeleBot.
|
||||
|
||||
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 messsages:
|
||||
for message in messages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
@ -352,7 +502,9 @@ bot.polling()
|
||||
```
|
||||
|
||||
### Using web hooks
|
||||
If you prefer using web hooks to the getUpdates method, you can use the `process_new_messages(messages)` function in TeleBot to make it process the messages that you supply. It takes a list of Message objects. This function is still incubating.
|
||||
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
|
||||
|
||||
@ -366,34 +518,138 @@ logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
```
|
||||
|
||||
### Proxy
|
||||
|
||||
You can use proxy for request. `apihelper.proxy` object will use by call `requests` proxies argument.
|
||||
|
||||
```python
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.proxy = {'http':'http://10.10.1.10:3128'}
|
||||
```
|
||||
|
||||
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
|
||||
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
|
||||
```
|
||||
|
||||
|
||||
## 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?
|
||||
There are two ways to do this:
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Checking the instance of message.chat with `isinstance`:
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
-
|
||||
```python
|
||||
def is_user(chat):
|
||||
return isinstance(chat, types.User)
|
||||
if message.chat.type == "private":
|
||||
# private chat message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:
|
||||
```python
|
||||
def is_user(chat):
|
||||
return chat.id > 0
|
||||
if message.chat.type == "group":
|
||||
# group chat message
|
||||
|
||||
if message.chat.type == "supergroup":
|
||||
# supergroup chat message
|
||||
|
||||
if message.chat.type == "channel":
|
||||
# channel message
|
||||
|
||||
print is_user(message.chat) # True or False
|
||||
```
|
||||
|
||||
## The Telegram Chat Group
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b).
|
||||
* 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.
|
||||
|
528
README.rst
528
README.rst
@ -1,528 +0,0 @@
|
||||
#
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
pyTelegramBotAPI
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
A simple, but extensible Python implementation for the `Telegram Bot
|
||||
API <https://core.telegram.org/bots/api>`__.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<p align="center">
|
||||
|
||||
|Build Status|
|
||||
|
||||
- `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>`__
|
||||
- `TeleBot <#telebot>`__
|
||||
- `Reply markup <#reply-markup>`__
|
||||
|
||||
- `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>`__
|
||||
|
||||
- `F.A.Q. <#faq>`__
|
||||
|
||||
- `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>`__
|
||||
|
||||
Getting started.
|
||||
================
|
||||
|
||||
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
|
||||
$ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
|
||||
It is generally recommended to use the first option.
|
||||
|
||||
\*\*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``\ \*
|
||||
|
||||
Writing your first bot
|
||||
======================
|
||||
|
||||
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.
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
*Note: Make sure to actually replace TOKEN with your own API token.*
|
||||
|
||||
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.
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
.. code:: python
|
||||
|
||||
bot.polling()
|
||||
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
|
||||
.. code:: 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',
|
||||
'location', 'contact', 'new\_chat\_participant',
|
||||
'left\_chat\_participant', 'new\_chat\_title', 'new\_chat\_photo',
|
||||
'delete\_chat\_photo', 'group\_chat\_created'.
|
||||
|
||||
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 which is decorated with the
|
||||
``message_handler`` decorator of a TeleBot instance. The following
|
||||
examples illustrate the possibilities of message handlers:
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
#Which could also be defined as:
|
||||
def test_message(message):
|
||||
return message.document.mime_type == 'text/plan'
|
||||
|
||||
@bot.message_handler(func=test_message, content_types=['document'])
|
||||
def handle_text_doc(message)
|
||||
pass
|
||||
|
||||
*Note: all handlers are tested in the order in which they were declared*
|
||||
#### TeleBot
|
||||
|
||||
.. code:: python
|
||||
|
||||
import telebot
|
||||
|
||||
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
|
||||
# - block: True/False (default True) - Blocks upon calling this function
|
||||
tb.polling(none_stop=False, interval=0, block=True)
|
||||
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chatid, text)
|
||||
|
||||
# forwardMessage
|
||||
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.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")
|
||||
|
||||
# sendLocation
|
||||
tb.send_location(chat_id, lat, lon)
|
||||
|
||||
# sendChatAction
|
||||
# action_string can be one of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
|
||||
# 'record_audio', 'upload_audio', 'upload_document' or 'find_location'.
|
||||
tb.send_chat_action(chat_id, action_string)
|
||||
|
||||
Reply markup
|
||||
~~~~~~~~~~~~
|
||||
|
||||
All ``send_xyz`` functions of TeleBot take an optional ``reply_markup``
|
||||
argument. This argument must be an instance of ``ReplyKeyboardMarkup``,
|
||||
``ReplyKeyboardHide`` or ``ForceReply``, which are defined in types.py.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from telebot import types
|
||||
|
||||
# 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)
|
||||
markup.add('a', 'v', 'd')
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
# or add strings one row at a time:
|
||||
markup = types.ReplyKeyboardMarkup()
|
||||
markup.row('a', 'v')
|
||||
markup.row('c', 'd', 'e')
|
||||
tb.send_message(chat_id, "Choose one letter:", reply_markup=markup)
|
||||
|
||||
The last example yields this result:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473e5/_mxxW7FPe4U.jpg
|
||||
:alt: ReplyKeyboardMarkup
|
||||
|
||||
ReplyKeyboardMarkup
|
||||
|
||||
.. code:: python
|
||||
|
||||
# ReplyKeyboardHide: hides a previously sent ReplyKeyboardMarkup
|
||||
# Takes an optional selective argument (True/False, default False)
|
||||
markup = types.ReplyKeyboardHide(selective=False)
|
||||
tb.send_message(chat_id, message, reply_markup=markup)
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
.. figure:: https://pp.vk.me/c624430/v624430512/473ec/602byyWUHcs.jpg
|
||||
:alt: ForceReply
|
||||
|
||||
ForceReply
|
||||
|
||||
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.
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
.. code:: 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:
|
||||
|
||||
- create\_threads: True/False (default True). A flag to indicate
|
||||
whether TeleBot should execute message handlers on it's polling
|
||||
Thread.
|
||||
- num\_threads: integer (default 4). Controls the amount of
|
||||
WorkerThreads created for the internal thread pool that TeleBot uses
|
||||
to execute message handlers. Is not used when create\_threads is
|
||||
False.
|
||||
|
||||
The listener mechanism
|
||||
----------------------
|
||||
|
||||
As an alternative to the message handlers, one can also register a
|
||||
function as a listener to TeleBot. Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
def handle_messages(messages):
|
||||
for message in messsages:
|
||||
# Do something with the message
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
|
||||
Using web hooks
|
||||
---------------
|
||||
|
||||
If you prefer using web hooks to the getUpdates method, you can use the
|
||||
``process_new_messages(messages)`` function in TeleBot to make it
|
||||
process the messages that you supply. It takes a list of Message
|
||||
objects. This function is still incubating.
|
||||
|
||||
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.
|
||||
|
||||
.. code:: python
|
||||
|
||||
logger = telebot.logger
|
||||
formatter = logging.Formatter('[%(asctime)s] %(thread)d {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
|
||||
'%m-%d %H:%M:%S')
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
logger.addHandler(ch)
|
||||
logger.setLevel(logging.DEBUG) # or use logging.INFO
|
||||
ch.setFormatter(formatter)
|
||||
|
||||
F.A.Q.
|
||||
======
|
||||
|
||||
How can I distinguish a User and a GroupChat in message.chat?
|
||||
-------------------------------------------------------------
|
||||
|
||||
There are two ways to do this:
|
||||
|
||||
- Checking the instance of message.chat with ``isinstance``:
|
||||
\`\`\`python def is\_user(chat): return isinstance(chat, types.User)
|
||||
|
||||
print is\_user(message.chat) # True or False
|
||||
``- Checking whether the chat id is negative or positive. If the chat id is negative, the chat is a GroupChat, if it is positive, it is a User. Example:``\ python
|
||||
def is\_user(chat): return chat.id > 0
|
||||
|
||||
print is\_user(message.chat) # True or False \`\`\`
|
||||
|
||||
The Telegram Chat Group
|
||||
=======================
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
Join the `pyTelegramBotAPI Telegram Chat
|
||||
Group <https://telegram.me/joinchat/067e22c60035523fda8f6025ee87e30b>`__.
|
||||
|
||||
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>`__
|
||||
|
||||
.. |Build Status| image:: https://travis-ci.org/eternnoir/pyTelegramBotAPI.svg?branch=master
|
||||
:target: https://travis-ci.org/eternnoir/pyTelegramBotAPI
|
@ -1,3 +1,5 @@
|
||||
#!/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.
|
||||
@ -31,34 +33,38 @@
|
||||
# steps are not shown here. Only steps 5 to 7 are illustrated, some in pseudo-code, with this example.
|
||||
|
||||
import telebot
|
||||
import time
|
||||
|
||||
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
|
||||
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
|
||||
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:
|
||||
@ -67,4 +73,5 @@ def send_welcome(message):
|
||||
reply = "Please visit me via a provided URL from the website."
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
|
||||
bot.polling()
|
||||
|
@ -2,9 +2,10 @@
|
||||
This is a detailed example using almost every command of the API
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
import time
|
||||
|
||||
TOKEN = '<token_string>'
|
||||
|
||||
@ -12,16 +13,16 @@ 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'
|
||||
'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('cock', 'pussy')
|
||||
imageSelect.add('Mickey', 'Minnie')
|
||||
|
||||
hideBoard = types.ReplyKeyboardHide() # if sent as reply_markup, will hide the keyboard
|
||||
hideBoard = types.ReplyKeyboardRemove() # if sent as reply_markup, will hide the keyboard
|
||||
|
||||
|
||||
# error handling if user isn't known yet
|
||||
@ -33,7 +34,7 @@ def get_user_step(uid):
|
||||
else:
|
||||
knownUsers.append(uid)
|
||||
userStep[uid] = 0
|
||||
print "New user detected, who hasn't used \"/start\" yet"
|
||||
print("New user detected, who hasn't used \"/start\" yet")
|
||||
return 0
|
||||
|
||||
|
||||
@ -45,7 +46,7 @@ def listener(messages):
|
||||
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
|
||||
print(str(m.chat.first_name) + " [" + str(m.chat.id) + "]: " + m.text)
|
||||
|
||||
|
||||
bot = telebot.TeleBot(TOKEN)
|
||||
@ -104,15 +105,15 @@ def msg_image_select(m):
|
||||
# for some reason the 'upload_photo' status isn't quite working (doesn't show at all)
|
||||
bot.send_chat_action(cid, 'typing')
|
||||
|
||||
if text == "cock": # send the appropriate image based on the reply to the "/getImage" command
|
||||
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 == "pussy":
|
||||
elif text == 'Minnie':
|
||||
bot.send_photo(cid, open('kitten.jpg', 'rb'), reply_markup=hideBoard)
|
||||
userStep[cid] = 0
|
||||
else:
|
||||
bot.send_message(cid, "Don't type bullsh*t, if I give you a predefined keyboard!")
|
||||
bot.send_message(cid, "Please, use the predefined keyboard!")
|
||||
bot.send_message(cid, "Please try again")
|
||||
|
||||
|
||||
@ -128,4 +129,5 @@ 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()
|
||||
|
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.
|
||||
|
||||
@ -7,6 +9,7 @@ API_TOKEN = '<api_token>'
|
||||
|
||||
bot = telebot.TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
def send_welcome(message):
|
||||
@ -21,4 +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()
|
||||
|
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
|
||||
|
@ -2,7 +2,6 @@
|
||||
"""
|
||||
This Example will show you how to use register_next_step handler.
|
||||
"""
|
||||
import time
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
@ -75,4 +74,13 @@ def process_sex_step(message):
|
||||
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()
|
||||
|
@ -3,9 +3,10 @@
|
||||
# and goes by the name 'TeleBot (@pyTeleBot)'. Join our group to talk to him!
|
||||
# WARNING: Tested with Python 2.7
|
||||
|
||||
import telebot
|
||||
import os
|
||||
|
||||
import telebot
|
||||
|
||||
text_messages = {
|
||||
'welcome':
|
||||
u'Please welcome {name}!\n\n'
|
||||
@ -33,8 +34,10 @@ if "TELEBOT_BOT_TOKEN" not in os.environ or "GROUP_CHAT_ID" not in os.environ:
|
||||
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
|
||||
return chat_id == GROUP_CHAT_ID
|
||||
|
||||
|
||||
@bot.message_handler(func=lambda m: True, content_types=['new_chat_participant'])
|
||||
def on_user_joins(message):
|
||||
@ -50,6 +53,7 @@ def on_user_joins(message):
|
||||
|
||||
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):
|
||||
@ -58,21 +62,23 @@ def on_info(message):
|
||||
|
||||
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)
|
||||
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,5 +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
|
||||
|
21
setup.py
21
setup.py
@ -1,26 +1,29 @@
|
||||
#!/usr/bin/env python
|
||||
from setuptools import setup
|
||||
from io import open
|
||||
|
||||
def readme():
|
||||
with open('README.rst') as f:
|
||||
return f.read()
|
||||
def read(filename):
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
return file.read()
|
||||
|
||||
setup(name='pyTelegramBotAPI',
|
||||
version='0.3.4',
|
||||
version='3.7.1',
|
||||
description='Python Telegram bot api. ',
|
||||
long_description=readme(),
|
||||
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='telegram bot api tools',
|
||||
install_requires=['pytest', 'requests', 'six'],
|
||||
install_requires=['requests', 'six'],
|
||||
extras_require={
|
||||
'json': 'ujson',
|
||||
'redis': 'redis>=3.4.1'
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Environment :: Console',
|
||||
'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
|
||||
|
2123
telebot/__init__.py
2123
telebot/__init__.py
File diff suppressed because it is too large
Load Diff
1012
telebot/apihelper.py
1012
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
|
2294
telebot/types.py
2294
telebot/types.py
File diff suppressed because it is too large
Load Diff
215
telebot/util.py
215
telebot/util.py
@ -1,54 +1,125 @@
|
||||
# -*- 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:
|
||||
class WorkerThread(threading.Thread):
|
||||
count = 0
|
||||
|
||||
def __init__(self, queue):
|
||||
threading.Thread.__init__(self, name="WorkerThread{0}".format(self.__class__.count + 1))
|
||||
self.__class__.count += 1
|
||||
self.queue = queue
|
||||
self.daemon = True
|
||||
|
||||
self._running = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
while self._running:
|
||||
try:
|
||||
task, args, kwargs = self.queue.get(block=True, timeout=.01)
|
||||
task(*args, **kwargs)
|
||||
except Queue.Empty:
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
|
||||
def __init__(self, num_threads=4):
|
||||
def __init__(self, num_threads=2):
|
||||
self.tasks = Queue.Queue()
|
||||
self.workers = [self.WorkerThread(self.tasks) for _ in range(num_threads)]
|
||||
|
||||
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
|
||||
@ -62,20 +133,20 @@ class AsyncTask:
|
||||
def _run(self):
|
||||
try:
|
||||
self.result = self.target(*self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
self.result = e
|
||||
except:
|
||||
self.result = sys.exc_info()
|
||||
self.done = True
|
||||
|
||||
def wait(self):
|
||||
if not self.done:
|
||||
self.thread.join()
|
||||
if isinstance(self.result, Exception):
|
||||
raise self.result
|
||||
if isinstance(self.result, BaseException):
|
||||
six.reraise(self.result[0], self.result[1], self.result[2])
|
||||
else:
|
||||
return self.result
|
||||
|
||||
|
||||
def async():
|
||||
def async_dec():
|
||||
def decorator(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
return AsyncTask(fn, *args, **kwargs)
|
||||
@ -124,3 +195,85 @@ def split_string(text, chars_per_string):
|
||||
: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
|
||||
|
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'
|
@ -9,7 +9,6 @@ import os
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
from telebot import apihelper
|
||||
from telebot import util
|
||||
|
||||
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
|
||||
@ -17,6 +16,7 @@ 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")
|
||||
@ -48,7 +48,7 @@ class TestTeleBot:
|
||||
bot = telebot.TeleBot('')
|
||||
msg = self.create_text_message(r'https://web.telegram.org/')
|
||||
|
||||
@bot.message_handler(regexp='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
@ -56,11 +56,35 @@ class TestTeleBot:
|
||||
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='((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
@bot.message_handler(regexp=r'((https?):((//)|(\\\\))+([\w\d:#@%/;$()~_?\+-=\\\.&](#!)?)*)')
|
||||
def command_url(message):
|
||||
msg.text = 'got'
|
||||
|
||||
@ -78,6 +102,15 @@ class TestTeleBot:
|
||||
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')
|
||||
@ -88,18 +121,48 @@ class TestTeleBot:
|
||||
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:
|
||||
@ -118,10 +181,28 @@ class TestTeleBot:
|
||||
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, 'eternnoir', 'pyTelegram')
|
||||
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'
|
||||
@ -132,12 +213,75 @@ class TestTeleBot:
|
||||
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)
|
||||
@ -145,6 +289,13 @@ class TestTeleBot:
|
||||
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)
|
||||
@ -173,10 +324,113 @@ class TestTeleBot:
|
||||
assert int(ret_msg.location.longitude) == int(lon)
|
||||
assert int(ret_msg.location.latitude) == int(lat)
|
||||
|
||||
def create_text_message(self, text):
|
||||
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, 'test')
|
||||
return types.Message(1, None, None, chat, 'text', params)
|
||||
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'
|
||||
@ -190,3 +444,118 @@ class TestTeleBot:
|
||||
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,19 +6,28 @@ 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
|
||||
@ -39,7 +48,7 @@ def test_json_Document():
|
||||
|
||||
|
||||
def test_json_Message_Audio():
|
||||
json_string = r'{"message_id":131,"from":{"id":12775,"first_name":"dd","username":"dd"},"chat":{"id":10834,"first_name":"dd","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}}'
|
||||
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 == 1
|
||||
assert msg.content_type == 'audio'
|
||||
@ -48,7 +57,7 @@ def test_json_Message_Audio():
|
||||
|
||||
|
||||
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
|
||||
@ -56,29 +65,29 @@ def test_json_Message_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"},"chat":{"id":10734,"first_name":"Fd","last_name":"Wd","username":"dd"},"date":1435479551,"sticker":{"width":550,"height":368,"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,"file_id":"BQADBQADNAIAAsYifgYdGJOa6bGAsQI","file_size":30320}}'
|
||||
msg = types.Message.de_json(json_string)
|
||||
assert msg.sticker.height == 368
|
||||
assert msg.sticker.thumb == None
|
||||
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
|
||||
@ -87,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'
|
||||
@ -113,10 +122,77 @@ def test_json_voice():
|
||||
assert voice.duration == 0
|
||||
assert voice.file_size == 10481
|
||||
|
||||
|
||||
def test_json_update():
|
||||
json_string = r'{"update_id":938203,"message":{"message_id":241,"from":{"id":9734,"first_name":"Fk","last_name":"Wg","username":"nir"},"chat":{"id":1111,"first_name":"Fk","last_name":"Wg","username":"oir"},"date":1441447009,"text":"HIHI"}}'
|
||||
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