mirror of
https://github.com/eternnoir/pyTelegramBotAPI.git
synced 2023-08-10 21:12:57 +03:00
Compare commits
748 Commits
3.7.5
...
b9b4885568
Author | SHA1 | Date | |
---|---|---|---|
b9b4885568 | |||
ce0a974c91 | |||
6ff015689a | |||
6459aded82 | |||
fec47cecaf | |||
3892b0fb80 | |||
fbb9a73fc0 | |||
db5c53b8e5 | |||
6e8abc709e | |||
752f35614c | |||
a2893945b2 | |||
1686ce4f44 | |||
24b1129c8a | |||
7f9dac4147 | |||
f96775e9eb | |||
1342cab259 | |||
dd8125cbd0 | |||
8769802744 | |||
a7fb8a2bec | |||
d7f34ae370 | |||
7d931abe37 | |||
f52124827f | |||
0d0f9ef330 | |||
24cdcb1bcc | |||
7994a80a00 | |||
b2662274f9 | |||
a21ab203a1 | |||
e689e968db | |||
808810e6d6 | |||
5a60846c7f | |||
d2c1615392 | |||
7cdb48c3e0 | |||
28ae0867f8 | |||
e84cc0e0af | |||
42ce47914d | |||
4401780ba9 | |||
82f056e88a | |||
6b19d631d1 | |||
ee7adb00df | |||
82a8aa65f0 | |||
72aaf44dc7 | |||
1d0efce76e | |||
74d0604c05 | |||
1943f659bc | |||
d6ec104829 | |||
7edaa51995 | |||
6bb47e9a44 | |||
8da749ee05 | |||
0c59d1187e | |||
e9d1d98f03 | |||
c63b0d6a3d | |||
388306b7fe | |||
5e3fd17436 | |||
ccc09ffaf3 | |||
e086303535 | |||
d6e93f85f1 | |||
d954f8f5b3 | |||
33375ac135 | |||
28662876a2 | |||
0f44fd3c40 | |||
8fefd7b5b3 | |||
7567750276 | |||
f526a9d8a4 | |||
feffe726dd | |||
3ff7e28467 | |||
5d74e18d1a | |||
91b665ea94 | |||
6e4c63b09c | |||
42efb8488c | |||
03a567ea93 | |||
3f28bb6e9d | |||
e051dda113 | |||
856debe7d2 | |||
42955d1886 | |||
a2f3cd03e1 | |||
7e68721475 | |||
7965ba4f69 | |||
aba4d3e246 | |||
caae6bb93f | |||
096d7a4eea | |||
ab4140ba9f | |||
77c3587012 | |||
efb1b44e59 | |||
2c8793b794 | |||
146fd57b10 | |||
8a12ae3565 | |||
2d8c2312e3 | |||
f9cd0d7e08 | |||
59cd1a00e7 | |||
836130a718 | |||
a7db2d8d9c | |||
c022d49996 | |||
825827cb1e | |||
cd92b70d6b | |||
617c990994 | |||
9b959373db | |||
76c0197ab7 | |||
7d9658b062 | |||
db0c946780 | |||
c6202da36f | |||
532011138c | |||
191164cba0 | |||
5688aaa03b | |||
88a76c0a15 | |||
e1dc6d7beb | |||
730d11012d | |||
b43b636ba0 | |||
a7ca6c057e | |||
bd002c6429 | |||
453df01f26 | |||
24ae38cca6 | |||
3b386965ea | |||
5077289d0d | |||
a54b21cb50 | |||
fa80cb0002 | |||
ad5b92b650 | |||
9b1b324ab4 | |||
e444bc2a0b | |||
dd25432359 | |||
cfbbfe84ad | |||
b25d2846e9 | |||
ab64e17464 | |||
3a5db47c1b | |||
4812dcb02b | |||
b146df346d | |||
5f2713bcfb | |||
a1bf961fd2 | |||
a8451a5e30 | |||
9417c49d8e | |||
1a40f1da7a | |||
5e28f27764 | |||
43d0e10ba4 | |||
9652fdbecb | |||
110575ca40 | |||
22b4e636e2 | |||
1688a466f4 | |||
625da4cdd9 | |||
a9e0f5b7b0 | |||
7b1b1a7caa | |||
a6477541c0 | |||
b652a9f6dc | |||
73b2813512 | |||
ace28983b6 | |||
e82675320c | |||
1e88671799 | |||
1cdf9640d7 | |||
91badb53e5 | |||
477d02468d | |||
9f3a270fae | |||
244b058648 | |||
886c9b9bc0 | |||
41025ba97b | |||
4875bb6188 | |||
5f91c3d4e6 | |||
7b62915a5b | |||
05c3cb2c1d | |||
60a96d1400 | |||
dcd0df93da | |||
f15101fc6f | |||
5f03253398 | |||
8fab55e937 | |||
60a23665cb | |||
b292b275cb | |||
403028bf35 | |||
3dda5cff06 | |||
dd589e2490 | |||
c8fb83c97c | |||
854f6a9506 | |||
be0557c2b5 | |||
436422e4da | |||
fc72576aaa | |||
f69a2ba044 | |||
c45e06c694 | |||
78bdf1ca4e | |||
3c7d3c0196 | |||
441a5793cc | |||
388477686b | |||
4f654d9e12 | |||
ac12d0fc02 | |||
b8ebe4fd58 | |||
c84896391e | |||
995e28e9d8 | |||
1bfc082d46 | |||
1a35bbb127 | |||
e585c77830 | |||
5ca92ff637 | |||
f4c76553ed | |||
75baf6dd96 | |||
301b9288a4 | |||
70b9fc86d2 | |||
dde9cd323c | |||
01a6827542 | |||
b960a9e574 | |||
102fe3a8fb | |||
292df419ba | |||
7993e1d1c9 | |||
7309f92c36 | |||
7875ff293d | |||
4adac4d852 | |||
38bff65caf | |||
9ecadf1bc1 | |||
5d7ae385ec | |||
74e9780b30 | |||
9b20f41ece | |||
967309120e | |||
94be2abdbd | |||
6c31b53cd9 | |||
9bfc0b2c6f | |||
fc374ec57a | |||
7a8e60ddc2 | |||
7f43f26886 | |||
4521982837 | |||
30c43b557c | |||
10b5886dcc | |||
93b97fc3fe | |||
1f6e60fd74 | |||
5337d4838d | |||
ae5d183db0 | |||
0d85a34551 | |||
002c608d45 | |||
ec766a3e43 | |||
0ef8d04ed2 | |||
3a86916e72 | |||
b41435f407 | |||
f689d90815 | |||
966f2e7ef7 | |||
9075430210 | |||
68095ad69a | |||
8c3d1e608c | |||
6822f18cbb | |||
6e4f2e19d6 | |||
8bbd062d13 | |||
5f7ccc8c9b | |||
5b1483f646 | |||
3cd86d0e93 | |||
a893fbc358 | |||
6fd2a38fe9 | |||
b89ecb3e5a | |||
2e5590b566 | |||
733bb2ebbb | |||
64a22457e2 | |||
0c8e94d2c6 | |||
b9436821e0 | |||
a8af9120de | |||
0655a1f6b6 | |||
97dbedaa54 | |||
4028b44d07 | |||
661218c7e3 | |||
cd4a9add68 | |||
7d2915c7f9 | |||
ce56a035b5 | |||
9fa79aabc0 | |||
62fad9ca3a | |||
388f055643 | |||
71be20636a | |||
3b38d1b46e | |||
1e0c2ea633 | |||
4e7652be7a | |||
723075d2da | |||
7ba021871a | |||
d7cb819502 | |||
5ee2aa77c6 | |||
80cf5d8d5b | |||
69277400b7 | |||
8d380b4913 | |||
23d20e0753 | |||
6fc7beba57 | |||
8d49d22074 | |||
6aa97d055f | |||
e55938e23a | |||
4166fb229e | |||
2e9947277a | |||
c350ea0ced | |||
588b5c4d89 | |||
91d0877c61 | |||
8045ad56ea | |||
124b07ee44 | |||
195974ddc1 | |||
2b081b42bb | |||
321d241483 | |||
ad4ff5835e | |||
a3cda2e0ff | |||
cf2eb1fec7 | |||
7eb759d1fd | |||
a07bf86c30 | |||
64c4aca3b7 | |||
40465643b9 | |||
56fbf491bc | |||
685c071056 | |||
fdbc0e6a61 | |||
7fe8d27686 | |||
9050f4af1f | |||
9140044956 | |||
2e6b6bda53 | |||
8d8aa5a380 | |||
ae2dbd00fa | |||
6550a5d745 | |||
593b27358b | |||
a51ff0f100 | |||
a96bc802bc | |||
df8f34e726 | |||
00998ac9c8 | |||
3f243c64ca | |||
034241ba31 | |||
ed6fb57cb5 | |||
b71507387f | |||
e7d0ec1f6c | |||
b3b318fd28 | |||
d334f5cb8d | |||
7f06424980 | |||
7490aa0d26 | |||
e59e2ee2ee | |||
f25dcad10c | |||
24a9491ec0 | |||
744549defe | |||
c86fc4c3fa | |||
943396767c | |||
13fffe58a1 | |||
7fe60e19ef | |||
ba9bf17f46 | |||
373ee4b45b | |||
e5f0ba67fc | |||
096d58ae71 | |||
ed5b47cb96 | |||
e92946301f | |||
7588c9fb9f | |||
6d10bfefbd | |||
cebfbb83fa | |||
5a06d8021b | |||
a75841aa8e | |||
e76649bb49 | |||
7567c6cd71 | |||
751deeafd7 | |||
3ebefa15bf | |||
bb19687854 | |||
311eec6888 | |||
08fc32b70a | |||
555257a3fe | |||
5a03ab62d0 | |||
038be81db3 | |||
fbf34f5953 | |||
4347dd3dd9 | |||
d830ae0b15 | |||
4f198bc6f5 | |||
66615a41c4 | |||
a5ee5f816c | |||
fb52137bff | |||
7ee07f4dc7 | |||
f224069a34 | |||
6cca77f755 | |||
084289baa4 | |||
e2dbb88459 | |||
a2822c74ed | |||
4cd30c75ac | |||
f4b9480588 | |||
482589af49 | |||
bbe4a96984 | |||
60294d0c41 | |||
3035763277 | |||
51eabde320 | |||
a5305f551c | |||
1f918dece5 | |||
fc152f37ad | |||
411c7e915a | |||
5bf2415653 | |||
7d9856dae3 | |||
a308ab12fa | |||
d58336adcb | |||
bfc0b8ecd5 | |||
a9b422783f | |||
e015b4c010 | |||
f666c15a1f | |||
6770011dd7 | |||
d7b0513fb1 | |||
9932ade00e | |||
8b6eba8203 | |||
9c8ea29fc6 | |||
714ae7d67f | |||
1e4477c148 | |||
53f9232f36 | |||
1f05b47ad6 | |||
98d63d235f | |||
7925bdc6c9 | |||
4ba23562ef | |||
e22a7fecea | |||
9b99bb5f21 | |||
c39b3aaaf3 | |||
d14ac2fe85 | |||
04f3e518da | |||
5f5298bcd1 | |||
7f1497c5e9 | |||
5ac71baafe | |||
695c699893 | |||
bf96f13296 | |||
62b1ec04ab | |||
e412d2f084 | |||
8003ff5e59 | |||
becce1f580 | |||
3a6073e3a0 | |||
fc347ae166 | |||
8dcfa0c282 | |||
6808ab3ebe | |||
31097c5380 | |||
d49c57699e | |||
ed6616e4c7 | |||
953e2286b8 | |||
06c8782127 | |||
2623fa362c | |||
4a274ba440 | |||
bfef7e1ce2 | |||
558b37b1c3 | |||
d1c26f82aa | |||
1a351bc8c7 | |||
27546daad9 | |||
bb58d3fead | |||
099d638a7e | |||
5fb48e68a0 | |||
b979c2fa1f | |||
b6625baec6 | |||
98044d6faa | |||
2113846567 | |||
5c9d4edca9 | |||
4dce9340b0 | |||
bf79e8341e | |||
dadfdc2f13 | |||
585f627e1f | |||
eead303d47 | |||
14cc15c711 | |||
bf8736e17e | |||
5014ca2459 | |||
f337abe06e | |||
ff35f25211 | |||
2e4280a947 | |||
4a6b5b3d28 | |||
a28af3903d | |||
d1d5b9effb | |||
062fababf2 | |||
946afcc3c1 | |||
6e502cd1c6 | |||
0a7f897f05 | |||
b35f17124f | |||
44b44ac2c5 | |||
39e875c1ea | |||
be7317cc86 | |||
e1c33a1de6 | |||
8149551a15 | |||
ab648ef3db | |||
e721910c0c | |||
9287eced49 | |||
967b94b14f | |||
2df6f00ba5 | |||
beb4f8df44 | |||
92ac5a4166 | |||
716323e56a | |||
cd92d95f91 | |||
9c86ed623d | |||
c6ff9b07df | |||
38cc96d0f3 | |||
82518d8664 | |||
aba2a9e179 | |||
c5c4d081ea | |||
f854163626 | |||
fc31a2d466 | |||
86a0a8cd68 | |||
31c3b3b28e | |||
b95ab104e3 | |||
a54e4c22a8 | |||
7913e25be2 | |||
63cbda8890 | |||
38851bce22 | |||
74835c40ea | |||
97e99b4910 | |||
4ced4d29f5 | |||
239a90de14 | |||
c86af0496b | |||
4071ab9124 | |||
5c715dabc3 | |||
43d2d8583e | |||
cf78234e3a | |||
5f4cd09490 | |||
8534804c0c | |||
cf75e76e5c | |||
7d5e9e5111 | |||
1ceec3cb54 | |||
5f8c75816e | |||
4e37662ab3 | |||
a97a917522 | |||
88f91518c7 | |||
e89acc8ba6 | |||
5d611ea7f3 | |||
5c80f11261 | |||
f2202b44fe | |||
2da48c0adc | |||
389407e3ee | |||
14be2b8c18 | |||
df7808264f | |||
9d37503442 | |||
dd6f39c3cd | |||
8e4d70b9c6 | |||
87fb30d57b | |||
8f3371dcd5 | |||
ec8975c9e3 | |||
16edfbb9dc | |||
f70b135359 | |||
78fb69ded1 | |||
0f7eb1571e | |||
ac54b7abd4 | |||
0f3a6393fc | |||
de6f339cdf | |||
d0969bd5f3 | |||
4035a38507 | |||
944b077c65 | |||
644c6b9082 | |||
dc9f8db556 | |||
07ebdeab25 | |||
e8738cce7d | |||
d9e638a7df | |||
08b1dd31c8 | |||
b4f0a6d546 | |||
4eb28df1ab | |||
50e5e96bb1 | |||
bd3a9bc350 | |||
06923c8274 | |||
3efc2cf869 | |||
f5de0eeacf | |||
6e871b8eb1 | |||
f6359bc32c | |||
2bc052ad5a | |||
022ef6a64c | |||
3232811543 | |||
fabcd93dd7 | |||
8053183cb5 | |||
b2b7d90888 | |||
3e9d73c25d | |||
d6501ddc0e | |||
e818e3875d | |||
56cd3971dc | |||
958ca34e9c | |||
f4ef2366b6 | |||
f553960096 | |||
24ef64456b | |||
3e7da0fd18 | |||
2c0f42b363 | |||
1e4a6e2125 | |||
beeb60aab8 | |||
5b942a5b31 | |||
3e4a6cd702 | |||
0e369953cb | |||
911e356930 | |||
554b39a49a | |||
ea16f35432 | |||
81d94687be | |||
4ba4bc18cf | |||
c117ff2d50 | |||
735c224444 | |||
81adfd335e | |||
7ebe589b46 | |||
9c1b19a9e4 | |||
02b886465e | |||
2d89ceb745 | |||
ae8c3252df | |||
7914f71938 | |||
097ba9fec2 | |||
d09d9f0c09 | |||
29c98b0230 | |||
2b1db1f1b3 | |||
fa80b1dba0 | |||
b45db584df | |||
f52ea635e5 | |||
9b56afd569 | |||
6fb10e92e4 | |||
fcf4d91564 | |||
38319871e6 | |||
2d0b092ea4 | |||
060b8c61bb | |||
db2accc2f8 | |||
798fda4c8a | |||
2578e48134 | |||
ac20216a7a | |||
beb5a456eb | |||
41faadd572 | |||
a15016d7d9 | |||
47dd84c441 | |||
c7b360e982 | |||
09041b018f | |||
3a4cf47def | |||
56e4f68a83 | |||
484e7fccbd | |||
791d65e95a | |||
073d7fb6a7 | |||
a6668397e1 | |||
983d626d87 | |||
a4e73a05c6 | |||
30e304ffb5 | |||
430b34c7a2 | |||
b222416fd8 | |||
f8110cd046 | |||
6bc60f4aa9 | |||
b48a445e9f | |||
0b383498eb | |||
2e3b4223a5 | |||
60bb63ab2b | |||
0aa7a8a8f6 | |||
72ed7c1dde | |||
a29c4af2ee | |||
8d8f234138 | |||
491cc05a95 | |||
b2c6077f4d | |||
fb290dc12d | |||
c088fabe6c | |||
a791ff4e46 | |||
e56f134a7c | |||
38c4c21030 | |||
3e33b7f1cb | |||
e381671645 | |||
ce991e9ac3 | |||
3d5415433e | |||
0bfefdf15d | |||
506464e637 | |||
4554cb969f | |||
65cf841015 | |||
0f0ce934dc | |||
bffbe764e5 | |||
c00595e212 | |||
b20f5b359b | |||
558eef78b4 | |||
3f46ce3b7b | |||
69e8edef19 | |||
d3369245c4 | |||
55e9f2095e | |||
7118613ef7 | |||
105d65d5ce | |||
f11bf08ba1 | |||
66598e39fe | |||
4146b50384 | |||
f62d642572 | |||
18f1fd42b0 | |||
07d198aebe | |||
0370a9f277 | |||
22d3ac027a | |||
795f7fff7f | |||
ab6d40a072 | |||
d26923e167 | |||
05aff236c1 | |||
a9ae070256 | |||
63fe6e01d1 | |||
bbafdd1c1d | |||
fe9df2df8c | |||
b0b8623dce | |||
a01e59951a | |||
d5c202abbd | |||
81299ff613 | |||
25bac68309 | |||
a05324bdad | |||
74c4ab2f04 | |||
ab05cb0045 | |||
709eb8cf45 | |||
643cdeceee | |||
2add34c702 | |||
877397a46b | |||
afbc67795a | |||
da5dc20b3a | |||
ed5e5e5077 | |||
9a6ddce8df | |||
db8478d0a4 | |||
20030f47af | |||
f7cf1965cb | |||
aea067f789 | |||
e2c20c1e55 | |||
1209281787 | |||
742f67c85b | |||
e22fcbe3c0 | |||
d3998dfadb | |||
ff54f194ad | |||
f6b967421e | |||
59559199d5 | |||
98784c811e | |||
26e5f3d3a8 | |||
fe1f99abdf | |||
7540a26fb9 | |||
10d0ff2c06 | |||
90de2e4ad9 | |||
d3c2ffd422 | |||
53c98328c1 | |||
3d26a0ce0d | |||
73fb18c193 | |||
1ba595daa0 | |||
47cab4d63e | |||
4a25675007 | |||
c437b40d58 | |||
b7a18bf0d9 | |||
bc3d5a46b7 | |||
990bb827be | |||
3ecb84bd94 | |||
2565094897 | |||
855b838e91 | |||
042d8c17da | |||
a39fb14726 | |||
888c7a6b0d | |||
2f69917a82 | |||
efa35ba71c | |||
6c90da793e | |||
4024490249 | |||
209d9b27b4 | |||
96e0be8942 | |||
87bce0bce1 | |||
f23059d7ec | |||
cc08fe32c6 | |||
07e93f95a1 | |||
a10e8afa5c | |||
b39244f827 | |||
af4d986a13 | |||
8790f26e68 | |||
f01412a996 | |||
20d0ab229f | |||
2dec4f1ffc | |||
a7d1dbf0e9 | |||
eace25d9d2 | |||
fdf2838669 | |||
74fb8258b6 | |||
cc299fe4da | |||
cd31c8db5c | |||
6d7116d521 | |||
f93916372e | |||
80c9e17fd4 | |||
003a92f466 | |||
d57aa04bfb | |||
9c2d279806 | |||
3109e35bb4 | |||
ea51b1e95e | |||
3799a1e99a | |||
ec8714ad3a | |||
bc54a5379c | |||
a7587057bf | |||
e9ba2fd8bb | |||
8203fa588f | |||
2e5250ec98 | |||
1f910745f1 | |||
f56da17741 | |||
a0d86977b0 | |||
82838e1d26 | |||
bb8bc7672a | |||
6e3e159109 | |||
b561e35330 | |||
b684c4f60d | |||
58281f0a10 | |||
87574a7613 | |||
52ebb5a1a7 |
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
14
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
## Description
|
||||
Include changes, new features and etc:
|
||||
|
||||
## Describe your tests
|
||||
How did you test your change?
|
||||
|
||||
Python version:
|
||||
|
||||
OS:
|
||||
|
||||
## Checklist:
|
||||
- [ ] I added/edited example on new feature/change (if exists)
|
||||
- [ ] My changes won't break backward compatibility
|
||||
- [ ] I made changes both for sync and async
|
35
.github/workflows/setup_python.yml
vendored
Normal file
35
.github/workflows/setup_python.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Setup
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
#workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
all-setups:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '3.6','3.7','3.8','3.9', '3.10', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9']
|
||||
name: ${{ matrix.python-version }} and tests
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
- run: |
|
||||
pip3 install -r requirements.txt
|
||||
python setup.py install
|
||||
cd tests && py.test
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -25,6 +25,7 @@ var/
|
||||
|
||||
.idea/
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
@ -62,3 +63,8 @@ testMain.py
|
||||
|
||||
#VS Code
|
||||
.vscode/
|
||||
.DS_Store
|
||||
*.code-workspace
|
||||
|
||||
# documentation
|
||||
_build/
|
||||
|
19
.readthedocs.yml
Normal file
19
.readthedocs.yml
Normal file
@ -0,0 +1,19 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: doc_req.txt
|
@ -1,10 +1,9 @@
|
||||
language: python
|
||||
python:
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "pypy3"
|
||||
install: "pip install -r requirements.txt"
|
||||
script:
|
||||
|
547
README.md
547
README.md
@ -1,13 +1,23 @@
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.
|
||||
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://pypi.python.org/pypi/pyTelegramBotAPI)
|
||||
[](https://pytba.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://travis-ci.org/eternnoir/pyTelegramBotAPI)
|
||||
[](https://pypi.org/project/pyTelegramBotAPI/)
|
||||
[](https://pypi.python.org/pypi/pytelegrambotapi)
|
||||
|
||||
* [Getting started.](#getting-started)
|
||||
# <p align="center">pyTelegramBotAPI
|
||||
|
||||
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
|
||||
<p align="center">Both synchronous and asynchronous.</p>
|
||||
|
||||
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#june-20-2022">6.1</a>!
|
||||
|
||||
<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
|
||||
|
||||
## Contents
|
||||
|
||||
* [Getting started](#getting-started)
|
||||
* [Writing your first bot](#writing-your-first-bot)
|
||||
* [Prerequisites](#prerequisites)
|
||||
* [A simple echo bot](#a-simple-echo-bot)
|
||||
@ -16,34 +26,53 @@
|
||||
* [Methods](#methods)
|
||||
* [General use of the API](#general-use-of-the-api)
|
||||
* [Message handlers](#message-handlers)
|
||||
* [Edited Message handler](#edited-message-handler)
|
||||
* [Channel Post handler](#channel-post-handler)
|
||||
* [Edited Channel Post handler](#edited-channel-post-handler)
|
||||
* [Callback Query handlers](#callback-query-handler)
|
||||
* [Middleware handlers](#middleware-handler)
|
||||
* [Shipping Query Handler](#shipping-query-handler)
|
||||
* [Pre Checkout Query Handler](#pre-checkout-query-handler)
|
||||
* [Poll Handler](#poll-handler)
|
||||
* [Poll Answer Handler](#poll-answer-handler)
|
||||
* [My Chat Member Handler](#my-chat-member-handler)
|
||||
* [Chat Member Handler](#chat-member-handler)
|
||||
* [Chat Join request handler](#chat-join-request-handler)
|
||||
* [Inline Mode](#inline-mode)
|
||||
* [Inline handler](#inline-handler)
|
||||
* [Chosen Inline handler](#chosen-inline-handler)
|
||||
* [Answer Inline Query](#answer-inline-query)
|
||||
* [Additional API features](#additional-api-features)
|
||||
* [Middleware handlers](#middleware-handlers)
|
||||
* [Custom filters](#custom-filters)
|
||||
* [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)
|
||||
* [Using local Bot API Server](#using-local-bot-api-sever)
|
||||
* [Asynchronous TeleBot](#asynchronous-telebot)
|
||||
* [Sending large text messages](#sending-large-text-messages)
|
||||
* [Controlling the amount of Threads used by TeleBot](#controlling-the-amount-of-threads-used-by-telebot)
|
||||
* [The listener mechanism](#the-listener-mechanism)
|
||||
* [Using web hooks](#using-web-hooks)
|
||||
* [Logging](#logging)
|
||||
* [Proxy](#proxy)
|
||||
* [Testing](#testing)
|
||||
* [API conformance](#api-conformance)
|
||||
* [Change log](#change-log)
|
||||
* [AsyncTeleBot](#asynctelebot)
|
||||
* [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)
|
||||
* [How can I handle reocurring ConnectionResetErrors?](#how-can-i-handle-reocurring-connectionreseterrors)
|
||||
* [The Telegram Chat Group](#the-telegram-chat-group)
|
||||
* [Telegram Channel](#telegram-channel)
|
||||
* [More examples](#more-examples)
|
||||
* [Bots using this API](#bots-using-this-api)
|
||||
* [Code Template](#code-template)
|
||||
* [Bots using this library](#bots-using-this-library)
|
||||
|
||||
## Getting started.
|
||||
## Getting started
|
||||
|
||||
This API is tested with Python Python 3.6-3.9 and Pypy 3.
|
||||
This API is tested with Python 3.6-3.10 and Pypy 3.
|
||||
There are two ways to install the library:
|
||||
|
||||
* Installation using pip (a Python package manager)*:
|
||||
* Installation using pip (a Python package manager):
|
||||
|
||||
```
|
||||
$ pip install pyTelegramBotAPI
|
||||
@ -55,10 +84,17 @@ $ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
$ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
```
|
||||
or:
|
||||
```
|
||||
$ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
```
|
||||
|
||||
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`*
|
||||
*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
|
||||
|
||||
@ -102,13 +138,13 @@ This one echoes all incoming text messages back to the sender. It uses a lambda
|
||||
|
||||
We now have a basic bot which replies a static message to "/start" and "/help" commands and which echoes the rest of the sent messages. To start the bot, add the following to our source file:
|
||||
```python
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
```
|
||||
Alright, that's it! Our source file now looks like this:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot("TOKEN")
|
||||
bot = telebot.TeleBot("YOUR_BOT_TOKEN")
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
def send_welcome(message):
|
||||
@ -118,7 +154,7 @@ def send_welcome(message):
|
||||
def echo_all(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_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.
|
||||
|
||||
@ -145,7 +181,7 @@ Outlined below are some general use cases of the API.
|
||||
|
||||
#### Message handlers
|
||||
A message handler is a function that is decorated with the `message_handler` decorator of a TeleBot instance. Message handlers consist of one or multiple filters.
|
||||
Each filter much return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
|
||||
Each filter must return True for a certain message in order for a message handler to become eligible to handle that message. A message handler is declared in the following way (provided `bot` is an instance of TeleBot):
|
||||
```python
|
||||
@bot.message_handler(filters)
|
||||
def function_name(message):
|
||||
@ -161,8 +197,9 @@ TeleBot supports the following filters:
|
||||
|content_types|list of strings (default `['text']`)|`True` if message.content_type is in the list of strings.|
|
||||
|regexp|a regular expression as a string|`True` if `re.search(regexp_arg)` returns `True` and `message.content_type == 'text'` (See [Python Regular Expressions](https://docs.python.org/2/library/re.html))|
|
||||
|commands|list of strings|`True` if `message.content_type == 'text'` and `message.text` starts with a command that is in the list of strings.|
|
||||
|func|a function (lambda or function reference)|`True` if the lambda or function reference returns `True`
|
||||
|
||||
|chat_types|list of chat types|`True` if `message.chat.type` in your filter|
|
||||
|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
|
||||
@ -206,34 +243,108 @@ def send_something(message):
|
||||
```
|
||||
**Important: all handlers are tested in the order in which they were declared**
|
||||
|
||||
#### Edited Message handlers
|
||||
#### Edited Message handler
|
||||
Handle edited messages
|
||||
`@bot.edited_message_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
@bot.edited_message_handler(filters)
|
||||
#### Channel Post handler
|
||||
Handle channel post messages
|
||||
`@bot.channel_post_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
#### channel_post_handler
|
||||
|
||||
@bot.channel_post_handler(filters)
|
||||
|
||||
#### edited_channel_post_handler
|
||||
|
||||
@bot.edited_channel_post_handler(filters)
|
||||
#### Edited Channel Post handler
|
||||
Handle edited channel post messages
|
||||
`@bot.edited_channel_post_handler(filters) # <- passes a Message type object to your function`
|
||||
|
||||
#### 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.
|
||||
|
||||
Handle callback queries
|
||||
```python
|
||||
@bot.callback_query_handler(func=lambda call: True)
|
||||
def test_callback(call):
|
||||
def test_callback(call): # <- passes a CallbackQuery type object to your function
|
||||
logger.info(call)
|
||||
```
|
||||
#### Middleware Handler
|
||||
|
||||
#### Shipping Query Handler
|
||||
Handle shipping queries
|
||||
`@bot.shipping_query_handeler() # <- passes a ShippingQuery type object to your function`
|
||||
|
||||
#### Pre Checkout Query Handler
|
||||
Handle pre checkoupt queries
|
||||
`@bot.pre_checkout_query_handler() # <- passes a PreCheckoutQuery type object to your function`
|
||||
|
||||
#### Poll Handler
|
||||
Handle poll updates
|
||||
`@bot.poll_handler() # <- passes a Poll type object to your function`
|
||||
|
||||
#### Poll Answer Handler
|
||||
Handle poll answers
|
||||
`@bot.poll_answer_handler() # <- passes a PollAnswer type object to your function`
|
||||
|
||||
#### My Chat Member Handler
|
||||
Handle updates of a the bot's member status in a chat
|
||||
`@bot.my_chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
|
||||
|
||||
#### Chat Member Handler
|
||||
Handle updates of a chat member's status in a chat
|
||||
`@bot.chat_member_handler() # <- passes a ChatMemberUpdated type object to your function`
|
||||
*Note: "chat_member" updates are not requested by default. If you want to allow all update types, set `allowed_updates` in `bot.polling()` / `bot.infinity_polling()` to `util.update_types`*
|
||||
|
||||
#### Chat Join Request Handler
|
||||
Handle chat join requests using:
|
||||
`@bot.chat_join_request_handler() # <- passes ChatInviteLink type object to your function`
|
||||
|
||||
### 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)
|
||||
|
||||
```
|
||||
|
||||
### Additional API features
|
||||
|
||||
#### Middleware Handlers
|
||||
|
||||
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.
|
||||
executed. Middleware processing is disabled by default, enable it by setting `apihelper.ENABLE_MIDDLEWARE = True`.
|
||||
|
||||
```python
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
|
||||
@bot.middleware_handler(update_types=['message'])
|
||||
def modify_message(bot_instance, message):
|
||||
# modifying the message before it reaches any other handler
|
||||
@ -246,6 +357,63 @@ def start(message):
|
||||
```
|
||||
There are other examples using middleware handler in the [examples/middleware](examples/middleware) directory.
|
||||
|
||||
#### Class-based middlewares
|
||||
There are class-based middlewares.
|
||||
Basic class-based middleware looks like this:
|
||||
```python
|
||||
class Middleware(BaseMiddleware):
|
||||
def __init__(self):
|
||||
self.update_types = ['message']
|
||||
def pre_process(self, message, data):
|
||||
data['foo'] = 'Hello' # just for example
|
||||
# we edited the data. now, this data is passed to handler.
|
||||
# return SkipHandler() -> this will skip handler
|
||||
# return CancelUpdate() -> this will cancel update
|
||||
def post_process(self, message, data, exception=None):
|
||||
print(data['foo'])
|
||||
if exception: # check for exception
|
||||
print(exception)
|
||||
```
|
||||
Class-based middleware should have to functions: post and pre process.
|
||||
So, as you can see, class-based middlewares work before and after handler execution.
|
||||
For more, check out in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/middleware/class_based)
|
||||
|
||||
#### Custom filters
|
||||
Also, you can use built-in custom filters. Or, you can create your own filter.
|
||||
|
||||
[Example of custom filter](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/general_custom_filters.py)
|
||||
|
||||
Also, we have examples on them. Check this links:
|
||||
|
||||
You can check some built-in filters in source [code](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/telebot/custom_filters.py)
|
||||
|
||||
Example of [filtering by id](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/id_filter_example.py)
|
||||
|
||||
Example of [filtering by text](https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/custom_filters/text_filter_example.py)
|
||||
|
||||
If you want to add some built-in filter, you are welcome to add it in custom_filters.py file.
|
||||
|
||||
Here is example of creating filter-class:
|
||||
|
||||
```python
|
||||
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
|
||||
# Class will check whether the user is admin or creator in group or not
|
||||
key='is_admin'
|
||||
@staticmethod
|
||||
def check(message: telebot.types.Message):
|
||||
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
|
||||
|
||||
# To register filter, you need to use method add_custom_filter.
|
||||
bot.add_custom_filter(IsAdmin())
|
||||
|
||||
# Now, you can use it in handler.
|
||||
@bot.message_handler(is_admin=True)
|
||||
def admin_of_group(message):
|
||||
bot.send_message(message.chat.id, 'You are admin of this group!')
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### TeleBot
|
||||
```python
|
||||
import telebot
|
||||
@ -254,11 +422,10 @@ 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
|
||||
# - interval: int (default 0) - The interval between polling requests
|
||||
# - timeout: integer (default 20) - Timeout in seconds for long polling.
|
||||
tb.polling(none_stop=False, interval=0, timeout=20)
|
||||
# - allowed_updates: List of Strings (default None) - List of update types to request
|
||||
tb.infinity_polling(interval=0, timeout=20)
|
||||
|
||||
# getMe
|
||||
user = tb.get_me()
|
||||
@ -270,11 +437,15 @@ tb.remove_webhook()
|
||||
|
||||
# getUpdates
|
||||
updates = tb.get_updates()
|
||||
# or
|
||||
updates = tb.get_updates(1234,100,20) #get_Updates(offset, limit, timeout):
|
||||
|
||||
# sendMessage
|
||||
tb.send_message(chat_id, text)
|
||||
|
||||
# editMessageText
|
||||
tb.edit_message_text(new_text, chat_id, message_id)
|
||||
|
||||
# forwardMessage
|
||||
tb.forward_message(to_chat_id, from_chat_id, message_id)
|
||||
|
||||
@ -388,49 +559,8 @@ 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:
|
||||
### Working with entities
|
||||
This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
|
||||
Attributes:
|
||||
* `type`
|
||||
@ -448,26 +578,38 @@ Refer [Bot Api](https://core.telegram.org/bots/api#messageentity) for extra deta
|
||||
|
||||
## 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.
|
||||
### Using local Bot API Sever
|
||||
Since version 5.0 of the Bot API, you have the possibility to run your own [Local Bot API Server](https://core.telegram.org/bots/api#using-a-local-bot-api-server).
|
||||
pyTelegramBotAPI also supports this feature.
|
||||
```python
|
||||
from telebot import apihelper
|
||||
|
||||
apihelper.API_URL = "http://localhost:4200/bot{0}/{1}"
|
||||
```
|
||||
**Important: Like described [here](https://core.telegram.org/bots/api#logout), you have to log out your bot from the Telegram server before switching to your local API server. in pyTelegramBotAPI use `bot.log_out()`**
|
||||
|
||||
*Note: 4200 is an example port*
|
||||
|
||||
### Asynchronous TeleBot
|
||||
New: There is an asynchronous implementation of telebot.
|
||||
To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
|
||||
```python
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
```
|
||||
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
|
||||
Now, every function that calls the Telegram API is executed in a separate asynchronous task.
|
||||
Using AsyncTeleBot allows you to do the following:
|
||||
```python
|
||||
import telebot
|
||||
|
||||
tb = telebot.AsyncTeleBot("TOKEN")
|
||||
task = tb.get_me() # Execute an API call
|
||||
# Do some other operations...
|
||||
a = 0
|
||||
for a in range(100):
|
||||
a += 10
|
||||
|
||||
result = task.wait() # Get the result of the execution
|
||||
@tb.message_handler(commands=['start'])
|
||||
async def start_message(message):
|
||||
await bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
```
|
||||
*Note: if you execute send_xyz functions after eachother without calling wait(), the order in which messages are delivered might be wrong.*
|
||||
|
||||
See more in [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot)
|
||||
|
||||
### 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:
|
||||
@ -478,6 +620,19 @@ 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)
|
||||
```
|
||||
|
||||
Or you can use the new `smart_split` function to get more meaningful substrings:
|
||||
```python
|
||||
from telebot import util
|
||||
large_text = open("large_text.txt", "rb").read()
|
||||
# Splits one string into multiple strings, with a maximum amount of `chars_per_string` (max. 4096)
|
||||
# Splits by last '\n', '. ' or ' ' in exactly this priority.
|
||||
# smart_split returns a list with the splitted text.
|
||||
splitted_text = util.smart_split(large_text, chars_per_string=3000)
|
||||
for text in splitted_text:
|
||||
tb.send_message(chat_id, text)
|
||||
```
|
||||
@ -499,7 +654,7 @@ def handle_messages(messages):
|
||||
bot.reply_to(message, 'Hi')
|
||||
|
||||
bot.set_update_listener(handle_messages)
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
```
|
||||
|
||||
### Using web hooks
|
||||
@ -508,7 +663,6 @@ When using webhooks telegram sends one Update per call, for processing it you sh
|
||||
There are some examples using webhooks in the [examples/webhook_examples](examples/webhook_examples) directory.
|
||||
|
||||
### Logging
|
||||
|
||||
You can use the Telebot module logger to log debug info about Telebot. Use `telebot.logger` to get the logger of the TeleBot module.
|
||||
It is possible to add custom logging Handlers to the logger. Refer to the [Python logging module page](https://docs.python.org/2/library/logging.html) for more info.
|
||||
|
||||
@ -520,13 +674,14 @@ telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
```
|
||||
|
||||
### Proxy
|
||||
For sync:
|
||||
|
||||
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'}
|
||||
apihelper.proxy = {'http':'http://127.0.0.1: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`.
|
||||
@ -535,57 +690,121 @@ If you want to use socket5 proxy you need install dependency `pip install reques
|
||||
apihelper.proxy = {'https':'socks5://userproxy:password@proxy_address:port'}
|
||||
```
|
||||
|
||||
For async:
|
||||
```python
|
||||
from telebot import asyncio_helper
|
||||
|
||||
asyncio_helper.proxy = 'http://127.0.0.1:3128' #url
|
||||
```
|
||||
|
||||
|
||||
### Testing
|
||||
You can disable or change the interaction with real Telegram server by using
|
||||
```python
|
||||
apihelper.CUSTOM_REQUEST_SENDER = your_handler
|
||||
```
|
||||
parameter. You can pass there your own function that will be called instead of _requests.request_.
|
||||
|
||||
For example:
|
||||
```python
|
||||
def custom_sender(method, url, **kwargs):
|
||||
print("custom_sender. method: {}, url: {}, params: {}".format(method, url, kwargs.get("params")))
|
||||
result = util.CustomRequestResponse('{"ok":true,"result":{"message_id": 1, "date": 1, "chat": {"id": 1, "type": "private"}}}')
|
||||
return result
|
||||
```
|
||||
|
||||
Then you can use API and proceed requests in your handler code.
|
||||
```python
|
||||
apihelper.CUSTOM_REQUEST_SENDER = custom_sender
|
||||
tb = TeleBot("test")
|
||||
res = tb.send_message(123, "Test")
|
||||
```
|
||||
|
||||
Result will be:
|
||||
|
||||
`custom_sender. method: post, url: https://api.telegram.org/botololo/sendMessage, params: {'chat_id': '123', 'text': 'Test'}`
|
||||
|
||||
|
||||
|
||||
## API conformance
|
||||
|
||||
_Checking is in progress..._
|
||||
|
||||
✅ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) _- To be checked..._
|
||||
|
||||
* ✔ [Bot API 6.1](https://core.telegram.org/bots/api#june-20-2022)
|
||||
* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022)
|
||||
* ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022)
|
||||
* ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021)
|
||||
* ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021)
|
||||
* ✔ [Bot API 5.4](https://core.telegram.org/bots/api#november-5-2021)
|
||||
* ➕ [Bot API 5.3](https://core.telegram.org/bots/api#june-25-2021) - ChatMember* classes are full copies of ChatMember
|
||||
* ✔ [Bot API 5.2](https://core.telegram.org/bots/api#april-26-2021)
|
||||
* ✔ [Bot API 5.1](https://core.telegram.org/bots/api#march-9-2021)
|
||||
* ✔ [Bot API 5.0](https://core.telegram.org/bots/api-changelog#november-4-2020)
|
||||
* ✔ [Bot API 4.9](https://core.telegram.org/bots/api-changelog#june-4-2020)
|
||||
* ✔ [Bot API 4.8](https://core.telegram.org/bots/api-changelog#april-24-2020)
|
||||
* ✔ [Bot API 4.7](https://core.telegram.org/bots/api-changelog#march-30-2020)
|
||||
* ✔ [Bot API 4.6](https://core.telegram.org/bots/api-changelog#january-23-2020)
|
||||
* ➕ [Bot API 4.5](https://core.telegram.org/bots/api-changelog#december-31-2019) - No nested MessageEntities and Markdown2 support
|
||||
* ✔ [Bot API 4.4](https://core.telegram.org/bots/api-changelog#july-29-2019)
|
||||
* ✔ [Bot API 4.3](https://core.telegram.org/bots/api-changelog#may-31-2019)
|
||||
* ✔ [Bot API 4.2](https://core.telegram.org/bots/api-changelog#april-14-2019)
|
||||
* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support.
|
||||
* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support.
|
||||
* ✔ [Bot API 3.6](https://core.telegram.org/bots/api-changelog#february-13-2018)
|
||||
* ✔ [Bot API 3.5](https://core.telegram.org/bots/api-changelog#november-17-2017)
|
||||
* ✔ [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)
|
||||
* ➕ [Bot API 4.1](https://core.telegram.org/bots/api-changelog#august-27-2018) - No Passport support
|
||||
* ➕ [Bot API 4.0](https://core.telegram.org/bots/api-changelog#july-26-2018) - No Passport support
|
||||
|
||||
|
||||
## Change log
|
||||
## AsyncTeleBot
|
||||
### Asynchronous version of telebot
|
||||
We have a fully asynchronous version of TeleBot.
|
||||
This class is not controlled by threads. Asyncio tasks are created to execute all the stuff.
|
||||
|
||||
27.04.2020 - Poll and Dice are up to date.
|
||||
Python2 conformance is not checked any more due to EOL.
|
||||
### EchoBot
|
||||
Echo Bot example on AsyncTeleBot:
|
||||
|
||||
```python
|
||||
# This is a simple echo bot using the decorator mechanism.
|
||||
# It echoes any incoming text messages.
|
||||
|
||||
11.04.2020 - Refactoring. new_chat_member is out of support. Bugfix in html_text. Started Bot API conformance checking.
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
import asyncio
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
06.06.2019 - Added polls support (Poll). Added functions send_poll, stop_poll
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
async def send_welcome(message):
|
||||
await bot.reply_to(message, """\
|
||||
Hi there, I am EchoBot.
|
||||
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
|
||||
""")
|
||||
|
||||
|
||||
# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
async def echo_message(message):
|
||||
await bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
asyncio.run(bot.polling())
|
||||
```
|
||||
As you can see here, keywords are await and async.
|
||||
|
||||
### Why should I use async?
|
||||
Asynchronous tasks depend on processor performance. Many asynchronous tasks can run parallelly, while thread tasks will block each other.
|
||||
|
||||
### Differences in AsyncTeleBot
|
||||
AsyncTeleBot is asynchronous. It uses aiohttp instead of requests module.
|
||||
|
||||
### Examples
|
||||
See more examples in our [examples](https://github.com/eternnoir/pyTelegramBotAPI/tree/master/examples/asynchronous_telebot) folder
|
||||
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
### Bot 2.0
|
||||
|
||||
April 9,2016 Telegram release new bot 2.0 API, which has a drastic revision especially for the change of method's interface.If you want to update to the latest version, please make sure you've switched bot's code to bot 2.0 method interface.
|
||||
|
||||
[More information about pyTelegramBotAPI support bot2.0](https://github.com/eternnoir/pyTelegramBotAPI/issues/130)
|
||||
|
||||
### How can I distinguish a User and a GroupChat in message.chat?
|
||||
Telegram Bot API support new type Chat for message.chat.
|
||||
|
||||
- Check the ```type``` attribute in ```Chat``` object:
|
||||
-
|
||||
```python
|
||||
if message.chat.type == "private":
|
||||
# private chat message
|
||||
# private chat message
|
||||
|
||||
if message.chat.type == "group":
|
||||
# group chat message
|
||||
@ -598,77 +817,87 @@ if message.chat.type == "channel":
|
||||
|
||||
```
|
||||
|
||||
### How can I handle reocurring ConnectionResetErrors?
|
||||
|
||||
Bot instances that were idle for a long time might be rejected by the server when sending a message due to a timeout of the last used session. Add `apihelper.SESSION_TIME_TO_LIVE = 5 * 60` to your initialisation to force recreation after 5 minutes without any activity.
|
||||
|
||||
## The Telegram Chat Group
|
||||
|
||||
Get help. Discuss. Chat.
|
||||
|
||||
* Join the [pyTelegramBotAPI Telegram Chat Group](https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A)
|
||||
* We now have a Telegram Channel as well! Keep yourself up to date with API changes, and [join it](https://telegram.me/pytelegrambotapi).
|
||||
|
||||
## Telegram Channel
|
||||
|
||||
Join the [News channel](https://t.me/pyTelegramBotAPI). Here we will post releases and updates.
|
||||
|
||||
## 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
|
||||
## Code Template
|
||||
Template is a ready folder that contains architecture of basic project.
|
||||
Here are some examples of template:
|
||||
|
||||
* [AsyncTeleBot template](https://github.com/coder2020official/asynctelebot_template)
|
||||
* [TeleBot template](https://github.com/coder2020official/telebot_template)
|
||||
|
||||
|
||||
## Bots using this library
|
||||
* [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`.
|
||||
* [Telegram LMGTFY_bot](https://github.com/GabrielRF/telegram-lmgtfy_bot) by *GabrielRF* - Let me Google that for you.
|
||||
* [Telegram Proxy Bot](https://github.com/mrgigabyte/proxybot) by *mrgigabyte*
|
||||
* [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*
|
||||
* [ColorCodeBot](https://t.me/colorcodebot) ([source](https://github.com/andydecleyre/colorcodebot)) - Share code snippets as beautifully syntax-highlighted HTML and/or images.
|
||||
* [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
|
||||
* [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.
|
||||
* [JoinGroup Silencer Bot](https://t.me/joingroup_silencer_bot) ([source](https://github.com/zeph1997/Telegram-Group-Silencer-Bot)) by [@zeph1997](https://github.com/zeph1997) - A Telegram Bot to remove "join group" and "removed from group" notifications.
|
||||
* [AdviceBook](https://t.me/adviceokbot) by [@barbax7](https://github.com/barbax7) - A Telegram Bot that allows you to receive random reading tips when you don't know which book to read.
|
||||
* [Blue_CC_Bot](https://t.me/Blue_CC_Bot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Checks Your Given Credit Cards And Says Which Is A Real,Card And Which Is Fake.
|
||||
* [RandomInfoBot](https://t.me/RandomInfoBot) by [@Akash](https://github.com/BLUE-DEVIL1134) - A Telegram Bot Which Generates Random Information Of Humans Scraped From Over 13 Websites.
|
||||
* [TasksListsBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/TasksListsBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - A (tasks) lists manager bot for Telegram.
|
||||
* [MyElizaPsychologistBot](https://t.me/TasksListsBot) ([source](https://github.com/Pablo-Davila/MyElizaPsychologistBot)) by [@Pablo-Davila](https://github.com/Pablo-Davila) - An implementation of the famous Eliza psychologist chatbot.
|
||||
* [Evdembot](https://t.me/Evdembot) by Adem Kavak. A bot that informs you about everything you want.
|
||||
* [Frcstbot](https://t.me/frcstbot) ([source](https://github.com/Mrsqd/frcstbot_public)) by [Mrsqd](https://github.com/Mrsqd). A Telegram bot that will always be happy to show you the weather forecast.
|
||||
* [Bot Hour](https://t.me/roadtocode_bot) a little bot that say the time in different countries by [@diegop384](https://github.com/diegop384) [repo](https://github.com/diegop384/telegrambothour)
|
||||
* [moodforfood_bot](https://t.me/moodforfood_bot) This bot will provide you with a list of food place(s) near your current Telegram location, which you are prompted to share. The API for all this info is from https://foursquare.com/. by [@sophiamarani](https://github.com/sophiamarani)
|
||||
* [Donation with Amazon](https://t.me/donamazonbot) by [@barbax7](https://github.com/barbax7) This bot donates amazon advertising commissions to the non-profit organization chosen by the user.
|
||||
* [MineGramBot](https://github.com/ModischFabrications/MineGramBot) by [ModischFabrications](https://github.com/ModischFabrications). This bot can start, stop and monitor a minecraft server.
|
||||
* [Tabletop DiceBot](https://github.com/dexpiper/tabletopdicebot) by [dexpiper](https://github.com/dexpiper). This bot can roll multiple dices for RPG-like games, add positive and negative modifiers and show short descriptions to the rolls.
|
||||
* [BarnameKon](https://t.me/BarnameKonBot) by [Anvaari](https://github.com/anvaari). This Bot make "Add to google calendar" link for your events. It give information about event and return link. It work for Jalali calendar and in Tehran Time. [Source code](https://github.com/anvaari/BarnameKon)
|
||||
* [Translator bot](https://github.com/AREEG94FAHAD/translate_text_bot) by Areeg Fahad. This bot can be used to translate texts.
|
||||
* [Digital Cryptocurrency bot](https://github.com/AREEG94FAHAD/currencies_bot) by Areeg Fahad. With this bot, you can now monitor the prices of more than 12 digital Cryptocurrency.
|
||||
* [Anti-Tracking Bot](https://t.me/AntiTrackingBot) by Leon Heess [(source)](https://github.com/leonheess/AntiTrackingBot). Send any link, and the bot tries its best to remove all tracking from the link you sent.
|
||||
* [Developer Bot](https://t.me/IndDeveloper_bot) by [Vishal Singh](https://github.com/vishal2376) [(source code)](https://github.com/vishal2376/telegram-bot) This telegram bot can do tasks like GitHub search & clone,provide c++ learning resources ,Stackoverflow search, Codeforces(profile visualizer,random problems)
|
||||
* [oneIPO bot](https://github.com/aaditya2200/IPO-proj) by [Aadithya](https://github.com/aaditya2200) & [Amol Soans](https://github.com/AmolDerickSoans) This Telegram bot provides live updates , data and documents on current and upcoming IPOs(Initial Public Offerings)
|
||||
* [CoronaGraphsBot](https://t.me/CovidGraph_bot) ([source](https://github.com/TrevorWinstral/CoronaGraphsBot)) by *TrevorWinstral* - Gets live COVID Country data, plots it, and briefs the user
|
||||
* [ETHLectureBot](https://t.me/ETHLectureBot) ([source](https://github.com/TrevorWinstral/ETHLectureBot)) by *TrevorWinstral* - Notifies ETH students when their lectures have been uploaded
|
||||
* [Vlun Finder Bot](https://github.com/resinprotein2333/Vlun-Finder-bot) by [Resinprotein2333](https://github.com/resinprotein2333). This bot can help you to find The information of CVE vulnerabilities.
|
||||
* [ETHGasFeeTrackerBot](https://t.me/ETHGasFeeTrackerBot) ([Source](https://github.com/DevAdvik/ETHGasFeeTrackerBot]) by *DevAdvik* - Get Live Ethereum Gas Fees in GWEI
|
||||
* [Google Sheet Bot](https://github.com/JoachimStanislaus/Tele_Sheet_bot) by [JoachimStanislaus](https://github.com/JoachimStanislaus). This bot can help you to track your expenses by uploading your bot entries to your google sheet.
|
||||
* [GrandQuiz Bot](https://github.com/Carlosma7/TFM-GrandQuiz) by [Carlosma7](https://github.com/Carlosma7). This bot is a trivia game that allows you to play with people from different ages. This project addresses the use of a system through chatbots to carry out a social and intergenerational game as an alternative to traditional game development.
|
||||
* [Diccionario de la RAE](https://t.me/dleraebot) ([source](https://github.com/studentenherz/dleraebot)) This bot lets you find difinitions of words in Spanish using [RAE's dictionary](https://dle.rae.es/). It features direct message and inline search.
|
||||
* [remoteTelegramShell](https://github.com/EnriqueMoran/remoteTelegramShell) by [EnriqueMoran](https://github.com/EnriqueMoran). Control your LinuxOS computer through Telegram.
|
||||
* [Pyfram-telegram-bot](https://github.com/skelly37/pyfram-telegram-bot) Query wolframalpha.com and make use of its API through Telegram.
|
||||
* [TranslateThisVideoBot](https://gitlab.com/WuerfelDev/translatethisvideo) This Bot can understand spoken text in videos and translate it to English
|
||||
* [Zyprexa](https://t.me/mathemathicsBot) ([source](https://github.com/atif5/zyprexa)) Zyprexa can solve, help you solve any mathematical problem you encounter and convert your regular mathematical expressions into beautiful imagery using LaTeX.
|
||||
* [Bincode-telegram-bot](https://github.com/tusharhero/bincode-telegram-bot) by [tusharhero](https://github.com/tusharhero) - Makes [bincodes](https://github.com/tusharhero/bincode) from text provides and also converts them back to text.
|
||||
* [hydrolib_bot](https://github.com/Mayson90/hydrolib_bot) Toolset for Hydrophilia tabletop game (game cards, rules, structure...).
|
||||
|
||||
Want to have your bot listed here? Just make a pull requet.
|
||||
**Want to have your bot listed here? Just make a pull request. Only bots with public source code are accepted.**
|
||||
|
5
doc_req.txt
Normal file
5
doc_req.txt
Normal file
@ -0,0 +1,5 @@
|
||||
-r requirements.txt
|
||||
|
||||
furo
|
||||
sphinx_copybutton
|
||||
git+https://github.com/eternnoir/pyTelegramBotAPI.git
|
20
docs/Makefile
Normal file
20
docs/Makefile
Normal file
@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
BIN
docs/source/_static/logo.png
Normal file
BIN
docs/source/_static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 57 KiB |
BIN
docs/source/_static/logo2.png
Normal file
BIN
docs/source/_static/logo2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
49
docs/source/async_version/index.rst
Normal file
49
docs/source/async_version/index.rst
Normal file
@ -0,0 +1,49 @@
|
||||
====================
|
||||
AsyncTeleBot
|
||||
====================
|
||||
|
||||
|
||||
.. meta::
|
||||
:description: Asynchronous pyTelegramBotAPI
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, asynctelebot, documentation
|
||||
|
||||
|
||||
AsyncTeleBot methods
|
||||
--------------------
|
||||
|
||||
.. automodule:: telebot.async_telebot
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
||||
Asyncio filters
|
||||
---------------
|
||||
|
||||
.. automodule:: telebot.asyncio_filters
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Asynchronous storage for states
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: telebot.asyncio_storage
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Asyncio handler backends
|
||||
------------------------
|
||||
|
||||
|
||||
|
||||
.. automodule:: telebot.asyncio_handler_backends
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
17
docs/source/calldata.rst
Normal file
17
docs/source/calldata.rst
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
=====================
|
||||
Callback data factory
|
||||
=====================
|
||||
|
||||
.. meta::
|
||||
:description: Callback data factory in pyTelegramBotAPI
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, callbackdatafactory, guide, callbackdata, factory
|
||||
|
||||
|
||||
callback\_data file
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: telebot.callback_data
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
70
docs/source/conf.py
Normal file
70
docs/source/conf.py
Normal file
@ -0,0 +1,70 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'pyTelegramBotAPI Documentation'
|
||||
copyright = '2022, coder2020official'
|
||||
author = 'coder2020official'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '4.6.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autosectionlabel',
|
||||
'sphinx.ext.autodoc',
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx_copybutton",
|
||||
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = []
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'furo'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
#html_logo = 'logo.png'
|
||||
html_theme_options = {
|
||||
"light_css_variables": {
|
||||
"color-brand-primary": "#7C4DFF",
|
||||
"color-brand-content": "#7C4DFF",
|
||||
},
|
||||
"light_logo": "logo.png",
|
||||
"dark_logo": "logo2.png",
|
||||
}
|
12
docs/source/formatting.rst
Normal file
12
docs/source/formatting.rst
Normal file
@ -0,0 +1,12 @@
|
||||
==================
|
||||
Formatting options
|
||||
==================
|
||||
|
||||
.. meta::
|
||||
:description: Formatting options in pyTelegramBotAPI
|
||||
:keywords: html, markdown, parse_mode, formatting, ptba, pytba, pyTelegramBotAPI
|
||||
|
||||
.. automodule:: telebot.formatting
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
69
docs/source/index.rst
Normal file
69
docs/source/index.rst
Normal file
@ -0,0 +1,69 @@
|
||||
.. pyTelegramBotAPI documentation master file, created by
|
||||
sphinx-quickstart on Fri Feb 18 20:58:37 2022.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
Welcome to pyTelegramBotAPI's documentation!
|
||||
============================================
|
||||
|
||||
.. meta::
|
||||
:description: Official documentation of pyTelegramBotAPI
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, documentation, guide
|
||||
|
||||
|
||||
=======
|
||||
TeleBot
|
||||
=======
|
||||
TeleBot is synchronous and asynchronous implementation of `Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
|
||||
Chats
|
||||
-----
|
||||
English chat: `Private chat <https://telegram.me/joinchat/Bn4ixj84FIZVkwhk2jag6A>`__
|
||||
|
||||
Russian chat: `@pytelegrambotapi_talks_ru <https://t.me/pytelegrambotapi_talks_ru>`__
|
||||
|
||||
News: `@pyTelegramBotAPI <https://t.me/pytelegrambotapi>`__
|
||||
|
||||
Pypi: `Pypi <https://pypi.org/project/pyTelegramBotAPI/>`__
|
||||
|
||||
Source: `Github repository <https://github.com/eternnoir/pyTelegramBotAPI>`__
|
||||
|
||||
Some features:
|
||||
--------------
|
||||
Easy to learn and use.
|
||||
|
||||
Easy to understand.
|
||||
|
||||
Both sync and async.
|
||||
|
||||
Examples on features.
|
||||
|
||||
States
|
||||
|
||||
And more...
|
||||
|
||||
Content
|
||||
--------
|
||||
.. toctree::
|
||||
|
||||
install
|
||||
quick_start
|
||||
types
|
||||
sync_version/index
|
||||
async_version/index
|
||||
calldata
|
||||
util
|
||||
formatting
|
||||
|
||||
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
45
docs/source/install.rst
Normal file
45
docs/source/install.rst
Normal file
@ -0,0 +1,45 @@
|
||||
==================
|
||||
Installation Guide
|
||||
==================
|
||||
|
||||
.. meta::
|
||||
:description: Installation of pyTelegramBotAPI
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, installation, guide
|
||||
|
||||
|
||||
Using PIP
|
||||
----------
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install pyTelegramBotAPI
|
||||
|
||||
Using pipenv
|
||||
------------
|
||||
.. code-block:: bash
|
||||
|
||||
$ pipenv install pyTelegramBotAPI
|
||||
|
||||
By cloning repository
|
||||
---------------------
|
||||
.. code-block:: bash
|
||||
|
||||
$ git clone https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
$ cd pyTelegramBotAPI
|
||||
$ python setup.py install
|
||||
|
||||
Directly using pip
|
||||
------------------
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install git+https://github.com/eternnoir/pyTelegramBotAPI.git
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip install pytelegrambotapi --upgrade
|
||||
|
||||
|
20
docs/source/quick_start.rst
Normal file
20
docs/source/quick_start.rst
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
===========
|
||||
Quick start
|
||||
===========
|
||||
|
||||
.. meta::
|
||||
:description: Quickstart guide
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, quickstart, guide
|
||||
|
||||
Synchronous TeleBot
|
||||
-------------------
|
||||
.. literalinclude:: ../../examples/echo_bot.py
|
||||
:language: python
|
||||
|
||||
Asynchronous TeleBot
|
||||
--------------------
|
||||
.. literalinclude:: ../../examples/asynchronous_telebot/echo_bot.py
|
||||
:language: python
|
||||
|
||||
|
39
docs/source/sync_version/index.rst
Normal file
39
docs/source/sync_version/index.rst
Normal file
@ -0,0 +1,39 @@
|
||||
===============
|
||||
TeleBot version
|
||||
===============
|
||||
|
||||
.. meta::
|
||||
:description: Synchronous pyTelegramBotAPI documentation
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, methods, guide, files, sync
|
||||
|
||||
TeleBot methods
|
||||
---------------
|
||||
.. automodule:: telebot
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
custom_filters file
|
||||
------------------------------
|
||||
|
||||
.. automodule:: telebot.custom_filters
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Synchronous storage for states
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: telebot.storage
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
handler_backends file
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: telebot.handler_backends
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
10
docs/source/types.rst
Normal file
10
docs/source/types.rst
Normal file
@ -0,0 +1,10 @@
|
||||
============
|
||||
Types of API
|
||||
============
|
||||
|
||||
|
||||
|
||||
.. automodule:: telebot.types
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
16
docs/source/util.rst
Normal file
16
docs/source/util.rst
Normal file
@ -0,0 +1,16 @@
|
||||
============
|
||||
Utils
|
||||
============
|
||||
|
||||
.. meta::
|
||||
:description: Utils in pyTelegramBotAPI
|
||||
:keywords: ptba, pytba, pyTelegramBotAPI, utils, guide
|
||||
|
||||
|
||||
util file
|
||||
-------------------
|
||||
|
||||
.. automodule:: telebot.util
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
117
examples/anonymous_bot.py
Normal file
117
examples/anonymous_bot.py
Normal file
@ -0,0 +1,117 @@
|
||||
# This bot is needed to connect two people and their subsequent anonymous communication
|
||||
#
|
||||
# Avaiable commands:
|
||||
# `/start` - Just send you a messsage how to start
|
||||
# `/find` - Find a person you can contact
|
||||
# `/stop` - Stop active conversation
|
||||
|
||||
import telebot
|
||||
from telebot import types
|
||||
|
||||
# Initialize bot with your token
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
# The `users` variable is needed to contain chat ids that are either in the search or in the active dialog, like {chat_id, chat_id}
|
||||
users = {}
|
||||
# The `freeid` variable is needed to contain chat id, that want to start conversation
|
||||
# Or, in other words: chat id of user in the search
|
||||
freeid = None
|
||||
|
||||
# `/start` command handler
|
||||
#
|
||||
# That command only sends you 'Just use /find command!'
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message: types.Message):
|
||||
bot.send_message(message.chat.id, 'Just use /find command!')
|
||||
|
||||
# `/find` command handler
|
||||
#
|
||||
# That command finds opponent for you
|
||||
#
|
||||
# That command according to the following principle:
|
||||
# 1. You have written `/find` command
|
||||
# 2. If you are already in the search or have an active dialog, bot sends you 'Shut up!'
|
||||
# 3. If not:
|
||||
# 3.1. Bot sends you 'Finding...'
|
||||
# 3.2. If there is no user in the search:
|
||||
# 3.2.2. `freeid` updated with `your_chat_id`
|
||||
# 3.3. If there is user in the search:
|
||||
# 3.3.1. Both you and the user in the search recieve the message 'Founded!'
|
||||
# 3.3.2. `users` updated with a {user_in_the_search_chat_id, your_chat_id}
|
||||
# 3.3.3. `users` updated with a {your_chat_id, user_in_the_search_id}
|
||||
# 3.3.4. `freeid` updated with `None`
|
||||
@bot.message_handler(commands=['find'])
|
||||
def find(message: types.Message):
|
||||
global freeid
|
||||
|
||||
if message.chat.id not in users:
|
||||
bot.send_message(message.chat.id, 'Finding...')
|
||||
|
||||
if freeid is None:
|
||||
freeid = message.chat.id
|
||||
else:
|
||||
# Question:
|
||||
# Is there any way to simplify this like `bot.send_message([message.chat.id, freeid], 'Founded!')`?
|
||||
bot.send_message(message.chat.id, 'Founded!')
|
||||
bot.send_message(freeid, 'Founded!')
|
||||
|
||||
users[freeid] = message.chat.id
|
||||
users[message.chat.id] = freeid
|
||||
freeid = None
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'Shut up!')
|
||||
|
||||
# `/stop` command handler
|
||||
#
|
||||
# That command stops your current conversation (if it exist)
|
||||
#
|
||||
# That command according to the following principle:
|
||||
# 1. You have written `/stop` command
|
||||
# 2. If you are not have active dialog or you are not in search, bot sends you 'You are not in search!'
|
||||
# 3. If you are in active dialog:
|
||||
# 3.1. Bot sends you 'Stopping...'
|
||||
# 3.2. Bot sends 'Your opponent is leavin`...' to your opponent
|
||||
# 3.3. {your_opponent_chat_id, your_chat_id} removes from `users`
|
||||
# 3.4. {your_chat_id, your_opponent_chat_id} removes from `users`
|
||||
# 4. If you are only in search:
|
||||
# 4.1. Bot sends you 'Stopping...'
|
||||
# 4.2. `freeid` updated with `None`
|
||||
@bot.message_handler(commands=['stop'])
|
||||
def stop(message: types.Message):
|
||||
global freeid
|
||||
|
||||
if message.chat.id in users:
|
||||
bot.send_message(message.chat.id, 'Stopping...')
|
||||
bot.send_message(users[message.chat.id], 'Your opponent is leavin`...')
|
||||
|
||||
del users[users[message.chat.id]]
|
||||
del users[message.chat.id]
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
elif message.chat.id == freeid:
|
||||
bot.send_message(message.chat.id, 'Stopping...')
|
||||
freeid = None
|
||||
|
||||
print(users, freeid) # Debug purpose, you can remove that line
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'You are not in search!')
|
||||
|
||||
# message handler for conversation
|
||||
#
|
||||
# That handler needed to send message from one opponent to another
|
||||
# If you are not in `users`, you will recieve a message 'No one can hear you...'
|
||||
# Otherwise all your messages are sent to your opponent
|
||||
#
|
||||
# Questions:
|
||||
# 1. Is there any way to improve readability like `content_types=['all']`?
|
||||
# 2. Is there any way to register this message handler only when i found the opponent?
|
||||
@bot.message_handler(content_types=['animation', 'audio', 'contact', 'dice', 'document', 'location', 'photo', 'poll', 'sticker', 'text', 'venue', 'video', 'video_note', 'voice'])
|
||||
def chatting(message: types.Message):
|
||||
if message.chat.id in users:
|
||||
bot.copy_message(users[message.chat.id], users[users[message.chat.id]], message.id)
|
||||
else:
|
||||
bot.send_message(message.chat.id, 'No one can hear you...')
|
||||
|
||||
bot.infinity_polling(skip_pending=True)
|
@ -0,0 +1,26 @@
|
||||
from telebot import types
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot.asyncio_filters import AdvancedCustomFilter
|
||||
from telebot.callback_data import CallbackData, CallbackDataFilter
|
||||
|
||||
calendar_factory = CallbackData("year", "month", prefix="calendar")
|
||||
calendar_zoom = CallbackData("year", prefix="calendar_zoom")
|
||||
|
||||
|
||||
class CalendarCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'calendar_config'
|
||||
|
||||
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
class CalendarZoomCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'calendar_zoom_config'
|
||||
|
||||
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
def bind_filters(bot: AsyncTeleBot):
|
||||
bot.add_custom_filter(CalendarCallbackFilter())
|
||||
bot.add_custom_filter(CalendarZoomCallbackFilter())
|
@ -0,0 +1,92 @@
|
||||
import calendar
|
||||
from datetime import date, timedelta
|
||||
|
||||
from filters import calendar_factory, calendar_zoom
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
EMTPY_FIELD = '1'
|
||||
WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)]
|
||||
MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)]
|
||||
|
||||
|
||||
def generate_calendar_days(year: int, month: int):
|
||||
keyboard = InlineKeyboardMarkup(row_width=7)
|
||||
today = date.today()
|
||||
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text=date(year=year, month=month, day=1).strftime('%b %Y'),
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*[
|
||||
InlineKeyboardButton(
|
||||
text=day,
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
for day in WEEK_DAYS
|
||||
])
|
||||
|
||||
for week in calendar.Calendar().monthdayscalendar(year=year, month=month):
|
||||
week_buttons = []
|
||||
for day in week:
|
||||
day_name = ' '
|
||||
if day == today.day and today.year == year and today.month == month:
|
||||
day_name = '🔘'
|
||||
elif day != 0:
|
||||
day_name = str(day)
|
||||
week_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=day_name,
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*week_buttons)
|
||||
|
||||
previous_date = date(year=year, month=month, day=1) - timedelta(days=1)
|
||||
next_date = date(year=year, month=month, day=1) + timedelta(days=31)
|
||||
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text='Previous month',
|
||||
callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Zoom out',
|
||||
callback_data=calendar_zoom.new(year=year)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Next month',
|
||||
callback_data=calendar_factory.new(year=next_date.year, month=next_date.month)
|
||||
),
|
||||
)
|
||||
|
||||
return keyboard
|
||||
|
||||
|
||||
def generate_calendar_months(year: int):
|
||||
keyboard = InlineKeyboardMarkup(row_width=3)
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text=date(year=year, month=1, day=1).strftime('Year %Y'),
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*[
|
||||
InlineKeyboardButton(
|
||||
text=month,
|
||||
callback_data=calendar_factory.new(year=year, month=month_number)
|
||||
)
|
||||
for month_number, month in MONTHS
|
||||
])
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text='Previous year',
|
||||
callback_data=calendar_zoom.new(year=year - 1)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Next year',
|
||||
callback_data=calendar_zoom.new(year=year + 1)
|
||||
)
|
||||
)
|
||||
return keyboard
|
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you an advanced usage of CallbackData.
|
||||
In this example calendar was implemented
|
||||
"""
|
||||
import asyncio
|
||||
from datetime import date
|
||||
|
||||
from filters import calendar_factory, calendar_zoom, bind_filters
|
||||
from keyboards import generate_calendar_days, generate_calendar_months, EMTPY_FIELD
|
||||
from telebot import types
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
API_TOKEN = ''
|
||||
bot = AsyncTeleBot(API_TOKEN)
|
||||
|
||||
|
||||
@bot.message_handler(commands='start')
|
||||
async def start_command_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id,
|
||||
f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard."
|
||||
"\nPress /calendar to see it.")
|
||||
|
||||
|
||||
@bot.message_handler(commands='calendar')
|
||||
async def calendar_command_handler(message: types.Message):
|
||||
now = date.today()
|
||||
await bot.send_message(message.chat.id, 'Calendar',
|
||||
reply_markup=generate_calendar_days(year=now.year, month=now.month))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter())
|
||||
async def calendar_action_handler(call: types.CallbackQuery):
|
||||
callback_data: dict = calendar_factory.parse(callback_data=call.data)
|
||||
year, month = int(callback_data['year']), int(callback_data['month'])
|
||||
|
||||
await bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
|
||||
reply_markup=generate_calendar_days(year=year, month=month))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter())
|
||||
async def calendar_zoom_out_handler(call: types.CallbackQuery):
|
||||
callback_data: dict = calendar_zoom.parse(callback_data=call.data)
|
||||
year = int(callback_data.get('year'))
|
||||
|
||||
await bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
|
||||
reply_markup=generate_calendar_months(year=year))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD)
|
||||
async def callback_empty_field_handler(call: types.CallbackQuery):
|
||||
await bot.answer_callback_query(call.id)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bind_filters(bot)
|
||||
asyncio.run(bot.infinity_polling())
|
@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you how to use CallbackData
|
||||
"""
|
||||
|
||||
from telebot.callback_data import CallbackData, CallbackDataFilter
|
||||
from telebot import types
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot.asyncio_filters import AdvancedCustomFilter
|
||||
|
||||
API_TOKEN = 'TOKEN'
|
||||
PRODUCTS = [
|
||||
{'id': '0', 'name': 'xiaomi mi 10', 'price': 400},
|
||||
{'id': '1', 'name': 'samsung s20', 'price': 800},
|
||||
{'id': '2', 'name': 'iphone 13', 'price': 1300}
|
||||
]
|
||||
|
||||
bot = AsyncTeleBot(API_TOKEN)
|
||||
products_factory = CallbackData('product_id', prefix='products')
|
||||
|
||||
|
||||
def products_keyboard():
|
||||
return types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=product['name'],
|
||||
callback_data=products_factory.new(product_id=product["id"])
|
||||
)
|
||||
]
|
||||
for product in PRODUCTS
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def back_keyboard():
|
||||
return types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text='⬅',
|
||||
callback_data='back'
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ProductsCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'config'
|
||||
|
||||
async def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['products'])
|
||||
async def products_command_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard())
|
||||
|
||||
|
||||
# Only product with field - product_id = 2
|
||||
@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2'))
|
||||
async def product_one_callback(call: types.CallbackQuery):
|
||||
await bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True)
|
||||
|
||||
|
||||
# Any other products
|
||||
@bot.callback_query_handler(func=None, config=products_factory.filter())
|
||||
async def products_callback(call: types.CallbackQuery):
|
||||
callback_data: dict = products_factory.parse(callback_data=call.data)
|
||||
product_id = int(callback_data['product_id'])
|
||||
product = PRODUCTS[product_id]
|
||||
|
||||
text = f"Product name: {product['name']}\n" \
|
||||
f"Product price: {product['price']}"
|
||||
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||
text=text, reply_markup=back_keyboard())
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=lambda c: c.data == 'back')
|
||||
async def back_callback(call: types.CallbackQuery):
|
||||
await bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||
text='Products:', reply_markup=products_keyboard())
|
||||
|
||||
|
||||
bot.add_custom_filter(ProductsCallbackFilter())
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
12
examples/asynchronous_telebot/chat_join_request.py
Normal file
12
examples/asynchronous_telebot/chat_join_request.py
Normal file
@ -0,0 +1,12 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
import telebot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
@bot.chat_join_request_handler()
|
||||
async def make_some(message: telebot.types.ChatJoinRequest):
|
||||
await bot.send_message(message.chat.id, 'I accepted a new user!')
|
||||
await bot.approve_chat_join_request(message.chat.id, message.from_user.id)
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
34
examples/asynchronous_telebot/chat_member_example.py
Normal file
34
examples/asynchronous_telebot/chat_member_example.py
Normal file
@ -0,0 +1,34 @@
|
||||
from telebot import types,util
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member.
|
||||
@bot.chat_member_handler()
|
||||
async def chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
await bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message
|
||||
|
||||
#if bot is added to group, this handler will work
|
||||
@bot.my_chat_member_handler()
|
||||
async def my_chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
await bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group
|
||||
await bot.leave_chat(message.chat.id)
|
||||
|
||||
#content_Type_service is:
|
||||
#'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',
|
||||
#'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended',
|
||||
#'video_chat_participants_invited', 'message_auto_delete_timer_changed'
|
||||
# this handler deletes service messages
|
||||
|
||||
@bot.message_handler(content_types=util.content_type_service)
|
||||
async def delall(message: types.Message):
|
||||
await bot.delete_message(message.chat.id,message.message_id)
|
||||
import asyncio
|
||||
asyncio.run(bot.polling(allowed_updates=util.update_types))
|
@ -0,0 +1,14 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot import asyncio_filters
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
# Handler
|
||||
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
|
||||
async def answer_for_admin(message):
|
||||
await bot.send_message(message.chat.id,"hello my admin")
|
||||
|
||||
# Register filter
|
||||
bot.add_custom_filter(asyncio_filters.IsAdminFilter(bot))
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you usage of TextFilter
|
||||
In this example you will see how to use TextFilter
|
||||
with (message_handler, callback_query_handler, poll_handler)
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from telebot import types
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot.asyncio_filters import TextMatchFilter, TextFilter, IsReplyFilter
|
||||
|
||||
bot = AsyncTeleBot("")
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='hello'))
|
||||
async def hello_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True))
|
||||
async def hello_handler_ignore_case(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(contains=['good', 'bad']))
|
||||
async def contains_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True))
|
||||
async def contains_handler_ignore_case(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE
|
||||
async def starts_with_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE
|
||||
async def starts_with_handler_ignore_case(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay
|
||||
async def ends_with_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY
|
||||
async def ends_with_handler_ignore_case(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='/callback'))
|
||||
async def send_callback(message: types.Message):
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[types.InlineKeyboardButton(text='callback data', callback_data='example')],
|
||||
[types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')]
|
||||
]
|
||||
)
|
||||
await bot.send_message(message.chat.id, message.text, reply_markup=keyboard)
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='example'))
|
||||
async def callback_query_handler(call: types.CallbackQuery):
|
||||
await bot.answer_callback_query(call.id, call.data, show_alert=True)
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True))
|
||||
async def callback_query_handler_ignore_case(call: types.CallbackQuery):
|
||||
await bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='/poll'))
|
||||
async def send_poll(message: types.Message):
|
||||
await bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night'])
|
||||
await bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night'])
|
||||
|
||||
|
||||
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?'))
|
||||
async def poll_question_handler(poll: types.Poll):
|
||||
print(poll.question)
|
||||
|
||||
|
||||
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True))
|
||||
async def poll_question_handler_ignore_case(poll: types.Poll):
|
||||
print(poll.question + ' ignore case')
|
||||
|
||||
|
||||
# either hi or contains one of (привет, salom)
|
||||
@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True))
|
||||
async def multiple_patterns_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# starts with one of (mi, mea) for ex. minor, milk, meal, meat
|
||||
@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True))
|
||||
async def multiple_starts_with_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson
|
||||
@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True))
|
||||
async def multiple_ends_with_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# !ban /ban .ban !бан /бан .бан
|
||||
@bot.message_handler(is_reply=True,
|
||||
text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True))
|
||||
async def ban_command_handler(message: types.Message):
|
||||
if len(message.text) == 4 and message.chat.type != 'private':
|
||||
try:
|
||||
await bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id)
|
||||
await bot.reply_to(message.reply_to_message, 'Banned.')
|
||||
except Exception as err:
|
||||
print(err.args)
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bot.add_custom_filter(TextMatchFilter())
|
||||
bot.add_custom_filter(IsReplyFilter())
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,44 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
import telebot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
# AdvancedCustomFilter is for list, string filter values
|
||||
class MainFilter(telebot.asyncio_filters.AdvancedCustomFilter):
|
||||
key='text'
|
||||
@staticmethod
|
||||
async def check(message, text):
|
||||
return message.text in text
|
||||
|
||||
# SimpleCustomFilter is for boolean values, such as is_admin=True
|
||||
class IsAdmin(telebot.asyncio_filters.SimpleCustomFilter):
|
||||
key='is_admin'
|
||||
@staticmethod
|
||||
async def check(message: telebot.types.Message):
|
||||
result = await bot.get_chat_member(message.chat.id,message.from_user.id)
|
||||
return result.status in ['administrator','creator']
|
||||
|
||||
|
||||
@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin
|
||||
async def admin_rep(message):
|
||||
await bot.send_message(message.chat.id, "Hi admin")
|
||||
|
||||
@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin
|
||||
async def not_admin(message):
|
||||
await bot.send_message(message.chat.id, "You are not admin")
|
||||
|
||||
@bot.message_handler(text=['hi']) # Response to hi message
|
||||
async def welcome_hi(message):
|
||||
await bot.send_message(message.chat.id, 'You said hi')
|
||||
|
||||
@bot.message_handler(text=['bye']) # Response to bye message
|
||||
async def bye_user(message):
|
||||
await bot.send_message(message.chat.id, 'You said bye')
|
||||
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(MainFilter())
|
||||
bot.add_custom_filter(IsAdmin())
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,18 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
import telebot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
# Chat id can be private or supergroups.
|
||||
@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not.
|
||||
async def admin_rep(message):
|
||||
await bot.send_message(message.chat.id, "You are allowed to use this command.")
|
||||
|
||||
@bot.message_handler(commands=['admin'])
|
||||
async def not_admin(message):
|
||||
await bot.send_message(message.chat.id, "You are not allowed to use this command")
|
||||
|
||||
# Do not forget to register
|
||||
bot.add_custom_filter(telebot.asyncio_filters.ChatFilter())
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,23 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
import telebot
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
|
||||
# Check if message is a reply
|
||||
@bot.message_handler(is_reply=True)
|
||||
async def start_filter(message):
|
||||
await bot.send_message(message.chat.id, "Looks like you replied to my message.")
|
||||
|
||||
# Check if message was forwarded
|
||||
@bot.message_handler(is_forwarded=True)
|
||||
async def text_filter(message):
|
||||
await bot.send_message(message.chat.id, "I do not accept forwarded messages!")
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(telebot.asyncio_filters.IsReplyFilter())
|
||||
bot.add_custom_filter(telebot.asyncio_filters.ForwardFilter())
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,21 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
import telebot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
# Check if message starts with @admin tag
|
||||
@bot.message_handler(text_startswith="@admin")
|
||||
async def start_filter(message):
|
||||
await bot.send_message(message.chat.id, "Looks like you are calling admin, wait...")
|
||||
|
||||
# Check if text is hi or hello
|
||||
@bot.message_handler(text=['hi','hello'])
|
||||
async def text_filter(message):
|
||||
await bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name))
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(telebot.asyncio_filters.TextMatchFilter())
|
||||
bot.add_custom_filter(telebot.asyncio_filters.TextStartsFilter())
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
91
examples/asynchronous_telebot/custom_states.py
Normal file
91
examples/asynchronous_telebot/custom_states.py
Normal file
@ -0,0 +1,91 @@
|
||||
from telebot import asyncio_filters
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
# list of storages, you can use any storage
|
||||
from telebot.asyncio_storage import StateMemoryStorage
|
||||
|
||||
# new feature for states.
|
||||
from telebot.asyncio_handler_backends import State, StatesGroup
|
||||
|
||||
# default state storage is statememorystorage
|
||||
bot = AsyncTeleBot('TOKEN', state_storage=StateMemoryStorage())
|
||||
|
||||
|
||||
# Just create different statesgroup
|
||||
class MyStates(StatesGroup):
|
||||
name = State() # statesgroup should contain states
|
||||
surname = State()
|
||||
age = State()
|
||||
|
||||
|
||||
|
||||
# set_state -> sets a new state
|
||||
# delete_state -> delets state if exists
|
||||
# get_state -> returns state if exists
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
async def start_ex(message):
|
||||
"""
|
||||
Start command. Here we are starting state
|
||||
"""
|
||||
await bot.set_state(message.from_user.id, MyStates.name, message.chat.id)
|
||||
await bot.send_message(message.chat.id, 'Hi, write me a name')
|
||||
|
||||
|
||||
|
||||
@bot.message_handler(state="*", commands='cancel')
|
||||
async def any_state(message):
|
||||
"""
|
||||
Cancel state
|
||||
"""
|
||||
await bot.send_message(message.chat.id, "Your state was cancelled.")
|
||||
await bot.delete_state(message.from_user.id, message.chat.id)
|
||||
|
||||
@bot.message_handler(state=MyStates.name)
|
||||
async def name_get(message):
|
||||
"""
|
||||
State 1. Will process when user's state is MyStates.name.
|
||||
"""
|
||||
await bot.send_message(message.chat.id, f'Now write me a surname')
|
||||
await bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
|
||||
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
data['name'] = message.text
|
||||
|
||||
|
||||
@bot.message_handler(state=MyStates.surname)
|
||||
async def ask_age(message):
|
||||
"""
|
||||
State 2. Will process when user's state is MyStates.surname.
|
||||
"""
|
||||
await bot.send_message(message.chat.id, "What is your age?")
|
||||
await bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
|
||||
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
data['surname'] = message.text
|
||||
|
||||
# result
|
||||
@bot.message_handler(state=MyStates.age, is_digit=True)
|
||||
async def ready_for_answer(message):
|
||||
"""
|
||||
State 3. Will process when user's state is MyStates.age.
|
||||
"""
|
||||
async with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
await bot.send_message(message.chat.id, "Ready, take a look:\n<b>Name: {name}\nSurname: {surname}\nAge: {age}</b>".format(name=data['name'], surname=data['surname'], age=message.text), parse_mode="html")
|
||||
await bot.delete_state(message.from_user.id, message.chat.id)
|
||||
|
||||
#incorrect number
|
||||
@bot.message_handler(state=MyStates.age, is_digit=False)
|
||||
async def age_incorrect(message):
|
||||
"""
|
||||
Will process for wrong input when state is MyState.age
|
||||
"""
|
||||
await bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
|
||||
|
||||
# register filters
|
||||
|
||||
bot.add_custom_filter(asyncio_filters.StateFilter(bot))
|
||||
bot.add_custom_filter(asyncio_filters.IsDigitFilter())
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
21
examples/asynchronous_telebot/download_file_example.py
Normal file
21
examples/asynchronous_telebot/download_file_example.py
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
import telebot
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
@bot.message_handler(content_types=['photo'])
|
||||
async def new_message(message: telebot.types.Message):
|
||||
result_message = await bot.send_message(message.chat.id, '<i>Downloading your photo...</i>', parse_mode='HTML', disable_web_page_preview=True)
|
||||
file_path = await bot.get_file(message.photo[-1].file_id)
|
||||
downloaded_file = await bot.download_file(file_path.file_path)
|
||||
with open('file.jpg', 'wb') as new_file:
|
||||
new_file.write(downloaded_file)
|
||||
await bot.edit_message_text(chat_id=message.chat.id, message_id=result_message.id, text='<i>Done!</i>', parse_mode='HTML')
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
27
examples/asynchronous_telebot/echo_bot.py
Normal file
27
examples/asynchronous_telebot/echo_bot.py
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This is a simple echo bot using the decorator mechanism.
|
||||
# It echoes any incoming text messages.
|
||||
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
|
||||
# Handle '/start' and '/help'
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
async def send_welcome(message):
|
||||
await bot.reply_to(message, """\
|
||||
Hi there, I am EchoBot.
|
||||
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!\
|
||||
""")
|
||||
|
||||
|
||||
# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
async def echo_message(message):
|
||||
await bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
28
examples/asynchronous_telebot/exception_handler.py
Normal file
28
examples/asynchronous_telebot/exception_handler.py
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
import telebot
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
class ExceptionHandler(telebot.ExceptionHandler):
|
||||
def handle(self, exception):
|
||||
logger.error(exception)
|
||||
|
||||
bot = AsyncTeleBot('TOKEN',exception_handler=ExceptionHandler())
|
||||
|
||||
|
||||
|
||||
|
||||
@bot.message_handler(commands=['photo'])
|
||||
async def photo_send(message: telebot.types.Message):
|
||||
await bot.send_message(message.chat.id, 'Hi, this is an example of exception handlers.')
|
||||
raise Exception('test') # Exception goes to ExceptionHandler if it is set
|
||||
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
54
examples/asynchronous_telebot/formatting_example.py
Normal file
54
examples/asynchronous_telebot/formatting_example.py
Normal file
@ -0,0 +1,54 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot import formatting
|
||||
|
||||
bot = AsyncTeleBot('token')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
async def start_message(message):
|
||||
await bot.send_message(
|
||||
message.chat.id,
|
||||
# function which connects all strings
|
||||
formatting.format_text(
|
||||
formatting.mbold(message.from_user.first_name),
|
||||
formatting.mitalic(message.from_user.first_name),
|
||||
formatting.munderline(message.from_user.first_name),
|
||||
formatting.mstrikethrough(message.from_user.first_name),
|
||||
formatting.mcode(message.from_user.first_name),
|
||||
separator=" " # separator separates all strings
|
||||
),
|
||||
parse_mode='MarkdownV2'
|
||||
)
|
||||
|
||||
# just a bold text using markdownv2
|
||||
await bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.mbold(message.from_user.first_name),
|
||||
parse_mode='MarkdownV2'
|
||||
)
|
||||
|
||||
# html
|
||||
await bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.format_text(
|
||||
formatting.hbold(message.from_user.first_name),
|
||||
formatting.hitalic(message.from_user.first_name),
|
||||
formatting.hunderline(message.from_user.first_name),
|
||||
formatting.hstrikethrough(message.from_user.first_name),
|
||||
formatting.hcode(message.from_user.first_name),
|
||||
# hide_link is only for html
|
||||
formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'),
|
||||
separator=" "
|
||||
),
|
||||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
# just a bold text in html
|
||||
await bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.hbold(message.from_user.first_name),
|
||||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,38 @@
|
||||
# Just a little example of middleware handlers
|
||||
|
||||
from telebot.asyncio_handler_backends import BaseMiddleware, CancelUpdate
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
class SimpleMiddleware(BaseMiddleware):
|
||||
def __init__(self, limit) -> None:
|
||||
self.last_time = {}
|
||||
self.limit = limit
|
||||
self.update_types = ['message']
|
||||
# Always specify update types, otherwise middlewares won't work
|
||||
|
||||
|
||||
async def pre_process(self, message, data):
|
||||
if not message.from_user.id in self.last_time:
|
||||
# User is not in a dict, so lets add and cancel this function
|
||||
self.last_time[message.from_user.id] = message.date
|
||||
return
|
||||
if message.date - self.last_time[message.from_user.id] < self.limit:
|
||||
# User is flooding
|
||||
await bot.send_message(message.chat.id, 'You are making request too often')
|
||||
return CancelUpdate()
|
||||
self.last_time[message.from_user.id] = message.date
|
||||
|
||||
|
||||
async def post_process(self, message, data, exception):
|
||||
pass
|
||||
|
||||
bot.setup_middleware(SimpleMiddleware(2))
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
async def start(message):
|
||||
await bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
49
examples/asynchronous_telebot/middleware/i18n.py
Normal file
49
examples/asynchronous_telebot/middleware/i18n.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This example shows how to implement i18n (internationalization) l10n (localization) to create
|
||||
# multi-language bots with middleware handler.
|
||||
#
|
||||
# Also, you could check language code in handler itself too.
|
||||
# But this example just to show the work of middlewares.
|
||||
|
||||
import telebot
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot import asyncio_handler_backends
|
||||
import logging
|
||||
|
||||
logger = telebot.logger
|
||||
telebot.logger.setLevel(logging.DEBUG) # Outputs debug messages to console.
|
||||
|
||||
TRANSLATIONS = {
|
||||
'hello': {
|
||||
'en': 'hello',
|
||||
'ru': 'привет',
|
||||
'uz': 'salom'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
class LanguageMiddleware(asyncio_handler_backends.BaseMiddleware):
|
||||
def __init__(self):
|
||||
self.update_types = ['message'] # Update types that will be handled by this middleware.
|
||||
async def pre_process(self, message, data):
|
||||
data['response'] = TRANSLATIONS['hello'][message.from_user.language_code]
|
||||
async def post_process(self, message, data, exception):
|
||||
if exception: # You can get exception occured in handler.
|
||||
logger.exception(str(exception))
|
||||
|
||||
bot.setup_middleware(LanguageMiddleware()) # do not forget to setup
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
async def start(message, data: dict):
|
||||
# you can get the data in handler too.
|
||||
# Not necessary to create data parameter in handler function.
|
||||
await bot.send_message(message.chat.id, data['response'])
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,120 @@
|
||||
import contextvars
|
||||
import gettext
|
||||
import os
|
||||
|
||||
from telebot.asyncio_handler_backends import BaseMiddleware
|
||||
|
||||
try:
|
||||
from babel.support import LazyProxy
|
||||
|
||||
babel_imported = True
|
||||
except ImportError:
|
||||
babel_imported = False
|
||||
|
||||
|
||||
class I18N(BaseMiddleware):
|
||||
"""
|
||||
This middleware provides high-level tool for internationalization
|
||||
It is based on gettext util.
|
||||
"""
|
||||
|
||||
context_lang = contextvars.ContextVar('language', default=None)
|
||||
|
||||
def __init__(self, translations_path, domain_name: str):
|
||||
super().__init__()
|
||||
self.update_types = self.process_update_types()
|
||||
|
||||
self.path = translations_path
|
||||
self.domain = domain_name
|
||||
self.translations = self.find_translations()
|
||||
|
||||
@property
|
||||
def available_translations(self):
|
||||
return list(self.translations)
|
||||
|
||||
def gettext(self, text: str, lang: str = None):
|
||||
"""
|
||||
Singular translations
|
||||
"""
|
||||
|
||||
if lang is None:
|
||||
lang = self.context_lang.get()
|
||||
|
||||
if lang not in self.translations:
|
||||
return text
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.gettext(text)
|
||||
|
||||
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
|
||||
"""
|
||||
Plural translations
|
||||
"""
|
||||
if lang is None:
|
||||
lang = self.context_lang.get()
|
||||
|
||||
if lang not in self.translations:
|
||||
if n == 1:
|
||||
return singular
|
||||
return plural
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.ngettext(singular, plural, n)
|
||||
|
||||
def lazy_gettext(self, text: str, lang: str = None):
|
||||
if not babel_imported:
|
||||
raise RuntimeError('babel module is not imported. Check that you installed it.')
|
||||
return LazyProxy(self.gettext, text, lang, enable_cache=False)
|
||||
|
||||
def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1):
|
||||
if not babel_imported:
|
||||
raise RuntimeError('babel module is not imported. Check that you installed it.')
|
||||
return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False)
|
||||
|
||||
async def get_user_language(self, obj):
|
||||
"""
|
||||
You need to override this method and return user language
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def process_update_types(self) -> list:
|
||||
"""
|
||||
You need to override this method and return any update types which you want to be processed
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def pre_process(self, message, data):
|
||||
"""
|
||||
context language variable will be set each time when update from 'process_update_types' comes
|
||||
value is the result of 'get_user_language' method
|
||||
"""
|
||||
self.context_lang.set(await self.get_user_language(obj=message))
|
||||
|
||||
async def post_process(self, message, data, exception):
|
||||
pass
|
||||
|
||||
def find_translations(self):
|
||||
"""
|
||||
Looks for translations with passed 'domain' in passed 'path'
|
||||
"""
|
||||
if not os.path.exists(self.path):
|
||||
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
|
||||
|
||||
result = {}
|
||||
|
||||
for name in os.listdir(self.path):
|
||||
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
|
||||
|
||||
if not os.path.isdir(translations_path):
|
||||
continue
|
||||
|
||||
po_file = os.path.join(translations_path, self.domain + '.po')
|
||||
mo_file = po_file[:-2] + 'mo'
|
||||
|
||||
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
|
||||
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
|
||||
|
||||
with open(mo_file, 'rb') as file:
|
||||
result[name] = gettext.GNUTranslations(file)
|
||||
|
||||
return result
|
@ -0,0 +1,34 @@
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
|
||||
|
||||
|
||||
def languages_keyboard():
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text="English", callback_data='en'),
|
||||
InlineKeyboardButton(text="Русский", callback_data='ru'),
|
||||
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def clicker_keyboard(_):
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text=_("click"), callback_data='click'),
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def menu_keyboard(_):
|
||||
keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||
keyboard.add(
|
||||
KeyboardButton(text=_("My user id")),
|
||||
KeyboardButton(text=_("My user name")),
|
||||
KeyboardButton(text=_("My first name"))
|
||||
)
|
||||
|
||||
return keyboard
|
@ -0,0 +1,81 @@
|
||||
# English translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
#, fuzzy
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr ""
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Hello, {user_fist_name}!\n"
|
||||
#~ "This is the example of multilanguage bot.\n"
|
||||
#~ "Available commands:\n"
|
||||
#~ "\n"
|
||||
#~ "/lang - change your language\n"
|
||||
#~ "/plural - pluralization example"
|
||||
#~ msgstr ""
|
||||
|
@ -0,0 +1,82 @@
|
||||
# Russian translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: ru\n"
|
||||
"Language-Team: ru <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "Клик"
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr "Мой user id"
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr "Мой user name"
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr "Мой first name"
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
"Привет, {user_fist_name}!\n"
|
||||
"Это пример мультиязычного бота.\n"
|
||||
"Доступные команды:\n"
|
||||
"\n"
|
||||
"/lang - изменить язык\n"
|
||||
"/plural - пример плюрализации\n"
|
||||
"/menu - Пример текстового меню"
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr "Язык был сменён"
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "У вас {number} клик"
|
||||
msgstr[1] "У вас {number} клика"
|
||||
msgstr[2] "У вас {number} кликов"
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Это кликер.\n"
|
||||
"\n"
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте."
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr "Кажется, вы перепутали язык"
|
||||
|
@ -0,0 +1,80 @@
|
||||
# Uzbek (Latin) translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: uz_Latn\n"
|
||||
"Language-Team: uz_Latn <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "clik"
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr "Mani user id"
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr "Mani user name"
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr "Mani first name"
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
"Salom, {user_fist_name}!\n"
|
||||
"Bu multilanguage bot misoli.\n"
|
||||
"Mavjud buyruqlar:\n"
|
||||
"\n"
|
||||
"/lang - tilni ozgartirish\n"
|
||||
"/plural - pluralizatsiya misoli\n"
|
||||
"/menu - text menu misoli"
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr "Til ozgartirildi"
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "Sizda {number}ta clik"
|
||||
msgstr[1] "Sizda {number}ta clik"
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Bu clicker.\n"
|
||||
"\n"
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli."
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr "Tilni adashtirdiz"
|
||||
|
@ -0,0 +1,214 @@
|
||||
"""
|
||||
In this example you will learn how to adapt your bot to different languages
|
||||
Using built-in middleware I18N.
|
||||
|
||||
You need to install babel package 'https://pypi.org/project/Babel/'
|
||||
Babel provides a command-line interface for working with message catalogs
|
||||
After installing babel package you have a script called 'pybabel'
|
||||
Too see all the commands open terminal and type 'pybabel --help'
|
||||
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
|
||||
|
||||
Create a directory 'locales' where our translations will be stored
|
||||
|
||||
First we need to extract texts:
|
||||
pybabel extract -o locales/{domain_name}.pot --input-dirs .
|
||||
{domain_name}.pot - is the file where all translations are saved
|
||||
The name of this file should be the same as domain which you pass to I18N class
|
||||
In this example domain_name will be 'messages'
|
||||
|
||||
For gettext (singular texts) we use '_' alias and it works perfect
|
||||
You may also you some alias for ngettext (plural texts) but you can face with a problem that
|
||||
your plural texts are not being extracted
|
||||
That is because by default 'pybabel extract' recognizes the following keywords:
|
||||
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
|
||||
To add your own keyword you can use '-k' flag
|
||||
In this example for 'ngettext' i will assign double underscore alias '__'
|
||||
|
||||
Full command with pluralization support will look so:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
|
||||
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l en
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l ru
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
|
||||
|
||||
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
|
||||
After you translated all the texts you need to compile .po files:
|
||||
pybabel compile -d locales
|
||||
|
||||
When you delete/update your texts you also need to update them in .po files:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
pybabel update -i locales/{domain_name}.pot -d locales
|
||||
- translate
|
||||
pybabel compile -d locales
|
||||
|
||||
If you have any exceptions check:
|
||||
- you have installed babel
|
||||
- translations are ready, so you just compiled it
|
||||
- in the commands above you replaced {domain_name} to messages
|
||||
- you are writing commands from correct path in terminal
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Union
|
||||
|
||||
import keyboards
|
||||
from telebot import types
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot.asyncio_filters import TextMatchFilter, TextFilter
|
||||
from i18n_base_middleware import I18N
|
||||
from telebot.asyncio_storage.memory_storage import StateMemoryStorage
|
||||
|
||||
|
||||
class I18NMiddleware(I18N):
|
||||
|
||||
def process_update_types(self) -> list:
|
||||
"""
|
||||
Here you need to return a list of update types which you want to be processed
|
||||
"""
|
||||
return ['message', 'callback_query']
|
||||
|
||||
async def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]):
|
||||
"""
|
||||
This method is called when new update comes (only updates which you return in 'process_update_types' method)
|
||||
Returned language will be used in 'pre_process' method of parent class
|
||||
Returned language will be set to context language variable.
|
||||
If you need to get translation with user's actual language you don't have to pass it manually
|
||||
It will be automatically passed from context language value.
|
||||
However if you need some other language you can always pass it.
|
||||
"""
|
||||
|
||||
user_id = obj.from_user.id
|
||||
|
||||
if user_id not in users_lang:
|
||||
users_lang[user_id] = 'en'
|
||||
|
||||
return users_lang[user_id]
|
||||
|
||||
|
||||
storage = StateMemoryStorage()
|
||||
bot = AsyncTeleBot("", state_storage=storage)
|
||||
|
||||
i18n = I18NMiddleware(translations_path='locales', domain_name='messages')
|
||||
_ = i18n.gettext # for singular translations
|
||||
__ = i18n.ngettext # for plural translations
|
||||
|
||||
# These are example storages, do not use it in a production development
|
||||
users_lang = {}
|
||||
users_clicks = {}
|
||||
|
||||
|
||||
@bot.message_handler(commands='start')
|
||||
async def start_handler(message: types.Message):
|
||||
text = _("Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example")
|
||||
|
||||
# remember don't use f string for interpolation, use .format method instead
|
||||
text = text.format(user_fist_name=message.from_user.first_name)
|
||||
await bot.send_message(message.from_user.id, text)
|
||||
|
||||
|
||||
@bot.message_handler(commands='lang')
|
||||
async def change_language_handler(message: types.Message):
|
||||
await bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
|
||||
reply_markup=keyboards.languages_keyboard())
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn']))
|
||||
async def language_handler(call: types.CallbackQuery):
|
||||
lang = call.data
|
||||
users_lang[call.from_user.id] = lang
|
||||
|
||||
# When you changed user language, you have to pass it manually beacause it is not changed in context
|
||||
await bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
|
||||
|
||||
|
||||
@bot.message_handler(commands='plural')
|
||||
async def pluralization_handler(message: types.Message):
|
||||
if not users_clicks.get(message.from_user.id):
|
||||
users_clicks[message.from_user.id] = 0
|
||||
clicks = users_clicks[message.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
|
||||
await bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='click'))
|
||||
async def click_handler(call: types.CallbackQuery):
|
||||
if not users_clicks.get(call.from_user.id):
|
||||
users_clicks[call.from_user.id] = 1
|
||||
else:
|
||||
users_clicks[call.from_user.id] += 1
|
||||
|
||||
clicks = users_clicks[call.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
|
||||
await bot.edit_message_text(text, call.from_user.id, call.message.message_id,
|
||||
reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
@bot.message_handler(commands='menu')
|
||||
async def menu_handler(message: types.Message):
|
||||
text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.")
|
||||
await bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_))
|
||||
|
||||
|
||||
# For lazy tranlations
|
||||
# lazy gettext is used when you don't know user's locale
|
||||
# It can be used for example to handle text buttons in multilanguage bot
|
||||
# The actual translation will be delayed until update comes and context language is set
|
||||
l_ = i18n.lazy_gettext
|
||||
|
||||
|
||||
# Handlers below will handle text according to user's language
|
||||
@bot.message_handler(text=l_("My user id"))
|
||||
async def return_user_id(message: types.Message):
|
||||
await bot.send_message(message.chat.id, str(message.from_user.id))
|
||||
|
||||
|
||||
@bot.message_handler(text=l_("My user name"))
|
||||
async def return_user_id(message: types.Message):
|
||||
username = message.from_user.username
|
||||
if not username:
|
||||
username = '-'
|
||||
await bot.send_message(message.chat.id, username)
|
||||
|
||||
|
||||
# You can make it case insensitive
|
||||
@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True))
|
||||
async def return_user_id(message: types.Message):
|
||||
await bot.send_message(message.chat.id, message.from_user.first_name)
|
||||
|
||||
|
||||
all_menu_texts = []
|
||||
for language in i18n.available_translations:
|
||||
for menu_text in ("My user id", "My user name", "My first name"):
|
||||
all_menu_texts.append(_(menu_text, language))
|
||||
|
||||
|
||||
# When user confused language. (handles all menu buttons texts)
|
||||
@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True))
|
||||
async def missed_message(message: types.Message):
|
||||
await bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bot.setup_middleware(i18n)
|
||||
bot.add_custom_filter(TextMatchFilter())
|
||||
asyncio.run(bot.infinity_polling())
|
17
examples/asynchronous_telebot/multibot/README.MD
Normal file
17
examples/asynchronous_telebot/multibot/README.MD
Normal file
@ -0,0 +1,17 @@
|
||||
You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot.
|
||||
<br>
|
||||
This type of bots are called <b>multibots</b>. They are created using webhooks.
|
||||
<br>
|
||||
|
||||
This is the example of simple multibot.<br>
|
||||
In order to reproduce this example you need to have <b>domain and ssl connection</b>.
|
||||
<br>
|
||||
If you have, go to config.py and specify your data.
|
||||
<br>
|
||||
There is also file called <b>nginx_conf.conf</b>, we will use nginx as proxy-server and this file is example nginx.conf file.
|
||||
<br>
|
||||
Make sure that server_name and port are the same in both config and nginx_conf
|
||||
<br>
|
||||
(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it)
|
||||
<br>
|
||||
Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts.
|
6
examples/asynchronous_telebot/multibot/config.py
Normal file
6
examples/asynchronous_telebot/multibot/config.py
Normal file
@ -0,0 +1,6 @@
|
||||
MAIN_BOT_TOKEN = "your_main_bot_token"
|
||||
|
||||
WEBHOOK_HOST = "your_domain.com"
|
||||
WEBHOOK_PATH = "telegram_webhook"
|
||||
WEBAPP_HOST = "0.0.0.0"
|
||||
WEBAPP_PORT = 3500
|
15
examples/asynchronous_telebot/multibot/handlers.py
Normal file
15
examples/asynchronous_telebot/multibot/handlers.py
Normal file
@ -0,0 +1,15 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from telebot import types
|
||||
|
||||
|
||||
async def hello_handler(message: types.Message, bot: AsyncTeleBot):
|
||||
await bot.send_message(message.chat.id, "Hi :)")
|
||||
|
||||
|
||||
async def echo_handler(message: types.Message, bot: AsyncTeleBot):
|
||||
await bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
def register_handlers(bot: AsyncTeleBot):
|
||||
bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True)
|
||||
bot.register_message_handler(echo_handler, pass_bot=True)
|
56
examples/asynchronous_telebot/multibot/main.py
Normal file
56
examples/asynchronous_telebot/multibot/main.py
Normal file
@ -0,0 +1,56 @@
|
||||
import asyncio
|
||||
|
||||
from aiohttp import web
|
||||
from telebot import types, util
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
from handlers import register_handlers
|
||||
|
||||
import config
|
||||
|
||||
main_bot = AsyncTeleBot(config.MAIN_BOT_TOKEN)
|
||||
app = web.Application()
|
||||
tokens = {config.MAIN_BOT_TOKEN: True}
|
||||
|
||||
|
||||
async def webhook(request):
|
||||
token = request.match_info.get('token')
|
||||
if not tokens.get(token):
|
||||
return web.Response(status=404)
|
||||
|
||||
if request.headers.get('content-type') != 'application/json':
|
||||
return web.Response(status=403)
|
||||
|
||||
json_string = await request.json()
|
||||
update = types.Update.de_json(json_string)
|
||||
if token == main_bot.token:
|
||||
await main_bot.process_new_updates([update])
|
||||
return web.Response()
|
||||
|
||||
from_update_bot = AsyncTeleBot(token)
|
||||
register_handlers(from_update_bot)
|
||||
await from_update_bot.process_new_updates([update])
|
||||
return web.Response()
|
||||
|
||||
|
||||
app.router.add_post("/" + config.WEBHOOK_PATH + "/{token}", webhook)
|
||||
|
||||
|
||||
@main_bot.message_handler(commands=['add_bot'])
|
||||
async def add_bot(message: types.Message):
|
||||
token = util.extract_arguments(message.text)
|
||||
tokens[token] = True
|
||||
|
||||
new_bot = AsyncTeleBot(token)
|
||||
await new_bot.delete_webhook()
|
||||
await new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}")
|
||||
|
||||
await new_bot.send_message(message.chat.id, "Webhook was set.")
|
||||
|
||||
|
||||
async def main():
|
||||
await main_bot.delete_webhook()
|
||||
await main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}")
|
||||
web.run_app(app, host=config.WEBAPP_HOST, port=config.WEBAPP_PORT)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
8
examples/asynchronous_telebot/multibot/nginx_conf.conf
Normal file
8
examples/asynchronous_telebot/multibot/nginx_conf.conf
Normal file
@ -0,0 +1,8 @@
|
||||
server {
|
||||
server_name your_domain.com;
|
||||
|
||||
location /telegram_webhook/ {
|
||||
proxy_pass http://localhost:3500;
|
||||
}
|
||||
|
||||
}
|
20
examples/asynchronous_telebot/register_handler.py
Normal file
20
examples/asynchronous_telebot/register_handler.py
Normal file
@ -0,0 +1,20 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
async def start_executor(message):
|
||||
await bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
bot.register_message_handler(start_executor, commands=['start']) # Start command executor
|
||||
|
||||
# See also
|
||||
# bot.register_callback_query_handler(*args, **kwargs)
|
||||
# bot.register_channel_post_handler(*args, **kwargs)
|
||||
# bot.register_chat_member_handler(*args, **kwargs)
|
||||
# bot.register_inline_handler(*args, **kwargs)
|
||||
# bot.register_my_chat_member_handler(*args, **kwargs)
|
||||
# bot.register_edited_message_handler(*args, **kwargs)
|
||||
# And other functions..
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
29
examples/asynchronous_telebot/send_file_example.py
Normal file
29
examples/asynchronous_telebot/send_file_example.py
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
import telebot
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['photo'])
|
||||
async def photo_send(message: telebot.types.Message):
|
||||
with open('test.png', 'rb') as new_file:
|
||||
await bot.send_photo(message.chat.id, new_file)
|
||||
|
||||
@bot.message_handler(commands=['document'])
|
||||
async def document_send(message: telebot.types.Message):
|
||||
with open('test.docx', 'rb') as new_file:
|
||||
await bot.send_document(message.chat.id, new_file)
|
||||
|
||||
@bot.message_handler(commands=['photos'])
|
||||
async def photos_send(message: telebot.types.Message):
|
||||
with open('test.png', 'rb') as new_file, open('test2.png', 'rb') as new_file2:
|
||||
await bot.send_media_group(message.chat.id, [telebot.types.InputMediaPhoto(new_file), telebot.types.InputMediaPhoto(new_file2)])
|
||||
|
||||
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
35
examples/asynchronous_telebot/set_command_example.py
Normal file
35
examples/asynchronous_telebot/set_command_example.py
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This is a set_my_commands example.
|
||||
# Press on [/] button in telegram client.
|
||||
# Important, to update the command menu, be sure to exit the chat with the bot and log in again
|
||||
# Important, command for chat_id and for group have a higher priority than for all
|
||||
|
||||
import asyncio
|
||||
import telebot
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
bot = AsyncTeleBot(API_TOKEN)
|
||||
|
||||
|
||||
async def main():
|
||||
# use in for delete with the necessary scope and language_code if necessary
|
||||
await bot.delete_my_commands(scope=None, language_code=None)
|
||||
|
||||
await bot.set_my_commands(
|
||||
commands=[
|
||||
telebot.types.BotCommand("command1", "command1 description"),
|
||||
telebot.types.BotCommand("command2", "command2 description")
|
||||
],
|
||||
# scope=telebot.types.BotCommandScopeChat(12345678) # use for personal command menu for users
|
||||
# scope=telebot.types.BotCommandScopeAllPrivateChats() # use for all private chats
|
||||
)
|
||||
|
||||
cmd = await bot.get_my_commands(scope=None, language_code=None)
|
||||
print([c.to_json() for c in cmd])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
15
examples/asynchronous_telebot/skip_updates_example.py
Normal file
15
examples/asynchronous_telebot/skip_updates_example.py
Normal file
@ -0,0 +1,15 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
bot = AsyncTeleBot('TOKEN')
|
||||
|
||||
@bot.message_handler(commands=['start', 'help'])
|
||||
async def send_welcome(message):
|
||||
await bot.reply_to(message, "Howdy, how are you doing?")
|
||||
|
||||
@bot.message_handler(func=lambda message: True)
|
||||
async def echo_all(message):
|
||||
await bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling(skip_pending=True)) # to skip updates
|
52
examples/asynchronous_telebot/timer_bot_async.py
Normal file
52
examples/asynchronous_telebot/timer_bot_async.py
Normal file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# This is a simple bot with schedule timer
|
||||
# https://github.com/ibrb/python-aioschedule
|
||||
# https://schedule.readthedocs.io
|
||||
|
||||
import asyncio
|
||||
import aioschedule
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
API_TOKEN = '<api_token>'
|
||||
bot = AsyncTeleBot(API_TOKEN)
|
||||
|
||||
|
||||
async def beep(chat_id) -> None:
|
||||
"""Send the beep message."""
|
||||
await bot.send_message(chat_id, text='Beep!')
|
||||
aioschedule.clear(chat_id) # return schedule.CancelJob not working in aioschedule use tag for delete
|
||||
|
||||
|
||||
@bot.message_handler(commands=['help', 'start'])
|
||||
async def send_welcome(message):
|
||||
await bot.reply_to(message, "Hi! Use /set <seconds> to set a timer")
|
||||
|
||||
|
||||
@bot.message_handler(commands=['set'])
|
||||
async def set_timer(message):
|
||||
args = message.text.split()
|
||||
if len(args) > 1 and args[1].isdigit():
|
||||
sec = int(args[1])
|
||||
aioschedule.every(sec).seconds.do(beep, message.chat.id).tag(message.chat.id)
|
||||
else:
|
||||
await bot.reply_to(message, 'Usage: /set <seconds>')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['unset'])
|
||||
def unset_timer(message):
|
||||
aioschedule.clean(message.chat.id)
|
||||
|
||||
|
||||
async def scheduler():
|
||||
while True:
|
||||
await aioschedule.run_pending()
|
||||
await asyncio.sleep(1)
|
||||
|
||||
|
||||
async def main():
|
||||
await asyncio.gather(bot.infinity_polling(), scheduler())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
16
examples/asynchronous_telebot/update_listener.py
Normal file
16
examples/asynchronous_telebot/update_listener.py
Normal file
@ -0,0 +1,16 @@
|
||||
from telebot.async_telebot import AsyncTeleBot
|
||||
|
||||
# Update listeners are functions that are called when any update is received.
|
||||
|
||||
bot = AsyncTeleBot(token='TOKEN')
|
||||
|
||||
async def update_listener(messages):
|
||||
for message in messages:
|
||||
if message.text == '/start':
|
||||
await bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
bot.set_update_listener(update_listener)
|
||||
|
||||
|
||||
import asyncio
|
||||
asyncio.run(bot.polling())
|
@ -0,0 +1,25 @@
|
||||
import telebot
|
||||
from telebot import types, AdvancedCustomFilter
|
||||
from telebot.callback_data import CallbackData, CallbackDataFilter
|
||||
|
||||
calendar_factory = CallbackData("year", "month", prefix="calendar")
|
||||
calendar_zoom = CallbackData("year", prefix="calendar_zoom")
|
||||
|
||||
|
||||
class CalendarCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'calendar_config'
|
||||
|
||||
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
class CalendarZoomCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'calendar_zoom_config'
|
||||
|
||||
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
def bind_filters(bot: telebot.TeleBot):
|
||||
bot.add_custom_filter(CalendarCallbackFilter())
|
||||
bot.add_custom_filter(CalendarZoomCallbackFilter())
|
@ -0,0 +1,92 @@
|
||||
import calendar
|
||||
from datetime import date, timedelta
|
||||
|
||||
from examples.callback_data_examples.advanced_calendar_example.filters import calendar_factory, calendar_zoom
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
EMTPY_FIELD = '1'
|
||||
WEEK_DAYS = [calendar.day_abbr[i] for i in range(7)]
|
||||
MONTHS = [(i, calendar.month_name[i]) for i in range(1, 13)]
|
||||
|
||||
|
||||
def generate_calendar_days(year: int, month: int):
|
||||
keyboard = InlineKeyboardMarkup(row_width=7)
|
||||
today = date.today()
|
||||
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text=date(year=year, month=month, day=1).strftime('%b %Y'),
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*[
|
||||
InlineKeyboardButton(
|
||||
text=day,
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
for day in WEEK_DAYS
|
||||
])
|
||||
|
||||
for week in calendar.Calendar().monthdayscalendar(year=year, month=month):
|
||||
week_buttons = []
|
||||
for day in week:
|
||||
day_name = ' '
|
||||
if day == today.day and today.year == year and today.month == month:
|
||||
day_name = '🔘'
|
||||
elif day != 0:
|
||||
day_name = str(day)
|
||||
week_buttons.append(
|
||||
InlineKeyboardButton(
|
||||
text=day_name,
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*week_buttons)
|
||||
|
||||
previous_date = date(year=year, month=month, day=1) - timedelta(days=1)
|
||||
next_date = date(year=year, month=month, day=1) + timedelta(days=31)
|
||||
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text='Previous month',
|
||||
callback_data=calendar_factory.new(year=previous_date.year, month=previous_date.month)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Zoom out',
|
||||
callback_data=calendar_zoom.new(year=year)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Next month',
|
||||
callback_data=calendar_factory.new(year=next_date.year, month=next_date.month)
|
||||
),
|
||||
)
|
||||
|
||||
return keyboard
|
||||
|
||||
|
||||
def generate_calendar_months(year: int):
|
||||
keyboard = InlineKeyboardMarkup(row_width=3)
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text=date(year=year, month=1, day=1).strftime('Year %Y'),
|
||||
callback_data=EMTPY_FIELD
|
||||
)
|
||||
)
|
||||
keyboard.add(*[
|
||||
InlineKeyboardButton(
|
||||
text=month,
|
||||
callback_data=calendar_factory.new(year=year, month=month_number)
|
||||
)
|
||||
for month_number, month in MONTHS
|
||||
])
|
||||
keyboard.add(
|
||||
InlineKeyboardButton(
|
||||
text='Previous year',
|
||||
callback_data=calendar_zoom.new(year=year - 1)
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Next year',
|
||||
callback_data=calendar_zoom.new(year=year + 1)
|
||||
)
|
||||
)
|
||||
return keyboard
|
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you an advanced usage of CallbackData.
|
||||
In this example calendar was implemented
|
||||
"""
|
||||
|
||||
from datetime import date
|
||||
|
||||
from examples.callback_data_examples.advanced_calendar_example.keyboards import generate_calendar_days, \
|
||||
generate_calendar_months, EMTPY_FIELD
|
||||
from filters import calendar_factory, calendar_zoom, bind_filters
|
||||
from telebot import types, TeleBot
|
||||
|
||||
API_TOKEN = ''
|
||||
bot = TeleBot(API_TOKEN)
|
||||
|
||||
|
||||
@bot.message_handler(commands='start')
|
||||
def start_command_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id,
|
||||
f"Hello {message.from_user.first_name}. This bot is an example of calendar keyboard."
|
||||
"\nPress /calendar to see it.")
|
||||
|
||||
|
||||
@bot.message_handler(commands='calendar')
|
||||
def calendar_command_handler(message: types.Message):
|
||||
now = date.today()
|
||||
bot.send_message(message.chat.id, 'Calendar', reply_markup=generate_calendar_days(year=now.year, month=now.month))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, calendar_config=calendar_factory.filter())
|
||||
def calendar_action_handler(call: types.CallbackQuery):
|
||||
callback_data: dict = calendar_factory.parse(callback_data=call.data)
|
||||
year, month = int(callback_data['year']), int(callback_data['month'])
|
||||
|
||||
bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
|
||||
reply_markup=generate_calendar_days(year=year, month=month))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, calendar_zoom_config=calendar_zoom.filter())
|
||||
def calendar_zoom_out_handler(call: types.CallbackQuery):
|
||||
callback_data: dict = calendar_zoom.parse(callback_data=call.data)
|
||||
year = int(callback_data.get('year'))
|
||||
|
||||
bot.edit_message_reply_markup(call.message.chat.id, call.message.id,
|
||||
reply_markup=generate_calendar_months(year=year))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=lambda call: call.data == EMTPY_FIELD)
|
||||
def callback_empty_field_handler(call: types.CallbackQuery):
|
||||
bot.answer_callback_query(call.id)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bind_filters(bot)
|
||||
bot.infinity_polling()
|
86
examples/callback_data_examples/simple_products_example.py
Normal file
86
examples/callback_data_examples/simple_products_example.py
Normal file
@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you how to use CallbackData
|
||||
"""
|
||||
|
||||
from telebot.callback_data import CallbackData, CallbackDataFilter
|
||||
from telebot import types, TeleBot
|
||||
from telebot.custom_filters import AdvancedCustomFilter
|
||||
|
||||
API_TOKEN = ''
|
||||
PRODUCTS = [
|
||||
{'id': '0', 'name': 'xiaomi mi 10', 'price': 400},
|
||||
{'id': '1', 'name': 'samsung s20', 'price': 800},
|
||||
{'id': '2', 'name': 'iphone 13', 'price': 1300}
|
||||
]
|
||||
|
||||
bot = TeleBot(API_TOKEN)
|
||||
products_factory = CallbackData('product_id', prefix='products')
|
||||
|
||||
|
||||
def products_keyboard():
|
||||
return types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=product['name'],
|
||||
callback_data=products_factory.new(product_id=product["id"])
|
||||
)
|
||||
]
|
||||
for product in PRODUCTS
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def back_keyboard():
|
||||
return types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text='⬅',
|
||||
callback_data='back'
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class ProductsCallbackFilter(AdvancedCustomFilter):
|
||||
key = 'config'
|
||||
|
||||
def check(self, call: types.CallbackQuery, config: CallbackDataFilter):
|
||||
return config.check(query=call)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['products'])
|
||||
def products_command_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, 'Products:', reply_markup=products_keyboard())
|
||||
|
||||
|
||||
# Only product with field - product_id = 2
|
||||
@bot.callback_query_handler(func=None, config=products_factory.filter(product_id='2'))
|
||||
def product_one_callback(call: types.CallbackQuery):
|
||||
bot.answer_callback_query(callback_query_id=call.id, text='Not available :(', show_alert=True)
|
||||
|
||||
|
||||
# Any other products
|
||||
@bot.callback_query_handler(func=None, config=products_factory.filter())
|
||||
def products_callback(call: types.CallbackQuery):
|
||||
callback_data: dict = products_factory.parse(callback_data=call.data)
|
||||
product_id = int(callback_data['product_id'])
|
||||
product = PRODUCTS[product_id]
|
||||
|
||||
text = f"Product name: {product['name']}\n" \
|
||||
f"Product price: {product['price']}"
|
||||
bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||
text=text, reply_markup=back_keyboard())
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=lambda c: c.data == 'back')
|
||||
def back_callback(call: types.CallbackQuery):
|
||||
bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
|
||||
text='Products:', reply_markup=products_keyboard())
|
||||
|
||||
|
||||
bot.add_custom_filter(ProductsCallbackFilter())
|
||||
bot.infinity_polling()
|
11
examples/chat_join_request.py
Normal file
11
examples/chat_join_request.py
Normal file
@ -0,0 +1,11 @@
|
||||
import telebot
|
||||
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
@bot.chat_join_request_handler()
|
||||
def make_some(message: telebot.types.ChatJoinRequest):
|
||||
bot.send_message(message.chat.id, 'I accepted a new user!')
|
||||
bot.approve_chat_join_request(message.chat.id, message.from_user.id)
|
||||
|
||||
bot.infinity_polling(allowed_updates=telebot.util.update_types)
|
33
examples/chat_member_example.py
Normal file
33
examples/chat_member_example.py
Normal file
@ -0,0 +1,33 @@
|
||||
import telebot
|
||||
from telebot import types,util
|
||||
|
||||
bot = telebot.TeleBot("token")
|
||||
|
||||
#chat_member_handler. When status changes, telegram gives update. check status from old_chat_member and new_chat_member.
|
||||
@bot.chat_member_handler()
|
||||
def chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
bot.send_message(message.chat.id,"Hello {name}!".format(name=new.user.first_name)) # Welcome message
|
||||
|
||||
#if bot is added to group, this handler will work
|
||||
@bot.my_chat_member_handler()
|
||||
def my_chat_m(message: types.ChatMemberUpdated):
|
||||
old = message.old_chat_member
|
||||
new = message.new_chat_member
|
||||
if new.status == "member":
|
||||
bot.send_message(message.chat.id,"Somebody added me to group") # Welcome message, if bot was added to group
|
||||
bot.leave_chat(message.chat.id)
|
||||
|
||||
#content_Type_service is:
|
||||
#'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',
|
||||
#'proximity_alert_triggered', 'video_chat_scheduled', 'video_chat_started', 'video_chat_ended',
|
||||
#'video_chat_participants_invited', 'message_auto_delete_timer_changed'
|
||||
# this handler deletes service messages
|
||||
|
||||
@bot.message_handler(content_types=util.content_type_service)
|
||||
def delall(message: types.Message):
|
||||
bot.delete_message(message.chat.id,message.message_id)
|
||||
bot.infinity_polling(allowed_updates=util.update_types)
|
33
examples/create_invite_link.py
Normal file
33
examples/create_invite_link.py
Normal file
@ -0,0 +1,33 @@
|
||||
import telebot
|
||||
from time import sleep, time
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton #Only for creating Inline Buttons, not necessary for creating Invite Links
|
||||
|
||||
Token = "api_token" #Your Bot Access Token
|
||||
Group_ID = -1234567890 #Group ID for which invite link is to be created
|
||||
|
||||
bot = telebot.TeleBot(Token, parse_mode="HTML")
|
||||
|
||||
#/start command message
|
||||
@bot.message_handler(commands=['start'])
|
||||
def startmsg(msg):
|
||||
bot.reply_to(msg, "Hey there, I'm a bot made by pyTelegramBotAPI!")
|
||||
|
||||
#Get notified of incoming members in group
|
||||
@bot.message_handler(content_types=['new_chat_members'])
|
||||
def newmember(msg):
|
||||
#Create an invite link class that contains info about the created invite link using create_chat_invite_link() with parameters
|
||||
invite = bot.create_chat_invite_link(Group_ID, member_limit=1, expire_date=int(time())+45) #Here, the link will auto-expire in 45 seconds
|
||||
InviteLink = invite.invite_link #Get the actual invite link from 'invite' class
|
||||
|
||||
mrkplink = InlineKeyboardMarkup() #Created Inline Keyboard Markup
|
||||
mrkplink.add(InlineKeyboardButton("Join our group 🚀", url=InviteLink)) #Added Invite Link to Inline Keyboard
|
||||
|
||||
bot.send_message(msg.chat.id, f"Hey there {msg.from_user.first_name}, Click the link below to join our Official Group.", reply_markup=mrkplink)
|
||||
|
||||
#This will send a message with the newly-created invite link as markup button.
|
||||
#The member limit will be 1 and expiring time will be 45 sec.
|
||||
|
||||
|
||||
|
||||
|
||||
bot.infinity_polling()
|
12
examples/custom_filters/admin_filter_example.py
Normal file
12
examples/custom_filters/admin_filter_example.py
Normal file
@ -0,0 +1,12 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
# Handler
|
||||
@bot.message_handler(chat_types=['supergroup'], is_chat_admin=True)
|
||||
def answer_for_admin(message):
|
||||
bot.send_message(message.chat.id,"hello my admin")
|
||||
|
||||
# Register filter
|
||||
bot.add_custom_filter(custom_filters.IsAdminFilter(bot))
|
||||
bot.infinity_polling()
|
125
examples/custom_filters/advanced_text_filter.py
Normal file
125
examples/custom_filters/advanced_text_filter.py
Normal file
@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This Example will show you usage of TextFilter
|
||||
In this example you will see how to use TextFilter
|
||||
with (message_handler, callback_query_handler, poll_handler)
|
||||
"""
|
||||
|
||||
from telebot import TeleBot, types
|
||||
from telebot.custom_filters import TextFilter, TextMatchFilter, IsReplyFilter
|
||||
|
||||
bot = TeleBot("")
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='hello'))
|
||||
def hello_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='hello', ignore_case=True))
|
||||
def hello_handler_ignore_case(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(contains=['good', 'bad']))
|
||||
def contains_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(contains=['good', 'bad'], ignore_case=True))
|
||||
def contains_handler_ignore_case(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(starts_with='st')) # stArk, steve, stONE
|
||||
def starts_with_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(starts_with='st', ignore_case=True)) # STark, sTeve, stONE
|
||||
def starts_with_handler_ignore_case(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(ends_with='ay')) # wednesday, SUNday, WeekDay
|
||||
def ends_with_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(ends_with='ay', ignore_case=True)) # wednesdAY, sundAy, WeekdaY
|
||||
def ends_with_handler_ignore_case(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text + ' ignore case')
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='/callback'))
|
||||
def send_callback(message: types.Message):
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[types.InlineKeyboardButton(text='callback data', callback_data='example')],
|
||||
[types.InlineKeyboardButton(text='ignore case callback data', callback_data='ExAmPLe')]
|
||||
]
|
||||
)
|
||||
bot.send_message(message.chat.id, message.text, reply_markup=keyboard)
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='example'))
|
||||
def callback_query_handler(call: types.CallbackQuery):
|
||||
bot.answer_callback_query(call.id, call.data, show_alert=True)
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='example', ignore_case=True))
|
||||
def callback_query_handler_ignore_case(call: types.CallbackQuery):
|
||||
bot.answer_callback_query(call.id, call.data + " ignore case", show_alert=True)
|
||||
|
||||
|
||||
@bot.message_handler(text=TextFilter(equals='/poll'))
|
||||
def send_poll(message: types.Message):
|
||||
bot.send_poll(message.chat.id, question='When do you prefer to work?', options=['Morning', 'Night'])
|
||||
bot.send_poll(message.chat.id, question='WHEN DO you pRefeR to worK?', options=['Morning', 'Night'])
|
||||
|
||||
|
||||
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?'))
|
||||
def poll_question_handler(poll: types.Poll):
|
||||
print(poll.question)
|
||||
|
||||
|
||||
@bot.poll_handler(func=None, text=TextFilter(equals='When do you prefer to work?', ignore_case=True))
|
||||
def poll_question_handler_ignore_case(poll: types.Poll):
|
||||
print(poll.question + ' ignore case')
|
||||
|
||||
|
||||
# either hi or contains one of (привет, salom)
|
||||
@bot.message_handler(text=TextFilter(equals="hi", contains=('привет', 'salom'), ignore_case=True))
|
||||
def multiple_patterns_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# starts with one of (mi, mea) for ex. minor, milk, meal, meat
|
||||
@bot.message_handler(text=TextFilter(starts_with=['mi', 'mea'], ignore_case=True))
|
||||
def multiple_starts_with_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# ends with one of (es, on) for ex. Jones, Davies, Johnson, Wilson
|
||||
@bot.message_handler(text=TextFilter(ends_with=['es', 'on'], ignore_case=True))
|
||||
def multiple_ends_with_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
# !ban /ban .ban !бан /бан .бан
|
||||
@bot.message_handler(is_reply=True,
|
||||
text=TextFilter(starts_with=('!', '/', '.'), ends_with=['ban', 'бан'], ignore_case=True))
|
||||
def ban_command_handler(message: types.Message):
|
||||
if len(message.text) == 4 and message.chat.type != 'private':
|
||||
try:
|
||||
bot.ban_chat_member(message.chat.id, message.reply_to_message.from_user.id)
|
||||
bot.reply_to(message.reply_to_message, 'Banned.')
|
||||
except Exception as err:
|
||||
print(err.args)
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bot.add_custom_filter(TextMatchFilter())
|
||||
bot.add_custom_filter(IsReplyFilter())
|
||||
bot.infinity_polling()
|
42
examples/custom_filters/general_custom_filters.py
Normal file
42
examples/custom_filters/general_custom_filters.py
Normal file
@ -0,0 +1,42 @@
|
||||
import telebot
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# AdvancedCustomFilter is for list, string filter values
|
||||
class MainFilter(telebot.custom_filters.AdvancedCustomFilter):
|
||||
key='text'
|
||||
@staticmethod
|
||||
def check(message, text):
|
||||
return message.text in text
|
||||
|
||||
# SimpleCustomFilter is for boolean values, such as is_admin=True
|
||||
class IsAdmin(telebot.custom_filters.SimpleCustomFilter):
|
||||
key='is_admin'
|
||||
@staticmethod
|
||||
def check(message: telebot.types.Message):
|
||||
return bot.get_chat_member(message.chat.id,message.from_user.id).status in ['administrator','creator']
|
||||
|
||||
|
||||
@bot.message_handler(is_admin=True, commands=['admin']) # Check if user is admin
|
||||
def admin_rep(message):
|
||||
bot.send_message(message.chat.id, "Hi admin")
|
||||
|
||||
@bot.message_handler(is_admin=False, commands=['admin']) # If user is not admin
|
||||
def not_admin(message):
|
||||
bot.send_message(message.chat.id, "You are not admin")
|
||||
|
||||
@bot.message_handler(text=['hi']) # Response to hi message
|
||||
def welcome_hi(message):
|
||||
bot.send_message(message.chat.id, 'You said hi')
|
||||
|
||||
@bot.message_handler(text=['bye']) # Response to bye message
|
||||
def bye_user(message):
|
||||
bot.send_message(message.chat.id, 'You said bye')
|
||||
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(MainFilter())
|
||||
bot.add_custom_filter(IsAdmin())
|
||||
|
||||
bot.infinity_polling(skip_pending=True) # Skip old updates
|
19
examples/custom_filters/id_filter_example.py
Normal file
19
examples/custom_filters/id_filter_example.py
Normal file
@ -0,0 +1,19 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('token')
|
||||
|
||||
|
||||
# Chat id can be private or supergroups.
|
||||
@bot.message_handler(chat_id=[12345678], commands=['admin']) # chat_id checks id corresponds to your list or not.
|
||||
def admin_rep(message):
|
||||
bot.send_message(message.chat.id, "You are allowed to use this command.")
|
||||
|
||||
@bot.message_handler(commands=['admin'])
|
||||
def not_admin(message):
|
||||
bot.send_message(message.chat.id, "You are not allowed to use this command")
|
||||
|
||||
# Do not forget to register
|
||||
bot.add_custom_filter(custom_filters.ChatFilter())
|
||||
|
||||
bot.infinity_polling()
|
21
examples/custom_filters/is_filter_example.py
Normal file
21
examples/custom_filters/is_filter_example.py
Normal file
@ -0,0 +1,21 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# Check if message is a reply
|
||||
@bot.message_handler(is_reply=True)
|
||||
def start_filter(message):
|
||||
bot.send_message(message.chat.id, "Looks like you replied to my message.")
|
||||
|
||||
# Check if message was forwarded
|
||||
@bot.message_handler(is_forwarded=True)
|
||||
def text_filter(message):
|
||||
bot.send_message(message.chat.id, "I do not accept forwarded messages!")
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(custom_filters.IsReplyFilter())
|
||||
bot.add_custom_filter(custom_filters.ForwardFilter())
|
||||
|
||||
bot.infinity_polling()
|
21
examples/custom_filters/text_filter_example.py
Normal file
21
examples/custom_filters/text_filter_example.py
Normal file
@ -0,0 +1,21 @@
|
||||
import telebot
|
||||
from telebot import custom_filters
|
||||
|
||||
bot = telebot.TeleBot('TOKEN')
|
||||
|
||||
|
||||
# Check if message starts with @admin tag
|
||||
@bot.message_handler(text_startswith="@admin")
|
||||
def start_filter(message):
|
||||
bot.send_message(message.chat.id, "Looks like you are calling admin, wait...")
|
||||
|
||||
# Check if text is hi or hello
|
||||
@bot.message_handler(text=['hi','hello'])
|
||||
def text_filter(message):
|
||||
bot.send_message(message.chat.id, "Hi, {name}!".format(name=message.from_user.first_name))
|
||||
|
||||
# Do not forget to register filters
|
||||
bot.add_custom_filter(custom_filters.TextMatchFilter())
|
||||
bot.add_custom_filter(custom_filters.TextStartsFilter())
|
||||
|
||||
bot.infinity_polling()
|
106
examples/custom_states.py
Normal file
106
examples/custom_states.py
Normal file
@ -0,0 +1,106 @@
|
||||
import telebot # telebot
|
||||
|
||||
from telebot import custom_filters
|
||||
from telebot.handler_backends import State, StatesGroup #States
|
||||
|
||||
# States storage
|
||||
from telebot.storage import StateMemoryStorage
|
||||
|
||||
|
||||
# Starting from version 4.4.0+, we support storages.
|
||||
# StateRedisStorage -> Redis-based storage.
|
||||
# StatePickleStorage -> Pickle-based storage.
|
||||
# For redis, you will need to install redis.
|
||||
# Pass host, db, password, or anything else,
|
||||
# if you need to change config for redis.
|
||||
# Pickle requires path. Default path is in folder .state-saves.
|
||||
# If you were using older version of pytba for pickle,
|
||||
# you need to migrate from old pickle to new by using
|
||||
# StatePickleStorage().convert_old_to_new()
|
||||
|
||||
|
||||
|
||||
# Now, you can pass storage to bot.
|
||||
state_storage = StateMemoryStorage() # you can init here another storage
|
||||
|
||||
bot = telebot.TeleBot("TOKEN",
|
||||
state_storage=state_storage)
|
||||
|
||||
|
||||
# States group.
|
||||
class MyStates(StatesGroup):
|
||||
# Just name variables differently
|
||||
name = State() # creating instances of State class is enough from now
|
||||
surname = State()
|
||||
age = State()
|
||||
|
||||
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start_ex(message):
|
||||
"""
|
||||
Start command. Here we are starting state
|
||||
"""
|
||||
bot.set_state(message.from_user.id, MyStates.name, message.chat.id)
|
||||
bot.send_message(message.chat.id, 'Hi, write me a name')
|
||||
|
||||
|
||||
# Any state
|
||||
@bot.message_handler(state="*", commands='cancel')
|
||||
def any_state(message):
|
||||
"""
|
||||
Cancel state
|
||||
"""
|
||||
bot.send_message(message.chat.id, "Your state was cancelled.")
|
||||
bot.delete_state(message.from_user.id, message.chat.id)
|
||||
|
||||
@bot.message_handler(state=MyStates.name)
|
||||
def name_get(message):
|
||||
"""
|
||||
State 1. Will process when user's state is MyStates.name.
|
||||
"""
|
||||
bot.send_message(message.chat.id, 'Now write me a surname')
|
||||
bot.set_state(message.from_user.id, MyStates.surname, message.chat.id)
|
||||
with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
data['name'] = message.text
|
||||
|
||||
|
||||
@bot.message_handler(state=MyStates.surname)
|
||||
def ask_age(message):
|
||||
"""
|
||||
State 2. Will process when user's state is MyStates.surname.
|
||||
"""
|
||||
bot.send_message(message.chat.id, "What is your age?")
|
||||
bot.set_state(message.from_user.id, MyStates.age, message.chat.id)
|
||||
with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
data['surname'] = message.text
|
||||
|
||||
# result
|
||||
@bot.message_handler(state=MyStates.age, is_digit=True)
|
||||
def ready_for_answer(message):
|
||||
"""
|
||||
State 3. Will process when user's state is MyStates.age.
|
||||
"""
|
||||
with bot.retrieve_data(message.from_user.id, message.chat.id) as data:
|
||||
msg = ("Ready, take a look:\n<b>"
|
||||
f"Name: {data['name']}\n"
|
||||
f"Surname: {data['surname']}\n"
|
||||
f"Age: {message.text}</b>")
|
||||
bot.send_message(message.chat.id, msg, parse_mode="html")
|
||||
bot.delete_state(message.from_user.id, message.chat.id)
|
||||
|
||||
#incorrect number
|
||||
@bot.message_handler(state=MyStates.age, is_digit=False)
|
||||
def age_incorrect(message):
|
||||
"""
|
||||
Wrong response for MyStates.age
|
||||
"""
|
||||
bot.send_message(message.chat.id, 'Looks like you are submitting a string in the field age. Please enter a number')
|
||||
|
||||
# register filters
|
||||
|
||||
bot.add_custom_filter(custom_filters.StateFilter(bot))
|
||||
bot.add_custom_filter(custom_filters.IsDigitFilter())
|
||||
|
||||
bot.infinity_polling(skip_pending=True)
|
@ -74,4 +74,4 @@ def send_welcome(message):
|
||||
bot.reply_to(message, reply)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -130,4 +130,4 @@ def command_default(m):
|
||||
bot.send_message(m.chat.id, "I don't understand \"" + m.text + "\"\nMaybe try the help page at /help")
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
@ -25,4 +25,4 @@ def echo_message(message):
|
||||
bot.reply_to(message, message.text)
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
||||
|
53
examples/formatting_example.py
Normal file
53
examples/formatting_example.py
Normal file
@ -0,0 +1,53 @@
|
||||
from telebot import TeleBot
|
||||
from telebot import formatting
|
||||
|
||||
bot = TeleBot('TOKEN')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start_message(message):
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
# function which connects all strings
|
||||
formatting.format_text(
|
||||
formatting.mbold(message.from_user.first_name),
|
||||
formatting.mitalic(message.from_user.first_name),
|
||||
formatting.munderline(message.from_user.first_name),
|
||||
formatting.mstrikethrough(message.from_user.first_name),
|
||||
formatting.mcode(message.from_user.first_name),
|
||||
separator=" " # separator separates all strings
|
||||
),
|
||||
parse_mode='MarkdownV2'
|
||||
)
|
||||
|
||||
# just a bold text using markdownv2
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.mbold(message.from_user.first_name),
|
||||
parse_mode='MarkdownV2'
|
||||
)
|
||||
|
||||
# html
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.format_text(
|
||||
formatting.hbold(message.from_user.first_name),
|
||||
formatting.hitalic(message.from_user.first_name),
|
||||
formatting.hunderline(message.from_user.first_name),
|
||||
formatting.hstrikethrough(message.from_user.first_name),
|
||||
formatting.hcode(message.from_user.first_name),
|
||||
# hide_link is only for html
|
||||
formatting.hide_link('https://telegra.ph/file/c158e3a6e2a26a160b253.jpg'),
|
||||
separator=" "
|
||||
),
|
||||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
# just a bold text in html
|
||||
bot.send_message(
|
||||
message.chat.id,
|
||||
formatting.hbold(message.from_user.first_name),
|
||||
parse_mode='HTML'
|
||||
)
|
||||
|
||||
bot.infinity_polling()
|
@ -61,7 +61,7 @@ def default_query(inline_query):
|
||||
|
||||
|
||||
def main_loop():
|
||||
bot.polling(True)
|
||||
bot.infinity_polling()
|
||||
while 1:
|
||||
time.sleep(3)
|
||||
|
||||
|
@ -24,4 +24,4 @@ def callback_query(call):
|
||||
def message_handler(message):
|
||||
bot.send_message(message.chat.id, "Yes/no?", reply_markup=gen_markup())
|
||||
|
||||
bot.polling(none_stop=True)
|
||||
bot.infinity_polling()
|
||||
|
35
examples/middleware/README.md
Normal file
35
examples/middleware/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Middlewares
|
||||
|
||||
## Type of middlewares in pyTelegramBotAPI
|
||||
Currently, synchronous version of pyTelegramBotAPI has two types of middlewares:
|
||||
|
||||
- Class-based middlewares
|
||||
- Function-based middlewares
|
||||
|
||||
## Purpose of middlewares
|
||||
Middlewares are designed to get update before handler's execution.
|
||||
|
||||
## Class-based middlewares
|
||||
This type of middleware has more functionality compared to function-based one.
|
||||
|
||||
Class based middleware should be instance of `telebot.handler_backends.BaseMiddleware.`
|
||||
|
||||
Each middleware should have 2 main functions:
|
||||
|
||||
`pre_process` -> is a method of class which receives update, and data.
|
||||
|
||||
Data - is a dictionary, which could be passed right to handler, and `post_process` function.
|
||||
|
||||
`post_process` -> is a function of class which receives update, data, and exception, that happened in handler. If handler was executed correctly - then exception will equal to None.
|
||||
|
||||
## Function-based middlewares
|
||||
To use function-based middleware, you should set `apihelper.ENABLE_MIDDLEWARE = True`.
|
||||
This type of middleware is created by using a decorator for middleware.
|
||||
With this type middleware, you can retrieve update immediately after update came. You should set update_types as well.
|
||||
|
||||
## Why class-based middlewares are better?
|
||||
- You can pass data between post, pre_process functions, and handler.
|
||||
- If there is an exception in handler, you will get exception parameter with exception class in post_process.
|
||||
- Has post_process -> method which works after the handler's execution.
|
||||
|
||||
## Take a look at examples for more.
|
39
examples/middleware/class_based/antiflood_middleware.py
Normal file
39
examples/middleware/class_based/antiflood_middleware.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Just a little example of middleware handlers
|
||||
|
||||
from telebot.handler_backends import BaseMiddleware
|
||||
from telebot import TeleBot
|
||||
from telebot.handler_backends import CancelUpdate
|
||||
bot = TeleBot('TOKEN',
|
||||
use_class_middlewares=True) # if you don't set it to true, middlewares won't work
|
||||
|
||||
|
||||
class SimpleMiddleware(BaseMiddleware):
|
||||
def __init__(self, limit) -> None:
|
||||
self.last_time = {}
|
||||
self.limit = limit
|
||||
self.update_types = ['message']
|
||||
# Always specify update types, otherwise middlewares won't work
|
||||
|
||||
|
||||
def pre_process(self, message, data):
|
||||
if not message.from_user.id in self.last_time:
|
||||
# User is not in a dict, so lets add and cancel this function
|
||||
self.last_time[message.from_user.id] = message.date
|
||||
return
|
||||
if message.date - self.last_time[message.from_user.id] < self.limit:
|
||||
# User is flooding
|
||||
bot.send_message(message.chat.id, 'You are making request too often')
|
||||
return CancelUpdate()
|
||||
self.last_time[message.from_user.id] = message.date
|
||||
|
||||
|
||||
def post_process(self, message, data, exception):
|
||||
pass
|
||||
|
||||
bot.setup_middleware(SimpleMiddleware(2))
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message): # you don't have to put data in handler.
|
||||
bot.send_message(message.chat.id, 'Hello!')
|
||||
|
||||
bot.infinity_polling()
|
32
examples/middleware/class_based/basic_example.py
Normal file
32
examples/middleware/class_based/basic_example.py
Normal file
@ -0,0 +1,32 @@
|
||||
from telebot import TeleBot
|
||||
from telebot.handler_backends import BaseMiddleware
|
||||
|
||||
bot = TeleBot('TOKEN', use_class_middlewares=True) # set use_class_middlewares to True!
|
||||
# otherwise, class-based middlewares won't execute.
|
||||
|
||||
# You can use this classes for cancelling update or skipping handler:
|
||||
# from telebot.handler_backends import CancelUpdate, SkipHandler
|
||||
|
||||
class Middleware(BaseMiddleware):
|
||||
def __init__(self):
|
||||
self.update_types = ['message']
|
||||
def pre_process(self, message, data):
|
||||
data['foo'] = 'Hello' # just for example
|
||||
# we edited the data. now, this data is passed to handler.
|
||||
# return SkipHandler() -> this will skip handler
|
||||
# return CancelUpdate() -> this will cancel update
|
||||
def post_process(self, message, data, exception=None):
|
||||
print(data['foo'])
|
||||
if exception: # check for exception
|
||||
print(exception)
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start(message, data: dict): # you don't have to put data parameter in handler if you don't need it.
|
||||
bot.send_message(message.chat.id, data['foo'])
|
||||
data['foo'] = 'Processed' # we changed value of data.. this data is now passed to post_process.
|
||||
|
||||
|
||||
# Setup middleware
|
||||
bot.setup_middleware(Middleware())
|
||||
|
||||
bot.infinity_polling()
|
@ -0,0 +1,121 @@
|
||||
import gettext
|
||||
import os
|
||||
import threading
|
||||
|
||||
from telebot.handler_backends import BaseMiddleware
|
||||
|
||||
try:
|
||||
from babel.support import LazyProxy
|
||||
|
||||
babel_imported = True
|
||||
except ImportError:
|
||||
babel_imported = False
|
||||
|
||||
|
||||
class I18N(BaseMiddleware):
|
||||
"""
|
||||
This middleware provides high-level tool for internationalization
|
||||
It is based on gettext util.
|
||||
"""
|
||||
|
||||
context_lang = threading.local()
|
||||
|
||||
def __init__(self, translations_path, domain_name: str):
|
||||
super().__init__()
|
||||
self.update_types = self.process_update_types()
|
||||
|
||||
self.path = translations_path
|
||||
self.domain = domain_name
|
||||
self.translations = self.find_translations()
|
||||
|
||||
@property
|
||||
def available_translations(self):
|
||||
return list(self.translations)
|
||||
|
||||
def gettext(self, text: str, lang: str = None):
|
||||
"""
|
||||
Singular translations
|
||||
"""
|
||||
|
||||
if lang is None:
|
||||
lang = self.context_lang.language
|
||||
|
||||
if lang not in self.translations:
|
||||
return text
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.gettext(text)
|
||||
|
||||
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
|
||||
"""
|
||||
Plural translations
|
||||
"""
|
||||
if lang is None:
|
||||
lang = self.context_lang.language
|
||||
|
||||
if lang not in self.translations:
|
||||
if n == 1:
|
||||
return singular
|
||||
return plural
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.ngettext(singular, plural, n)
|
||||
|
||||
def lazy_gettext(self, text: str, lang: str = None):
|
||||
if not babel_imported:
|
||||
raise RuntimeError('babel module is not imported. Check that you installed it.')
|
||||
return LazyProxy(self.gettext, text, lang, enable_cache=False)
|
||||
|
||||
def lazy_ngettext(self, singular: str, plural: str, lang: str = None, n=1):
|
||||
if not babel_imported:
|
||||
raise RuntimeError('babel module is not imported. Check that you installed it.')
|
||||
return LazyProxy(self.ngettext, singular, plural, lang, n, enable_cache=False)
|
||||
|
||||
def get_user_language(self, obj):
|
||||
"""
|
||||
You need to override this method and return user language
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def process_update_types(self) -> list:
|
||||
"""
|
||||
You need to override this method and return any update types which you want to be processed
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def pre_process(self, message, data):
|
||||
"""
|
||||
context language variable will be set each time when update from 'process_update_types' comes
|
||||
value is the result of 'get_user_language' method
|
||||
"""
|
||||
|
||||
self.context_lang.language = self.get_user_language(obj=message)
|
||||
|
||||
def post_process(self, message, data, exception):
|
||||
pass
|
||||
|
||||
def find_translations(self):
|
||||
"""
|
||||
Looks for translations with passed 'domain' in passed 'path'
|
||||
"""
|
||||
if not os.path.exists(self.path):
|
||||
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
|
||||
|
||||
result = {}
|
||||
|
||||
for name in os.listdir(self.path):
|
||||
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
|
||||
|
||||
if not os.path.isdir(translations_path):
|
||||
continue
|
||||
|
||||
po_file = os.path.join(translations_path, self.domain + '.po')
|
||||
mo_file = po_file[:-2] + 'mo'
|
||||
|
||||
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
|
||||
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
|
||||
|
||||
with open(mo_file, 'rb') as file:
|
||||
result[name] = gettext.GNUTranslations(file)
|
||||
|
||||
return result
|
34
examples/middleware/class_based/i18n_middleware/keyboards.py
Normal file
34
examples/middleware/class_based/i18n_middleware/keyboards.py
Normal file
@ -0,0 +1,34 @@
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
|
||||
|
||||
|
||||
def languages_keyboard():
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text="English", callback_data='en'),
|
||||
InlineKeyboardButton(text="Русский", callback_data='ru'),
|
||||
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def clicker_keyboard(_):
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text=_("click"), callback_data='click'),
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def menu_keyboard(_):
|
||||
keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
|
||||
keyboard.add(
|
||||
KeyboardButton(text=_("My user id")),
|
||||
KeyboardButton(text=_("My user name")),
|
||||
KeyboardButton(text=_("My first name"))
|
||||
)
|
||||
|
||||
return keyboard
|
@ -0,0 +1,81 @@
|
||||
# English translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr ""
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
#, fuzzy
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr ""
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr ""
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Hello, {user_fist_name}!\n"
|
||||
#~ "This is the example of multilanguage bot.\n"
|
||||
#~ "Available commands:\n"
|
||||
#~ "\n"
|
||||
#~ "/lang - change your language\n"
|
||||
#~ "/plural - pluralization example"
|
||||
#~ msgstr ""
|
||||
|
@ -0,0 +1,82 @@
|
||||
# Russian translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: ru\n"
|
||||
"Language-Team: ru <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "Клик"
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr "Мой user id"
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr "Мой user name"
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr "Мой first name"
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
"Привет, {user_fist_name}!\n"
|
||||
"Это пример мультиязычного бота.\n"
|
||||
"Доступные команды:\n"
|
||||
"\n"
|
||||
"/lang - изменить язык\n"
|
||||
"/plural - пример плюрализации\n"
|
||||
"/menu - Пример текстового меню"
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr "Язык был сменён"
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "У вас {number} клик"
|
||||
msgstr[1] "У вас {number} клика"
|
||||
msgstr[2] "У вас {number} кликов"
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Это кликер.\n"
|
||||
"\n"
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr "Это пример ReplyKeyboardMarkup меню в мультиязычном боте."
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr "Кажется, вы перепутали язык"
|
||||
|
@ -0,0 +1,80 @@
|
||||
# Uzbek (Latin) translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-19 18:37+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: uz_Latn\n"
|
||||
"Language-Team: uz_Latn <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "clik"
|
||||
|
||||
#: keyboards.py:29
|
||||
msgid "My user id"
|
||||
msgstr "Mani user id"
|
||||
|
||||
#: keyboards.py:30
|
||||
msgid "My user name"
|
||||
msgstr "Mani user name"
|
||||
|
||||
#: keyboards.py:31
|
||||
msgid "My first name"
|
||||
msgstr "Mani first name"
|
||||
|
||||
#: main.py:97
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example"
|
||||
msgstr ""
|
||||
"Salom, {user_fist_name}!\n"
|
||||
"Bu multilanguage bot misoli.\n"
|
||||
"Mavjud buyruqlar:\n"
|
||||
"\n"
|
||||
"/lang - tilni ozgartirish\n"
|
||||
"/plural - pluralizatsiya misoli\n"
|
||||
"/menu - text menu misoli"
|
||||
|
||||
#: main.py:121
|
||||
msgid "Language has been changed"
|
||||
msgstr "Til ozgartirildi"
|
||||
|
||||
#: main.py:130 main.py:150
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "Sizda {number}ta clik"
|
||||
msgstr[1] "Sizda {number}ta clik"
|
||||
|
||||
#: main.py:135 main.py:155
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Bu clicker.\n"
|
||||
"\n"
|
||||
|
||||
#: main.py:163
|
||||
msgid "This is ReplyKeyboardMarkup menu example in multilanguage bot."
|
||||
msgstr "Bu multilanguage bot da replykeyboardmarkup menyu misoli."
|
||||
|
||||
#: main.py:203
|
||||
msgid "Seems you confused language"
|
||||
msgstr "Tilni adashtirdiz"
|
||||
|
211
examples/middleware/class_based/i18n_middleware/main.py
Normal file
211
examples/middleware/class_based/i18n_middleware/main.py
Normal file
@ -0,0 +1,211 @@
|
||||
"""
|
||||
In this example you will learn how to adapt your bot to different languages
|
||||
Using built-in middleware I18N.
|
||||
|
||||
You need to install babel package 'https://pypi.org/project/Babel/'
|
||||
Babel provides a command-line interface for working with message catalogs
|
||||
After installing babel package you have a script called 'pybabel'
|
||||
Too see all the commands open terminal and type 'pybabel --help'
|
||||
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
|
||||
|
||||
Create a directory 'locales' where our translations will be stored
|
||||
|
||||
First we need to extract texts:
|
||||
pybabel extract -o locales/{domain_name}.pot --input-dirs .
|
||||
{domain_name}.pot - is the file where all translations are saved
|
||||
The name of this file should be the same as domain which you pass to I18N class
|
||||
In this example domain_name will be 'messages'
|
||||
|
||||
For gettext (singular texts) we use '_' alias and it works perfect
|
||||
You may also you some alias for ngettext (plural texts) but you can face with a problem that
|
||||
your plural texts are not being extracted
|
||||
That is because by default 'pybabel extract' recognizes the following keywords:
|
||||
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
|
||||
To add your own keyword you can use '-k' flag
|
||||
In this example for 'ngettext' i will assign double underscore alias '__'
|
||||
|
||||
Full command with pluralization support will look so:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
|
||||
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l en
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l ru
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
|
||||
|
||||
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
|
||||
After you translated all the texts you need to compile .po files:
|
||||
pybabel compile -d locales
|
||||
|
||||
When you delete/update your texts you also need to update them in .po files:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
pybabel update -i locales/{domain_name}.pot -d locales
|
||||
- translate
|
||||
pybabel compile -d locales
|
||||
|
||||
If you have any exceptions check:
|
||||
- you have installed babel
|
||||
- translations are ready, so you just compiled it
|
||||
- in the commands above you replaced {domain_name} to messages
|
||||
- you are writing commands from correct path in terminal
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Union
|
||||
|
||||
import keyboards
|
||||
from i18n_base_middleware import I18N
|
||||
from telebot import TeleBot
|
||||
from telebot import types, StateMemoryStorage
|
||||
from telebot.custom_filters import TextMatchFilter, TextFilter
|
||||
|
||||
class I18NMiddleware(I18N):
|
||||
|
||||
def process_update_types(self) -> list:
|
||||
"""
|
||||
Here you need to return a list of update types which you want to be processed
|
||||
"""
|
||||
return ['message', 'callback_query']
|
||||
|
||||
def get_user_language(self, obj: Union[types.Message, types.CallbackQuery]):
|
||||
"""
|
||||
This method is called when new update comes (only updates which you return in 'process_update_types' method)
|
||||
Returned language will be used in 'pre_process' method of parent class
|
||||
Returned language will be set to context language variable.
|
||||
If you need to get translation with user's actual language you don't have to pass it manually
|
||||
It will be automatically passed from context language value.
|
||||
However if you need some other language you can always pass it.
|
||||
"""
|
||||
|
||||
user_id = obj.from_user.id
|
||||
|
||||
if user_id not in users_lang:
|
||||
users_lang[user_id] = 'en'
|
||||
|
||||
return users_lang[user_id]
|
||||
|
||||
|
||||
storage = StateMemoryStorage()
|
||||
bot = TeleBot("", state_storage=storage, use_class_middlewares=True)
|
||||
|
||||
i18n = I18NMiddleware(translations_path='locales', domain_name='messages')
|
||||
_ = i18n.gettext # for singular translations
|
||||
__ = i18n.ngettext # for plural translations
|
||||
|
||||
# These are example storages, do not use it in a production development
|
||||
users_lang = {}
|
||||
users_clicks = {}
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start_handler(message: types.Message):
|
||||
text = _("Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example\n"
|
||||
"/menu - text menu example")
|
||||
|
||||
# remember don't use f string for interpolation, use .format method instead
|
||||
text = text.format(user_fist_name=message.from_user.first_name)
|
||||
bot.send_message(message.from_user.id, text)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['lang'])
|
||||
def change_language_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
|
||||
reply_markup=keyboards.languages_keyboard())
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(contains=['en', 'ru', 'uz_Latn']))
|
||||
def language_handler(call: types.CallbackQuery):
|
||||
lang = call.data
|
||||
users_lang[call.from_user.id] = lang
|
||||
|
||||
# When you changed user language, you have to pass it manually beacause it is not changed in context
|
||||
bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['plural'])
|
||||
def pluralization_handler(message: types.Message):
|
||||
if not users_clicks.get(message.from_user.id):
|
||||
users_clicks[message.from_user.id] = 0
|
||||
clicks = users_clicks[message.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
|
||||
bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=TextFilter(equals='click'))
|
||||
def click_handler(call: types.CallbackQuery):
|
||||
if not users_clicks.get(call.from_user.id):
|
||||
users_clicks[call.from_user.id] = 1
|
||||
else:
|
||||
users_clicks[call.from_user.id] += 1
|
||||
|
||||
clicks = users_clicks[call.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
|
||||
bot.edit_message_text(text, call.from_user.id, call.message.message_id,
|
||||
reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
@bot.message_handler(commands=['menu'])
|
||||
def menu_handler(message: types.Message):
|
||||
text = _("This is ReplyKeyboardMarkup menu example in multilanguage bot.")
|
||||
bot.send_message(message.chat.id, text, reply_markup=keyboards.menu_keyboard(_))
|
||||
|
||||
|
||||
# For lazy tranlations
|
||||
# lazy gettext is used when you don't know user's locale
|
||||
# It can be used for example to handle text buttons in multilanguage bot
|
||||
# The actual translation will be delayed until update comes and context language is set
|
||||
l_ = i18n.lazy_gettext
|
||||
|
||||
|
||||
# Handlers below will handle text according to user's language
|
||||
@bot.message_handler(text=l_("My user id"))
|
||||
def return_user_id(message: types.Message):
|
||||
bot.send_message(message.chat.id, str(message.from_user.id))
|
||||
|
||||
|
||||
@bot.message_handler(text=l_("My user name"))
|
||||
def return_user_id(message: types.Message):
|
||||
username = message.from_user.username
|
||||
if not username:
|
||||
username = '-'
|
||||
bot.send_message(message.chat.id, username)
|
||||
|
||||
|
||||
# You can make it case-insensitive
|
||||
@bot.message_handler(text=TextFilter(equals=l_("My first name"), ignore_case=True))
|
||||
def return_user_id(message: types.Message):
|
||||
bot.send_message(message.chat.id, message.from_user.first_name)
|
||||
|
||||
|
||||
all_menu_texts = []
|
||||
for language in i18n.available_translations:
|
||||
for menu_text in ("My user id", "My user name", "My first name"):
|
||||
all_menu_texts.append(_(menu_text, language))
|
||||
|
||||
|
||||
# When user confused language. (handles all menu buttons texts)
|
||||
@bot.message_handler(text=TextFilter(contains=all_menu_texts, ignore_case=True))
|
||||
def missed_message(message: types.Message):
|
||||
bot.send_message(message.chat.id, _("Seems you confused language"), reply_markup=keyboards.menu_keyboard(_))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bot.setup_middleware(i18n)
|
||||
bot.add_custom_filter(TextMatchFilter())
|
||||
asyncio.run(bot.infinity_polling())
|
@ -50,4 +50,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, _('hello'))
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
@ -0,0 +1,77 @@
|
||||
import gettext
|
||||
import os
|
||||
import threading
|
||||
|
||||
|
||||
class I18N:
|
||||
"""
|
||||
This class provides high-level tool for internationalization
|
||||
It is based on gettext util.
|
||||
"""
|
||||
|
||||
context_lang = threading.local()
|
||||
|
||||
def __init__(self, translations_path, domain_name: str):
|
||||
self.path = translations_path
|
||||
self.domain = domain_name
|
||||
self.translations = self.find_translations()
|
||||
|
||||
@property
|
||||
def available_translations(self):
|
||||
return list(self.translations)
|
||||
|
||||
def gettext(self, text: str, lang: str = None):
|
||||
"""
|
||||
Singular translations
|
||||
"""
|
||||
|
||||
if lang is None:
|
||||
lang = self.context_lang.language
|
||||
|
||||
if lang not in self.translations:
|
||||
return text
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.gettext(text)
|
||||
|
||||
def ngettext(self, singular: str, plural: str, lang: str = None, n=1):
|
||||
"""
|
||||
Plural translations
|
||||
"""
|
||||
if lang is None:
|
||||
lang = self.context_lang.language
|
||||
|
||||
if lang not in self.translations:
|
||||
if n == 1:
|
||||
return singular
|
||||
return plural
|
||||
|
||||
translator = self.translations[lang]
|
||||
return translator.ngettext(singular, plural, n)
|
||||
|
||||
|
||||
def find_translations(self):
|
||||
"""
|
||||
Looks for translations with passed 'domain' in passed 'path'
|
||||
"""
|
||||
if not os.path.exists(self.path):
|
||||
raise RuntimeError(f"Translations directory by path: {self.path!r} was not found")
|
||||
|
||||
result = {}
|
||||
|
||||
for name in os.listdir(self.path):
|
||||
translations_path = os.path.join(self.path, name, 'LC_MESSAGES')
|
||||
|
||||
if not os.path.isdir(translations_path):
|
||||
continue
|
||||
|
||||
po_file = os.path.join(translations_path, self.domain + '.po')
|
||||
mo_file = po_file[:-2] + 'mo'
|
||||
|
||||
if os.path.isfile(po_file) and not os.path.isfile(mo_file):
|
||||
raise FileNotFoundError(f"Translations for: {name!r} were not compiled!")
|
||||
|
||||
with open(mo_file, 'rb') as file:
|
||||
result[name] = gettext.GNUTranslations(file)
|
||||
|
||||
return result
|
23
examples/middleware/function_based/i18n_gettext/keyboards.py
Normal file
23
examples/middleware/function_based/i18n_gettext/keyboards.py
Normal file
@ -0,0 +1,23 @@
|
||||
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
|
||||
def languages_keyboard():
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text="English", callback_data='en'),
|
||||
InlineKeyboardButton(text="Русский", callback_data='ru'),
|
||||
InlineKeyboardButton(text="O'zbekcha", callback_data='uz_Latn')
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def clicker_keyboard(_):
|
||||
return InlineKeyboardMarkup(
|
||||
keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text=_("click"), callback_data='click'),
|
||||
]
|
||||
]
|
||||
)
|
@ -0,0 +1,50 @@
|
||||
# English translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
"Language-Team: en <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:78
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:102
|
||||
msgid "Language has been changed"
|
||||
msgstr ""
|
||||
|
||||
#: main.py:114
|
||||
#, fuzzy
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: main.py:120
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
@ -0,0 +1,59 @@
|
||||
# Russian translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: ru\n"
|
||||
"Language-Team: ru <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "Клик"
|
||||
|
||||
#: main.py:78
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example"
|
||||
msgstr ""
|
||||
"Привет, {user_fist_name}!\n"
|
||||
"Это пример мультиязычного бота.\n"
|
||||
"Доступные команды:\n"
|
||||
"\n"
|
||||
"/lang - изменить язык\n"
|
||||
"/plural - пример плюрализации"
|
||||
|
||||
#: main.py:102
|
||||
msgid "Language has been changed"
|
||||
msgstr "Язык был сменён"
|
||||
|
||||
#: main.py:114
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "У вас {number} клик"
|
||||
msgstr[1] "У вас {number} клика"
|
||||
msgstr[2] "У вас {number} кликов"
|
||||
|
||||
#: main.py:120
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Это кликер.\n"
|
||||
"\n"
|
@ -0,0 +1,57 @@
|
||||
# Uzbek (Latin) translations for PROJECT.
|
||||
# Copyright (C) 2022 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2022-02-18 17:54+0500\n"
|
||||
"PO-Revision-Date: 2022-02-18 16:22+0500\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: uz_Latn\n"
|
||||
"Language-Team: uz_Latn <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: keyboards.py:20
|
||||
msgid "click"
|
||||
msgstr "clik"
|
||||
|
||||
#: main.py:78
|
||||
msgid ""
|
||||
"Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n"
|
||||
"\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example"
|
||||
msgstr ""
|
||||
"Salom, {user_fist_name}!\n"
|
||||
"Bu multilanguage bot misoli.\n"
|
||||
"Mavjud buyruqlar:\n"
|
||||
"\n"
|
||||
"/lang - tilni ozgartirish\n"
|
||||
"/plural - pluralizatsiya misoli"
|
||||
|
||||
#: main.py:102
|
||||
msgid "Language has been changed"
|
||||
msgstr "Til ozgartirildi"
|
||||
|
||||
#: main.py:114
|
||||
msgid "You have {number} click"
|
||||
msgid_plural "You have {number} clicks"
|
||||
msgstr[0] "Sizda {number}ta clik"
|
||||
msgstr[1] "Sizda {number}ta clik"
|
||||
|
||||
#: main.py:120
|
||||
msgid ""
|
||||
"This is clicker.\n"
|
||||
"\n"
|
||||
msgstr ""
|
||||
"Bu clicker.\n"
|
||||
"\n"
|
134
examples/middleware/function_based/i18n_gettext/main.py
Normal file
134
examples/middleware/function_based/i18n_gettext/main.py
Normal file
@ -0,0 +1,134 @@
|
||||
"""
|
||||
In this example you will learn how to adapt your bot to different languages
|
||||
Using built-in class I18N.
|
||||
You need to install babel package 'https://pypi.org/project/Babel/'
|
||||
Babel provides a command-line interface for working with message catalogs
|
||||
After installing babel package you have a script called 'pybabel'
|
||||
Too see all the commands open terminal and type 'pybabel --help'
|
||||
Full description for pybabel commands can be found here: 'https://babel.pocoo.org/en/latest/cmdline.html'
|
||||
Create a directory 'locales' where our translations will be stored
|
||||
First we need to extract texts:
|
||||
pybabel extract -o locales/{domain_name}.pot --input-dirs .
|
||||
{domain_name}.pot - is the file where all translations are saved
|
||||
The name of this file should be the same as domain which you pass to I18N class
|
||||
In this example domain_name will be 'messages'
|
||||
For gettext (singular texts) we use '_' alias and it works perfect
|
||||
You may also you some alias for ngettext (plural texts) but you can face with a problem that
|
||||
your plural texts are not being extracted
|
||||
That is because by default 'pybabel extract' recognizes the following keywords:
|
||||
_, gettext, ngettext, ugettext, ungettext, dgettext, dngettext, N_
|
||||
To add your own keyword you can use '-k' flag
|
||||
In this example for 'ngettext' i will assign double underscore alias '__'
|
||||
Full command with pluralization support will look so:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
Then create directories with translations (get list of all locales: 'pybabel --list-locales'):
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l en
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l ru
|
||||
pybabel init -i locales/{domain_name}.pot -d locales -l uz_Latn
|
||||
Now you can translate the texts located in locales/{language}/LC_MESSAGES/{domain_name}.po
|
||||
After you translated all the texts you need to compile .po files:
|
||||
pybabel compile -d locales
|
||||
When you delete/update your texts you also need to update them in .po files:
|
||||
pybabel extract -o locales/{domain_name}.pot -k __:1,2 --input-dirs .
|
||||
pybabel update -i locales/{domain_name}.pot -d locales
|
||||
- translate
|
||||
pybabel compile -d locales
|
||||
If you have any exceptions check:
|
||||
- you have installed babel
|
||||
- translations are ready, so you just compiled it
|
||||
- in the commands above you replaced {domain_name} to messages
|
||||
- you are writing commands from correct path in terminal
|
||||
"""
|
||||
|
||||
from telebot import TeleBot, types, custom_filters
|
||||
from telebot import apihelper
|
||||
from telebot.storage.memory_storage import StateMemoryStorage
|
||||
|
||||
import keyboards
|
||||
from i18n_class import I18N
|
||||
|
||||
apihelper.ENABLE_MIDDLEWARE = True
|
||||
storage = StateMemoryStorage()
|
||||
# IMPORTANT! This example works only if polling is non-threaded.
|
||||
bot = TeleBot("", state_storage=storage, threaded=False)
|
||||
|
||||
i18n = I18N(translations_path='locales', domain_name='messages')
|
||||
_ = i18n.gettext # for singular translations
|
||||
__ = i18n.ngettext # for plural translations
|
||||
|
||||
# These are example storages, do not use it in a production development
|
||||
users_lang = {}
|
||||
users_clicks = {}
|
||||
|
||||
|
||||
@bot.middleware_handler(update_types=['message', 'callback_query'])
|
||||
def set_contex_language(bot_instance, message):
|
||||
i18n.context_lang.language = users_lang.get(message.from_user.id, 'en')
|
||||
|
||||
|
||||
@bot.message_handler(commands=['start'])
|
||||
def start_handler(message: types.Message):
|
||||
text = _("Hello, {user_fist_name}!\n"
|
||||
"This is the example of multilanguage bot.\n"
|
||||
"Available commands:\n\n"
|
||||
"/lang - change your language\n"
|
||||
"/plural - pluralization example")
|
||||
|
||||
# remember don't use f string for interpolation, use .format method instead
|
||||
text = text.format(user_fist_name=message.from_user.first_name)
|
||||
bot.send_message(message.from_user.id, text)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['lang'])
|
||||
def change_language_handler(message: types.Message):
|
||||
bot.send_message(message.chat.id, "Choose language\nВыберите язык\nTilni tanlang",
|
||||
reply_markup=keyboards.languages_keyboard())
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(contains=['en', 'ru', 'uz_Latn']))
|
||||
def language_handler(call: types.CallbackQuery):
|
||||
lang = call.data
|
||||
users_lang[call.from_user.id] = lang
|
||||
|
||||
# When you change user's language, pass language explicitly coz it's not changed in context
|
||||
bot.edit_message_text(_("Language has been changed", lang=lang), call.from_user.id, call.message.id)
|
||||
bot.delete_state(call.from_user.id)
|
||||
|
||||
|
||||
@bot.message_handler(commands=['plural'])
|
||||
def pluralization_handler(message: types.Message):
|
||||
if not users_clicks.get(message.from_user.id):
|
||||
users_clicks[message.from_user.id] = 0
|
||||
clicks = users_clicks[message.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks,
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
bot.send_message(message.chat.id, text, reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
@bot.callback_query_handler(func=None, text=custom_filters.TextFilter(equals='click'))
|
||||
def click_handler(call: types.CallbackQuery):
|
||||
if not users_clicks.get(call.from_user.id):
|
||||
users_clicks[call.from_user.id] = 1
|
||||
else:
|
||||
users_clicks[call.from_user.id] += 1
|
||||
|
||||
clicks = users_clicks[call.from_user.id]
|
||||
|
||||
text = __(
|
||||
singular="You have {number} click",
|
||||
plural="You have {number} clicks",
|
||||
n=clicks
|
||||
)
|
||||
text = _("This is clicker.\n\n") + text.format(number=clicks)
|
||||
bot.edit_message_text(text, call.from_user.id, call.message.message_id,
|
||||
reply_markup=keyboards.clicker_keyboard(_))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
bot.add_custom_filter(custom_filters.TextMatchFilter())
|
||||
bot.infinity_polling()
|
@ -58,4 +58,4 @@ def start(message):
|
||||
bot.send_message(message.chat.id, bot.session['state'])
|
||||
|
||||
|
||||
bot.polling()
|
||||
bot.infinity_polling()
|
17
examples/multibot/README.MD
Normal file
17
examples/multibot/README.MD
Normal file
@ -0,0 +1,17 @@
|
||||
You probably have seen bots which allow you to send them token of your bot and then handle updates providing some functionality for your bot.
|
||||
<br>
|
||||
This type of bots are called <b>multibots</b>. They are created using webhooks.
|
||||
<br>
|
||||
|
||||
This is the example of simple multibot.<br>
|
||||
In order to reproduce this example you need to have <b>domain and ssl connection</b>.
|
||||
<br>
|
||||
If you have, go to config.py and specify your data.
|
||||
<br>
|
||||
There is also file called <b>nginx_conf.conf</b>, we will use nginx as proxy-server and this file is example nginx.conf file.
|
||||
<br>
|
||||
Make sure that server_name and port are the same in both config and nginx_conf
|
||||
<br>
|
||||
(nginx_conf.conf IS NOT complete, you would probably use tools like certbot to add ssl connection to it)
|
||||
<br>
|
||||
Also, in this example I used dictionary as tokens storage, but in production you should use database so that you can re-set webhooks in case bot restarts.
|
6
examples/multibot/config.py
Normal file
6
examples/multibot/config.py
Normal file
@ -0,0 +1,6 @@
|
||||
MAIN_BOT_TOKEN = "your_main_bot_token"
|
||||
|
||||
WEBHOOK_HOST = "your_domain.com"
|
||||
WEBHOOK_PATH = "telegram_webhook"
|
||||
WEBAPP_HOST = "0.0.0.0"
|
||||
WEBAPP_PORT = 3500
|
14
examples/multibot/handlers.py
Normal file
14
examples/multibot/handlers.py
Normal file
@ -0,0 +1,14 @@
|
||||
from telebot import types, TeleBot
|
||||
|
||||
|
||||
def hello_handler(message: types.Message, bot: TeleBot):
|
||||
bot.send_message(message.chat.id, "Hi :)")
|
||||
|
||||
|
||||
def echo_handler(message: types.Message, bot: TeleBot):
|
||||
bot.send_message(message.chat.id, message.text)
|
||||
|
||||
|
||||
def register_handlers(bot: TeleBot):
|
||||
bot.register_message_handler(hello_handler, func=lambda message: message.text == 'Hello', pass_bot=True)
|
||||
bot.register_message_handler(echo_handler, pass_bot=True)
|
48
examples/multibot/main.py
Normal file
48
examples/multibot/main.py
Normal file
@ -0,0 +1,48 @@
|
||||
from flask import Flask
|
||||
from flask import request, abort
|
||||
from telebot import TeleBot, types, util
|
||||
from handlers import register_handlers
|
||||
|
||||
import config
|
||||
|
||||
main_bot = TeleBot(config.MAIN_BOT_TOKEN)
|
||||
app = Flask(__name__)
|
||||
tokens = {config.MAIN_BOT_TOKEN: True}
|
||||
|
||||
|
||||
@app.route(f"/{config.WEBHOOK_PATH}/<token>", methods=['POST'])
|
||||
def webhook(token: str):
|
||||
if not tokens.get(token):
|
||||
return abort(404)
|
||||
|
||||
if request.headers.get('content-type') != 'application/json':
|
||||
return abort(403)
|
||||
|
||||
json_string = request.get_data().decode('utf-8')
|
||||
update = types.Update.de_json(json_string)
|
||||
if token == main_bot.token:
|
||||
main_bot.process_new_updates([update])
|
||||
return ''
|
||||
|
||||
from_update_bot = TeleBot(token)
|
||||
register_handlers(from_update_bot)
|
||||
from_update_bot.process_new_updates([update])
|
||||
return ''
|
||||
|
||||
|
||||
@main_bot.message_handler(commands=['add_bot'])
|
||||
def add_bot(message: types.Message):
|
||||
token = util.extract_arguments(message.text)
|
||||
tokens[token] = True
|
||||
|
||||
new_bot = TeleBot(token)
|
||||
new_bot.delete_webhook()
|
||||
new_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{token}")
|
||||
|
||||
new_bot.send_message(message.chat.id, "Webhook was set.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main_bot.delete_webhook()
|
||||
main_bot.set_webhook(f"{config.WEBHOOK_HOST}/{config.WEBHOOK_PATH}/{config.MAIN_BOT_TOKEN}")
|
||||
app.run(host=config.WEBAPP_HOST, port=config.WEBAPP_PORT)
|
8
examples/multibot/nginx_conf.conf
Normal file
8
examples/multibot/nginx_conf.conf
Normal file
@ -0,0 +1,8 @@
|
||||
server {
|
||||
server_name your_domain.com;
|
||||
|
||||
location /telegram_webhook/ {
|
||||
proxy_pass http://localhost:3500;
|
||||
}
|
||||
|
||||
}
|
@ -38,21 +38,20 @@ def command_pay(message):
|
||||
"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',
|
||||
bot.send_invoice(
|
||||
message.chat.id, #chat_id
|
||||
'Working Time Machine', #title
|
||||
' 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!', #description
|
||||
'HAPPY FRIDAYS COUPON', #invoice_payload
|
||||
provider_token, #provider_token
|
||||
'usd', #currency
|
||||
prices, #prices
|
||||
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')
|
||||
start_parameter='time-machine-example')
|
||||
|
||||
|
||||
@bot.shipping_query_handler(func=lambda query: True)
|
||||
@ -78,5 +77,4 @@ def got_payment(message):
|
||||
parse_mode='Markdown')
|
||||
|
||||
|
||||
bot.skip_pending = True
|
||||
bot.polling(none_stop=True, interval=0)
|
||||
bot.infinity_polling(skip_pending = True)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user