From 9148dc5f046ba7cf1c5949c966722d45a6f8a6a8 Mon Sep 17 00:00:00 2001 From: "Kay Marquardt (Gnadelwartz)" Date: Thu, 30 May 2019 12:16:13 +0200 Subject: [PATCH] fix uppercase DIST and STANDALONE dirs --- .gitignore | 4 +- DIST/telegram-bot-bash-0.90-dev2.tar.gz | Bin 96164 -> 0 bytes DIST/telegram-bot-bash-0.90-dev2.zip | Bin 131682 -> 0 bytes DIST/telegram-bot-bash/JSON.sh/JSON.sh | 208 ----- DIST/telegram-bot-bash/LICENSE | 18 - DIST/telegram-bot-bash/README.html | 195 ----- DIST/telegram-bot-bash/README.md | 184 ---- DIST/telegram-bot-bash/README.txt | 258 ------ .../addons/antiFlood.sh.dist | 77 -- DIST/telegram-bot-bash/addons/example.sh.dist | 97 --- DIST/telegram-bot-bash/bashbot.rc.dist | 86 -- DIST/telegram-bot-bash/bashbot.sh | 784 ------------------ DIST/telegram-bot-bash/commands.sh | 117 --- DIST/telegram-bot-bash/doc/0_install.md | 91 -- DIST/telegram-bot-bash/doc/1_firstbot.md | 69 -- DIST/telegram-bot-bash/doc/2_usage.md | 225 ----- DIST/telegram-bot-bash/doc/3_advanced.md | 184 ---- DIST/telegram-bot-bash/doc/4_expert.md | 352 -------- DIST/telegram-bot-bash/doc/5_practice.md | 156 ---- DIST/telegram-bot-bash/doc/6_reference.md | 692 ---------------- DIST/telegram-bot-bash/doc/7_develop.md | 276 ------ DIST/telegram-bot-bash/examples/README.html | 51 -- DIST/telegram-bot-bash/examples/README.md | 60 -- .../examples/background-scripts/mycommands.sh | 107 --- .../background-scripts/run_diskusage.sh | 42 - .../background-scripts/run_filecontent.sh | 35 - .../background-scripts/run_filename.sh | 33 - .../examples/background-scripts/run_notify.sh | 34 - .../examples/bashbot-multi.sh | 44 - DIST/telegram-bot-bash/examples/bashbot.cron | 36 - DIST/telegram-bot-bash/examples/calc.sh | 36 - DIST/telegram-bot-bash/examples/notify.sh | 35 - DIST/telegram-bot-bash/examples/question.sh | 33 - .../examples/send-system-status/botacl | 18 - .../examples/send-system-status/mycommands.sh | 75 -- DIST/telegram-bot-bash/html/0_install.html | 126 --- DIST/telegram-bot-bash/html/1_firstbot.html | 53 -- DIST/telegram-bot-bash/html/2_usage.html | 227 ----- DIST/telegram-bot-bash/html/3_advanced.html | 164 ---- DIST/telegram-bot-bash/html/4_expert.html | 289 ------- DIST/telegram-bot-bash/html/5_practice.html | 175 ---- DIST/telegram-bot-bash/html/6_reference.html | 453 ---------- DIST/telegram-bot-bash/html/7_develop.html | 242 ------ DIST/telegram-bot-bash/html/index.html | 195 ----- DIST/telegram-bot-bash/modules/aliases.sh | 62 -- .../telegram-bot-bash/modules/answerInline.sh | 102 --- DIST/telegram-bot-bash/modules/background.sh | 142 ---- DIST/telegram-bot-bash/modules/chatMember.sh | 63 -- DIST/telegram-bot-bash/modules/jsonDB.sh | 44 - DIST/telegram-bot-bash/modules/sendMessage.sh | 237 ------ DIST/telegram-bot-bash/mycommands.sh.dist | 140 ---- README.html | 2 +- README.txt | 2 +- doc/7_develop.md | 2 +- 54 files changed, 5 insertions(+), 7427 deletions(-) delete mode 100644 DIST/telegram-bot-bash-0.90-dev2.tar.gz delete mode 100644 DIST/telegram-bot-bash-0.90-dev2.zip delete mode 100755 DIST/telegram-bot-bash/JSON.sh/JSON.sh delete mode 100644 DIST/telegram-bot-bash/LICENSE delete mode 100644 DIST/telegram-bot-bash/README.html delete mode 100644 DIST/telegram-bot-bash/README.md delete mode 100644 DIST/telegram-bot-bash/README.txt delete mode 100644 DIST/telegram-bot-bash/addons/antiFlood.sh.dist delete mode 100644 DIST/telegram-bot-bash/addons/example.sh.dist delete mode 100755 DIST/telegram-bot-bash/bashbot.rc.dist delete mode 100755 DIST/telegram-bot-bash/bashbot.sh delete mode 100644 DIST/telegram-bot-bash/commands.sh delete mode 100644 DIST/telegram-bot-bash/doc/0_install.md delete mode 100644 DIST/telegram-bot-bash/doc/1_firstbot.md delete mode 100644 DIST/telegram-bot-bash/doc/2_usage.md delete mode 100644 DIST/telegram-bot-bash/doc/3_advanced.md delete mode 100644 DIST/telegram-bot-bash/doc/4_expert.md delete mode 100644 DIST/telegram-bot-bash/doc/5_practice.md delete mode 100644 DIST/telegram-bot-bash/doc/6_reference.md delete mode 100644 DIST/telegram-bot-bash/doc/7_develop.md delete mode 100644 DIST/telegram-bot-bash/examples/README.html delete mode 100644 DIST/telegram-bot-bash/examples/README.md delete mode 100644 DIST/telegram-bot-bash/examples/background-scripts/mycommands.sh delete mode 100755 DIST/telegram-bot-bash/examples/background-scripts/run_diskusage.sh delete mode 100755 DIST/telegram-bot-bash/examples/background-scripts/run_filecontent.sh delete mode 100755 DIST/telegram-bot-bash/examples/background-scripts/run_filename.sh delete mode 100755 DIST/telegram-bot-bash/examples/background-scripts/run_notify.sh delete mode 100755 DIST/telegram-bot-bash/examples/bashbot-multi.sh delete mode 100644 DIST/telegram-bot-bash/examples/bashbot.cron delete mode 100755 DIST/telegram-bot-bash/examples/calc.sh delete mode 100755 DIST/telegram-bot-bash/examples/notify.sh delete mode 100755 DIST/telegram-bot-bash/examples/question.sh delete mode 100644 DIST/telegram-bot-bash/examples/send-system-status/botacl delete mode 100644 DIST/telegram-bot-bash/examples/send-system-status/mycommands.sh delete mode 100644 DIST/telegram-bot-bash/html/0_install.html delete mode 100644 DIST/telegram-bot-bash/html/1_firstbot.html delete mode 100644 DIST/telegram-bot-bash/html/2_usage.html delete mode 100644 DIST/telegram-bot-bash/html/3_advanced.html delete mode 100644 DIST/telegram-bot-bash/html/4_expert.html delete mode 100644 DIST/telegram-bot-bash/html/5_practice.html delete mode 100644 DIST/telegram-bot-bash/html/6_reference.html delete mode 100644 DIST/telegram-bot-bash/html/7_develop.html delete mode 100644 DIST/telegram-bot-bash/html/index.html delete mode 100644 DIST/telegram-bot-bash/modules/aliases.sh delete mode 100644 DIST/telegram-bot-bash/modules/answerInline.sh delete mode 100644 DIST/telegram-bot-bash/modules/background.sh delete mode 100644 DIST/telegram-bot-bash/modules/chatMember.sh delete mode 100644 DIST/telegram-bot-bash/modules/jsonDB.sh delete mode 100644 DIST/telegram-bot-bash/modules/sendMessage.sh delete mode 100644 DIST/telegram-bot-bash/mycommands.sh.dist diff --git a/.gitignore b/.gitignore index 6afbbb7..1912318 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ *.log /JSON.sh/ /data-bot-bash/ -/dist/ -/standalone/ +/DIST/ +/STANDALONE/ diff --git a/DIST/telegram-bot-bash-0.90-dev2.tar.gz b/DIST/telegram-bot-bash-0.90-dev2.tar.gz deleted file mode 100644 index 3b49f7063d88736b0b20c9ad6b0066117ec32123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96164 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 diff --git a/DIST/telegram-bot-bash-0.90-dev2.zip b/DIST/telegram-bot-bash-0.90-dev2.zip deleted file mode 100644 index a1619fba7ff259b6ea391155be034173aab6b9ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131682 zcmagEQ?O`FwyisD+qP}nwr$(CZQHhOd#-8Q<~o1Xy)Sj5_SqSc895?Ge2=a7?5!zC z1A{;T{A*Pk^vV7E^8X)505||HrZ%SLPKLI$M)oeWMuyH7^r|Y50KkV;cRK&tt{%_; zfFS?+|KEqgKP;4gJ|Os zcKE-UK>dM?*|9Cb><8SwY9;wUbMD`tVknYSr@qrGryGfVb33$|oY@*?C z@D4Ismpd7f>wK!`c=yLSC}2K=+c>(@7L*6@i3BZYSTkMzeWSSyW|sl1{=;EtM>9*4Ia{?h^GIfcF&}hp1Q1{=tJC;_HrKFUyB*epGf$H( zUq@T-Uf*&=JMWziyQ_J?c@7=0J$}O;+S=_9Ol{w61KoW4IPd2`H_R2+`kE+KGmLZ2 zL1SbSU6A1|{_&#`S%+rMS=c=*eq#z;sf=bKg%orsg)09k^QM@}@2VxMVwhJ1dEBU5 z+58j+c>3uPiDvc{tJSWLr#%dvCl^pLd8xX;)*o)ZvShh+Kr$R7u8{tY@DVsZw1K*!Pc7Jsmv zhMK{j@wg9usN0!OONOl75{sg#kX0Jg#=~%S3@5V@H(u=Z1i}j(7G&8|OkzX_UQZ{; zAK@5K`<*}<53F1{g`a*U#EuKdBf9`@)5z^YZyp7u*C$*A0gi>~NO(!C;DZd{1Ko!S z5F3U*kadqM|6ZxQNuP(Nz>^l9C!g7#F`LtvB^Y#>m!XXHgY+O~gK`>-;0V+E2P-eu zWZi}x=Cx`BCOFsE#G+L*Xi&e@E^Vt6#UtsY^jvnUCQZrL&r2aE3Vqym*(OT;>ru5@ z*A6fHaEJS&PP6b1^qtEoVVn_uV+2<#5LhnaGE-Aoqny}Qmz*or?WRPVDG8K#k!|`V z407IGe=65LSgv}#E~7lOxs-6Qs+BF~Hn0!^86b*Mni#g(d$#of2#y4+)~envYOot& znTg-^W;EmrfRF_(jxMx}=(%9L|CcDDXwYE99ep<;nM^W~J7_)0G5Vb2ox3L~>Ev;e z7a@Z>4Ha@9W|f4pctl@oXRuvuc;%X;(Ryhd0GWHF)yv$CTKaGvA#8n@pXtfFv)7TV zM?X7KC%t+#O{HD#ha!7gyA3uP$0iKz!c`_{P@bS+`W|pN0ALG^gK4I9o>i7C<=_>EnLLRP%y$wRF?2{f~G}6B5W!Bu)A>>0MTVLPnHe zDMscM>a-p+ak-|1FkRZ%W4u$hrZpbEC5GU2=C&&b-I+Sn-x2f8$XO>5!bRI3JGTC{ z%hN8Wbk8V0v57GV$}r?KMDYf^R3yvL@fvDe%v7jFevQ;&3uBec*iF!O8w~V%+O*qFUfoPr3qlY;I2M;inx#P zpW?^?L({8bwVjgy`=x2Hj&7r1R#VC{_ZdyT5!q2NpC+B)OhO3Eid&AJO8q4v1c*Q6 z8-jrR^C)IJJa*~WHEOW1Mr?-uf@ZhkAw((u?3?Ra-OH$k1z$%X5zQ*Q;orm(g`}wF z)ng>O6B(lOjQ(Zpqy+tg+fZJ?X)M<2mv5V19GeCgQobMihxGt^3EcM=JRPP7C{l1j zy3}%s^Yd7Ghm~4jVn+w4LX3={mbIv6UWoQ6&&Mpeofo7w;f;(N`7IP}j!VLgOfu0$ zne|T8i8NwzU=T^wOP8$O)s3=}Vw7u9ZNrdU-qlsh0cxRJ9DgGZMV-|_bOdpd5kQIx zf+8o%QxSl*{pxX_Sl+!-R5yEB-JPhwXF7K=e(WXV1UVd}Z8JDZQ$qZeJSXb0cv2;v z<*FXqd8>TC^|u!xj3g;jIoKjsIabu~#WHM#Q3E$k*18Re2AUc63=A(vM##oi#^!|@ zhghdWz?m&Q(X1%Mgd~RpV97=dl>o{bD<4dE8l;M`eChZ0K}1#pJBWoOf!RVH8zUtQ zjV)LM6X~kksZQurU}_#~w@vl6Z8wsu6L#}f`~@mL?AW}?oPRf&xA>91&2GH_pE~BL zPLOj_1k7z&re>|ol`RrFd-9>z3-anhHlrRp5K-Wmc1BEiJ<+Ms8hQsGuZdAqBXe^# z4(X9*mE=yUJgpA}=ps!3pHYN~7gB?zG2uo_-UFmGk+~Cc9W#sSYFG$uQ*Qc_&R-Bo z7kI5DfWa&~Bs>gU-&MFKK_G&;PWTV1meNy;LRlO%aRXj+MtH3jXA(9dkJmWhK+CtS z++X0QP%O37hGlCs>zWsh*haEO!v{?eeW`|%zEFB8Bf){C@WBDx5ogc=*TxmRLKz^j zq52}NH@z+0z!ib33L-b)q{e1u2{Qx-&b7b9H;-{)=@e~Rpt_O)s2bVDc0GpYLKL3? zCj=gP86+cQ37Gf}U)!9017FT7SsIVCbm%<8kta6JJ*>EFu2fV;zuby8GBQvJ~8?cccfMuEqn!Xe+;pj2yBnJQo-Jr;D<(zEn2GQYM zS+wv-0&CX8hrEPtxsAwMtK#!FPB7?r;^!&kg-L|x3{{&dQDNv4q?#nI4`dDi-%_7K zhrkW+di+}iXkV;USBSB_ede0`tIKLPQL{Ga)R84 zMFZ0I{F9*eZ%UanN$&9z5H{Cy(fhey#Xlmuvyl=qWTeMM<3J{uXpD!EuKj3n`oa}U zX4D>ry#0p+{}A!;3Vb7OXJZB%bV{*&2`+}dG>;?@`u;KBhsRO&TD>XkNW)kN?@yQ8 zjs$V7*TYP?dQl1mZFi3I#TX>6(`a;=4-tHa1&4lM*d?$Fgh_ln7{G*G$zT}uJ->^} z&`Hp+3W_-tdsNo}yb=l#DMg8^U-U|%KqN#V-_En=Q42*XtTxAHGhqd^7jQL>B|JmW z2UmX3jW~VEq!osd8dh?0+20VS2~~8mED<4rY>b8gYP1aSid9iri3UbZBmx2h4&eZH zzy{(}2=Q1UoMwVIrnT?EQ5L1kpUtmmYAvCACf3F z#RzI(ETp3fX+?bNfK7x&yIAQe&0Xwj%83Myi?-GFo3q7MYxTbZ_qinRY7N`GbbYZA z=>syeG)~YL{Pb`B%Ry_xpVKQl6-%7cqt8(E8#VLaD`cE8fE%-R8C8QYWNEn;8GZ2+Rs>QG#9kt!KAx^AU=aN zUmj=CaJE?aP!0fD;d72%^A!Ml(>X1d5hj_*n$PyfFU;qc45#yUhQp)I+p?sDJs2kp zPQUu{ey?^i=&BBnc1;y#Nul9GMSx4Hr0yVaa2b#k_uhfRnZ_n+UJ=c*X_rT;a6zh@ zV1~pXoTjg-l)Yd3Dx1%ul00)t0uQTYUP_+A(5BGWz5m(zai4g@%>7N)k1`PCjnw1> zkvjt9+dp|w9An7FQTj|^ISB_|G>kzIt?#i1KyagJ!*s^&$eJ8)cm?RId9>*-{ozP6 z$g~-w{+tnUMGK*WLdk>?ccOQx3XNV(SaeDh-Cy?NF)Nq_rx2ps7QlYU7~rde(VQq4$O~(v{k-n9^KuB^zfsk-#EQ+sp4?hjdR+vw zRHaN{ya8NFlSWe#kPAN@?Njd{1CNOVZ^HM%7#vOk4Otu@{s=?(WBZ`Z9KcYN_>bJ{ zXx}(t$h)J?_LOLHfk9?Y+z1pFOWau`UdNN%NOq8Ubnjv0$54DDFdhmMDw>>Vx-MKU zBRCJy*esP=BJxp}nu|d}DffY-_vl}=Y4?_NG2jCdfF7h(PoT#G8=0mk@a6l|X!qHL zTv+m5Y}@by&3bdg!hRJF;prZHY*`)aE>iXw>9`ZVBw75z>h82bUaZ%%egzJ5k4b$PdjZ5F5ErZ(%%5fE;@ zW0{o*88>wbIv_~WQ{2fw-DJ2;g{#%2Zf_Xh>m7`zi+9>KDcq^|D}~7R_+<+Zlzy6do?7k8 zYw&Y0eHZ-sZAqSfjQrx@%0Uln8&8ZWDA?>{i{;^xrgH$bJJ|`fyH^gO?{eP%Tt2H$FS%O^R-3q3{T^eR{Q~ftgu*sIcsODcFR)drb|6Vunn3ceus{6p$5~Pu zywa{}HDSs&G=R+m6RzTpW9jr=0DQyHqmYn{2*zCm%hzDDY0C=Qo<=pGAnv4=e3~ro za#f~vVO}ZkZy0KnZ@$5Fep6$8Ss)G)0?}LPv_Z1wdwsLzrYkB*u;p;BnXx1>$U>8Nq*o{O$qoa$z!HEu+mrxJ zfFpCIilORq!kQxE_)Ox$u0@4N?+k94j3a7H@6Up~+Z#3o^W552uzV52TAOch8)+rX zDx+oRi;`tKB?xQ@P>dVq^I<(>lCwLEg_i?okP!(VQPqPhO7g_(*J{?YtdB!hvMU!A zK|0Um&hwc%i}%YkPSg!x@B`2DK!RVu;&@7QklOrez)v@5PIsZ>*oZv!$q`_4femIs zy&I0eH}s7Tx#pH_#AL4i=&3dVL_8N)gJJ>@0(&aMArli|%bY#;nk+aH-2biJMqMF< zE~b0U50DJRyk4Fvyt$h9%+^0Khem)layA*Wm^xc!Ht5CG>>C8e--hS;Q5uy z#S07fc}7E%!ikZCJE~tPJp6fHF*Lb0^;Fpd7Dv=@+i5=W>K;szL{#M~0%NrzEjAzt zxgiz2QGnKG_QHj3O?b4#*w{j%HfnOSSJm-xCm<>oplC0EEsBPfycQ8*bK|KVB)x-J zeYE*Uyu;>@Qc3Ryx>Q%d*mwwH^B;%Mu5k^i@H^(C8_H%Rtr${E~yHbr=KLYZM`U2(qn#9J+GY56Uq_9!jj27U`Q(6jXxMc{mP*$8F%yU} zs#h5|wIB*nikYfFpuAw5l=?~*YG1AW_45r)X;;}~gX7yv`R6pE!Y zeW%|>M!>zJC6a}#lg-l{yIW^H<97)n)2Fx9T%h_D&c1*#VDi(%wBvldO>&Y zcw_3-3!tv+?$FGd36LBg?^zyy0}o%p9x;dCzGmRnM;0`}W7y`qQ5$!#(-umIRjyt4 zwrwl7h+`<^ry#0c%*YB5x3u#H=Ui?i@^+H>tIsuEVPW2RCN*QMS>H(TEv#9{2PmJv zJ_>Qq{M*dz0kkS4H_=J0hehG1F+}dh;?pg8g$9l@*zLYvEkA8x`JN?IfO*IWtWE_* zq}56SzjjrsMCFlBr1VaGzbpN+MBl*rZF$G3AJ*U*_rcGz8gbbSn5pgH4eI;~bV)gl zU!a*oJU5??UEnd4fYS1-YLqhHZch7m!nhVI+m)yx+1!VixPprwlFOk9n(jG{fe940{acW^`y@Bsk==_ms{ZU7+lMcpNwVF3ZZ`EE|9aKg zTF-tV5NyWoxdiH&TCLG-EM_8&VHwP-OCp1fFMXXY03ErAq~-R?Lr8 zgl?H?@j0#I4y}d8&|QR0EoOyj+IOq2TARNT)uJ%z)vDi+Z%>l{41EOrED!?QcCGN% zyLziWP@*YR42MDS&5g-5Hgx`o8qvDzhoIoCIr!PW{K@ryC79m>yPDAT_|1o=Azpc< znbk^sD|0`tM{j>~^TcO}!g^^KM`NpMTe|!4ioLfj-27sZB4>22@En>Mg8VB_eb|-A z{#uN`IttG-!1?sY{`}qp3$JLu z`Osf=E)RW0UB}FhTOMGQol$>w=T=$UBY}74chqP}=n#?+Yj5@tz%V$==@!bA0U7(O zU0Dej*{uyu?t>GK->;i&lSwvAzU}a@B`k3*>FQN0(wnDm96^!x&afR*w9DB$(d!d0 z95=oE{#9dSPv`Gb*?Q-;t)>qKcb}JTY@1dslmuC&CuljcGjei&vArc&W4DI&uZ4@8 z7VLVh+BIOC0W|Uh#C(YA^sNI@gY#6%T-2eb-rfLdi>Xlc(ODkSXCBqV>v=rajUg6A zRo$kGzS=+VQhfN^pC9-<4ebG^LMrS}lp_^?E{3&DZ(l489=O{ zU(I& zk4n}Fv#p@JL#TuGUit7Dnb7la_X83M0?D6yh$VTHFatTc%2ZL+V_>~s0x<+FS2yLO zl}a@qkG$bA+u-H9L?LlZxloeeq&CShBpAjE*!KQgo$qBfkkhkUTN`O3g`quF$0Lhv zNoDKFp0|5lj@~)LX@9QCxsf;+p%!|DKw=3Z>zpJcG@{nAnKOExX%~5L0VYl{8cK$R zSy1X1>*CBBY~by^m4OdECB?I~x95-WOhzsXpY&jZWb{4jyCQ_$y(TKS^U3(sV2*=& zon7AcEAm0c)|;SdJ;ovG5T&H&fuS3p%u%E(rR&i%0WUK-TSi&j-?);ma>2;o9rXK2 zg&AWqJPQZP>pEp!_PGxqZoZSuR2RKza+%<$7aCU&7*)<{`y&Lcxb8wBji$i^4Su`55*-aef2ZnDx^s8^3RBw8o@$~>iAq)RU z^AOFGi=Nu_4>rE)_RmmVyeIQl_UaRdX_=JxWa46mLdDQe@w1sB#Y#O;Ef7@=L% z_&SO-R|qwHwu&x2+9K>tdP9%tboW*F32mY)MR9iF49j0^oHl$s8s^Y&w8jGm%kfV? zT^701dy_SerZ>JLRBwuMdQ6;7?syrV>F;bk}YClHc}jqv_TPAam6R^sKDoUF2ksWYJ1b|(#`TW zCt6)4?7a@!=fP4#oe_?6=F>&(H`dP`%f^OjuyxkJQldqS(~7-W4w!rq^$f=_Ek13O zE|{G`Tv$ph|DqI6W3AeH$3@&!1S~D67S!fbq|EAopOO9N7Xnl}lushBg&4wcp+F33 zQY~s%53kf6An%E*vJnZrO;Ig%UIdTzJv!tpcNu85hno>)OEH|E9%D9SJ6zvMCaEHQ zac5}%F=Z(Ua15h&LST^?CqP0c1{Aop!QB#!6u9XF`9}OikiT~ipz}F|`d`>9CT+Kq zAYT+ATsU$WbKT0VMhlswsFF<%Eg~qUU#lk5MnH&ib9$o>IRBXiF{F z1R0#-!LaEhavENG=Rg@~{)xG?5coMn=y?FaZp>FzHcfBxEQf>FGy=x8Oirgoo2nDc3wFteV$3H>K+rFUKM` zeOs37r;G6r(mWdi5oU~aaOC3QoO)T_wUp#mpu@=bLtbdb`{L#%OX&)_-Iz%DYH-kv zby^waKI`id$Ib6mH6}BI_ZX|rH4)L72#$>GXfkxwI~&98Q}8}N%icqE9QDv zcz2@JC$|r7z?;dfS@jh{pSC`n)ERfA%<_@3a7p+hnI}c^bnNUO&^T%<4#vu+nM0DP zcd^FC&7Md%colBUrylMJTJ`2&kn5fxWfCHpe=le+1a%Dl~0Vw^cLt- ztJASWTG<~7a0D4i+{@WW8qpV zkUul#l!@ZT`yn)SD?J#w==^e$b4Fmsy5_?hb$5@Q<(5$Mkx`4Bce-xRqsGEfQPpd3 zY9+8a%ZdVt^mHKPxI3oLS9bfzlib@6UH}{U{J8F(=a{&sDbxRm2WQiQ z*)rB~S$E7G$;=z;EhmqXH?e+yP7w>wv z^e`~R!GX!aL=kb?l#C&Jd}7f)Q?VYhZjZ<*nGhGAEcO4VLq0bfI@t;a0B{5c0D${n z9dcuPTU$dr6X*ZMB2QKOvBMHa_?zt=1=$uPcCyie#zm(a_%lExlQl9L4?K5w0=GKZbm8XoI$h3nn+bWlw;wq<(j;S$R&A@ z$3-h~$dw(k#J~kMhR9Mnr8P+z;beOf${8_>3EvV`DkGLbJoN~FTD3=E=DOInKHElAjK!ndLUlj!Xwqy^NFK-v<%>?DI$;~uef`GsFG zV~ShKi|~}x@Z|+YF-WVGhKz&7NXhPc+nj)nC-sYsmid#b3bSMtS%po2yRt2f3fFkA zS-GLjNW31`y|dfFMRlC4-P9rZWpD zuqjtsVc}AgVAKl^S5~rA zQDP!$^NHInmvvlmA;pd`IMAmP8(_LeKtZo<%i$G^wtOyZMKc&o^6AckDEC1Fl0?)5 zE~G4IWb{Y^V#HL`)F}*fUs)8c%(#|S8@}k$-O$W5L{;^g;uu15p(0{K($pSh&f=l< zy7-CEsbzo|hD^GfI>LLCCC&W?CF{8m8Jr1FWn2D=pQrV`1&LaQwl?@)I)4t3jbg&j z&HdUJp@00w%%5jUu(3`932~L`?WL_p93l$fWrvl;shS%sMdd5y=YiaI^b zKZ-d3=qD*~Hx%%TP-=|qSdP9Li*zQsF^G zYh#{=B;+gfODVxzzj~JW>~YiHrbZ{b-O5+HZF1ZE_-j})dI#H~J2THY$6_1A7-$?d z++6QH60T(RN8DDf`}n&$E?ev3bgS6JhJyq8hjmK`Hv&F4xz@npWDCNXO&FU*&C;^Y zA7E12jIvNybkzY_f{C*b>Kv8#OU_Pms-@hk@R#vI*rj_D)JuR5h3*>lnp5t$tYg#;fnKcp20GsGWs4V?h!HnndUMy zFt7L~s;EORQ=QL>M%^98M)oR$jz_e>g07&!oh0VWe(U#nsQ1iLIYky;58)sjGBQ8~8@>`tzLW$&D&M-JaUOrzK9gOJ9r z&)acb*A7rP_8`8H2D`0Yx>=?Yj+7j_{pQDK1C-}R7n_-*#${`ds*PvgUj9*;3=*Nb zEvs6ode*&^4Z$4us`DD96}79ir{jT;Dh&oEKX>t#+=cfdlBb`<>9^PWxTM}1O{v<1 zd^Pz)tV)!JAf{3szwve4-h#zy{B&I7s|TG+Vd@~91xEe%^(g31LM|n}`Q=;C!526D zn5y=i{~owlJA*)O;@P9b);4igs3@Hj9e&M|I8tZR4RU9Xf@Id8kox6svJ+-HZuKXX zSkLokCPP~44^KR99#o&&Wh+06{CuEbVOdRT=XOpr^VxBbg#0A?QS%4I2W(()%>^%? z6=yi5i1nQ{)3~oFLS^5=-)Q3(Ld_(vsZIrPVe+F$m zHPM-IznckFBb+pNj62pi=iY5u582Ls-oyc{xz@`+$2x9H%EHah6X%Y;O?`3ZdDW{J%nIp=!GJTO0^JXZ0IIfH`1^HfPn5iqBbNa@Ga2ptg14 z!U`iTEo~7BMFL80zWu*$lACf~wx3AhNS(~w+}$g4b8~Rpz6_*(Thhz7XN#lv%M`Rv z=|Oq=S}AyfL$p>o+L}Y$M~0O?fXYYaOXwD(zrVP=y-?5!aOdbUp*_GofLj*F=A;s} z{Wqw)>vjt_W@AQ|TMdnL`=B*fMFZ5}DcM+<6ABft|aQ+PrO;Y((r z8I&uW7sG!Bu{$#4>Ful=Vf%Ov}Uu*~ptFjh|=kqF|H+f6gmLC?fhj_A`p zDXIQlx)Dd_@4f6~@&{QMme08Dqq@~?kYqsaRy9qIctV0z8S~;V1G3fIf?GmOHLP$G zSi4l?!UGzQg!H#tAaZ^(2EcF2Bya^Ke^O*2$+uh6rsQ5Y5w6=|b2AXKc|xnldC0bD z=xA<*;UkvyshA+j0tj3GLU)j4D}J$YaSYn>^j9A9%cPyn$cP3@(c`(FD(`pjC!;#d zPx6pwI=f`5vW}C-%g7#u4#%VyW?EUBMTr^JGVN)63!;QW>@Xa@cY??^L(jx;5y#b> zJG@ViJ_=xPZi6ySSF8%t`*!s921aK;5ZL(g`Y!YHaPe{ay1Maoba?ph4*xnXW#^KP z_x^4!GF;^2=D$51oV&Vzl8N<0EyO<(fDtmG=K+al0rr<2Fd@v(&(zhVBGhjrvtT*l zb>h?@ceq=KsKx|xMz;z~-Pg1(l}HEov{?~0uZARVtG)ltl|wD<@4s>W`*wd-#9{aK z{hi6zrSm51o!II=y`5F2H?P{>Y1IMx*~|LS&i{`5`e*s3!+QxEB}fNs&KQ=QC_(_a z;edR}>(zkd*-a?s>qlQHZuBaK3U3@&f~_qoj4f4f@fT(lsQsI_mAU|G5GZeBn2N-c0l;VFRlmdB=8-jLrfTHbUL5}@=q7WZBt_!L1v0Qj;{uc zfs60M?U9895l&sG9b+)J#kOsrf7AsB0edur1WE`mPDr^v6?N}X)B=Om`F@nn8o$>V zk$%>KD?NqWH-e{#8^-awcJpE5b_kUJMW_?2wH$yvPQu6r45s;z zu=Qc&QlpFSE!<0Ro9#Do0jE@}9@!t?9;F{$=4UQpFJ(qweK}f~d3ShsA( z^rB^R>|Hy%;e&(K?!k6w_Fx+P5%nf`l^Q*2OEOfd;DCwvB8gSjh^U>FQ7jxOu1qvB z6k^1xt41u7#KqA)Xk`eBMMct%xe~;3s#RmDGa@9?oGX=VTB}5>=~6AkL%Pi+)b zZ@8)nS}L}NNTg$*QYS`A)lwu)x8Q?k^ZDTjI5p8yg6%pPRz)FrnpnEpZoP+G=<(nFP8N=2 z5DXA;P8wWmAY6=~nk3HPJh|haQsc;(bVYRyYoVale2KDF#73-34aCo+H4GR7J41w0 z56%4GTz_t%yF=wIdsB&FlytH8DHT&MBdl}FcI$~V*Y>qhGcDL(C6^DiMsEyoW%STh z$z<{SExO}9s-q=QEBU(9N)TF8TYlar=WchG&A){HSb#v0OgEWZb%E0^Y;svld>QiV z7`FbHc$<5tYpjT=h1IqCY$rW?`iBnxHkW&MbIaq|14mrAgu)xU{oVXs{^|YMDIj#4 z$%4Iy*&Ll2(FDW4j+2T<4qliyR=m>q$68v$13WGn2F@{SM|F`qGZx|k%;m)E=SNj% zT73v6Fo6!4l|AoP3|x4gJOZ?#l}=xPfR*+eo}!t)NDK#LLdmyb7-x$#L_5|@7U-_S z*bummPXBdqc6rb+p0We(ZA-Dr0CkM9&qs8FV(AMrh^LG*H@%;L|-=}r*OdBj| zO*R{x_EUhMIWTG(OLU8u{BuCYx7KHl1n6y^${9@$Y0=13OX@d2`Dv2W%1YsuSkaAP`)AkSAt(CN4>;xmQZIHC!ZgFO@)>?Oc5iGVt}O<+VPb7Sp2b}I*;D= zB`0R~Tn!HO1Lx9f-#l5=_Bm2kv(xlM87Fm;{ELqjXp&bTS*HI;VEX z6m%a1Cyu-bMNC)VPZ+W>pi^bpu#g%fkEkoC{}NGBTVxTn*||d3pnzE9#HuTodTCBr zO)@Km6~2Yf$eebyFn%NmRmL!aMdvXSAZqn#0DXZ;jvJfnV}`q|W#JC&M4G51A6+0) zJ&$^f51=m|g=whjQmU&ZT#1vEb>BW)=9iY7d8wMHB%xM014czjcNL8^DYd@#FHMuB z@sOCJBblGIuac?wiiNJfbtGJ0d~}?Ig4BM5q+G8#*XW#QwD7BlnFR~<3QQH%b)@%| zU^&*=tjh)I#5eDw5Ix;k)mJHwb|$z3y@P>(FTJMj1Qt@vX~;M0Y6VFLa2uH_S9>ys z^4gCcy&BVxd3#lrU5(izw(ni7Al2E)-Sv`EVm$hqnbX3c{H^6uJ!%kZu`E}(at#C% z*KZMLiJq0+U22-~$d_PNRVBx56uG>%?9JJ2<3tz=@qp=TaG?i1%+54ue_7NYFcTuv zm$$+?mRrru84_%kdjMnKwS(tSs5BSQ6GiC>?G;bw_d2eD)F5qK0Bl`!2E?RE#wOGn z_m_(9b@E%TaadSNAb5wQE@p8~C7s(e5z*dE{)s$i-Z}NJ^rNbbfEWEJ zLtGGWi_)x8`q6}ZT$eKCjhNFmpHlPYS-|H?U(cK)0j_bCvW`LrS^+ZKJI1Zj3|wmF zbplt+z!j;fEbNk0wZ%T_6j5DkD?I_B(`mBGMGMsQIz?wT2iTwK^36u2Xs4IF zjl4=hSdtt1$tTy)?p`mY^r=onOUIr!;wnaWko5rT+`-6TrXis7j4?EL*^@nK)oy89#x z7sf`9=R}pes?o%X!Nym>Az+=fSApfr9v9v>W^ z?_(TMKtjB7s9yOJz*nr)CuwOJrdL-%p!=nBa)neYo4B{kR4F59dN*Au))Hm9B=&F0 z9w+j7_t$NMF|a_CHERuPd&}M_7Y)8Y{B?BU`};wy`i}i9ZKg>Q;o_Cc0}@0HPuZ(Y zjrdDb8Fq8DO+WYcw^^2O+E4$$b9;q6nDXtnf~m$~sA^?mUlj(ZMPt!QyHZf+vi>WH zec; z$$jiC)+B}EL+E%#8BtZ@V3inucWVJ2@!>e7`GG^URf{9Kfq#zIQ^pS#sWnO8^j$&t zk#{$m-!h*jtNl#$cc<}P#HeTrn9HlxN#RFYaa%whZ3^-)_+egsXr__i@BueR_o$N&B5@-{13499JbSGT_c@LXBmS=L`(UhWc)$Ky3x@1N}K*nI679KAn(w>h5; z#sweKe{x|Jt$#JJVEEp$a>O6wv0U<)lG;mHV^Py4-0<1tO z5X(W}(C_nU^c$j2n>*H>dQ>5y?9P6Op*788D8VY5nMWo_7nMevV94`|Tb@OHd#Z~~ z?bP<~`?xv3{5*a??Vi?Qy##9T1o0?Hr9}?_!Si~j))z?vFCLXKTZ$naN|$8lf(0hD zc1UG}9e*OI8E%aQU79~@fMe!?41AOL)PV+EA1zKOA24q$b3#4D&Zj5RVVSark#bzC z(W_n31vhHGBa$mGs@5QL#*1yigU(eAmvm>yX_E1o&WI6|6nmmcrpbn6gS8i(@mZ*< z?PMczg?nkRhA!h94$>qod=Y8F%)t1LuO>x##{$ZHI<_XbI}DS!q?anpV5gZ)OVpsw zq;|-*96FnQ{n_Rl7ZO3yyu=aVW$gK7!0h##%U;AT(@Igc|%aoL$&pfX^C zVm8>qZ?s-{m~}%L(|2!)qJ^Dr@?74(8B}WS*UioSC8;HjnKBIOo4B$(v95q3zK482XY6nm{WJ7nq0 zYdh}vd7q!yCHbFl3||UU&m` zGuu&68%6aH#-c8bK=!9uO-c@tV>v;Q0~~o<$HS_vOgT9l%JxOK3@^x|$E0Bv8l~`{ z-7@D|DtLlN`PBYXx11l~Vzj{2IA zkDyDU1Rj`09~mggFeefuIbK_EhG1SG?MuHra^~P+&nd^NX3G~^1DSD_xUOb6{fTp& zHIHFN=`?{2bO0FhJR|~y2!hbLhyrTc=j(OWp`};Zq>575-`I!7K6L$t&8^?NIgSbB z)iBCn9shoI_VqUcK``ulVys2@s+$kW&NBv3PZ&R@18)$Lyj0=S;tjRj(0&-A=10Xm zl=oF}gcIhQF3mX7PFGfv4B!Su-XI;ZY;oVD0vX3#kfYFqmst6tgn~ar)?3CyIbQJ) zntzGrxnJmY$2bN4bpR?T0a)SG`xgan*sUQlF&Et+#8Yxaqy*kga1L-@;ucU~7TiO5rQs}>mb zj@5exwW&_wz~OkoE%-tV3%MY^KRJctA+G+v8M>92=TDhLfGQj+~(?L z^1{J|@!R#izUu=KpLJ`Pahv-6u&<@x_SFE4yaO<=2rLlf>HEzHs<>Q9?DkSptk~V% zU5{XK%X{B37)8NQBExIh0mvZz)ScOu$vorF=Zr({K4pxVCqYE3M^#?6n{Tw5zOcA4 z_JPr@YDOYXd=LTPy2#+rSn_XTvE6WhZS_x-IJZOxqF29_z4M$YSsrr)cV7d{n@3!H+ets`E?;7@p^BQ0S+|@|aa2q1sERq7QM==mD{%{_jT9{2G4jx&Gxw^j;2C3@bwbZBpd@`G&h=K1T$uyBhW^zXP1frnw9J|vV)Ht z&a?)>INEGpp?avUh$mEe?uo{Ji*LPe(xH2t!AR2>;@J}@yo-}vN=ez>;<4}NSHXaT zL_hxWo_y4z{`s5o=a(j9m-Nh9d}Ao3LfSe!ur;<}ou~FB(-h@RK=pTDj`~Z8bOUM& z?1rQw$1UsEH|FRICOSZTV@Nsn`cJe5A2uX~N{BUY72&OO!`}d9ED`T~SoW{cWkJvv zL&o0@4=<&__fiahEgs%#forSV8iv#e&Juu9ee}y_{RN~MC^Z#)26h`OI06~1_Z(ex zZ&tkZr%^KR(|tuEw8bTZyycNxJ8287GfgvbE$^0{&-)SRzdjutwKXF$m3?}qu3%kN zht0)%;n&wYANR=K-Y%Xm_Xoll-xy^LiF4`KPGsn7zeodVpL=>qBzgo{-VzwYnzKs_ zsAVr)`Gem~hX_GuoHkP!M5rvvI;9xI9AIb?ZsxJ+Etuud2jR&GL3HE8IH9Icb~5C> zrbC87=uIB8zbZgpa(}No5uhJQg7WX*^1gbo*q!!m=CqH$E(4hkYU4=z@E?BIcSXEoJRb`n{MTfHlY1mPntqFQ}Eqg z7`RDCt0G@9mO>8Hq<`QJ{E(ASfwbnmN$uXl69-oeU#l6uEExPljY2hYRYIYbG`^59 z39@g}!gr8QR6yyU@FQ%kaIJ%|aZ(JbIUX+{KERYdMa0Osd_tWkBdo}s8S$e_6h#Y? zOXO{_1yRa9VUF2MRT-s}4R9Ev3kEHa zPs(bjMrv)uf`;S5IdbL0I=dO7R-mE6UaHhMOQrV8D$KY;v%`=bK3z(lAN3>H!a!`B zi*7FN&yOZQ>S4eMkz91mS)r2^Hm%BARF(KsQ|2TrvNb8^gubN~US`o%gRcNRw33_& zn|K0yi35cvUvJ^iDkfO2htL4to-G0Z%$R@%A(it1&K1@kz)o#hN!B6j*E(TW@VKfO z{J*x$E&-?ue@eeTcUBi4HYYvTtFQOMgz?i-CQWipSRkg#XcP^bB>yHw(^?U==hrBLzBR z%`f#R*47Da5&6lZYObvmB&v*hoA$3%^?+GsJ$iLGOfB{p20 zTo~ZOMXex!@h+vCs#Ub_`8y$sG&EQP|E1+64qh$tj`<}V4~&;UE9R1VAJ&L};=nK- zZ8k&5=$!_*wghIy{knvimc$6lvZ{u0Nl=uz30_muQBm=zjVE@%fwZ5)y8C|!JEt~L zprtvFZQHi@*tTukwrzX%*tTukwryqSF6YVllJyhTO;=Y}zwO*nV|&l6k?f~p@V9MV zEnBQYm*6q_JdiNRn`B?)detemPS<(``fV?4=GtLds>11jPBKKcib^Q}5C>H%1wmhF zEHO%}{eWyHP7u)yukgUtM%gC#$Z9QtAKA*Cf>YlXF(P_;fI~h`9+7{8Iki@u?H!x( zvMJgiUV{Y53kry;>JaPf0nE5=PlFvy!mOi)s1N|+k_aUSXQYCD&`Gu-5`w=$*&Oj4 zh(ILj=&EgFB&`$m=RFR7FiKAKPr%%-Kd2mGcTtcW2SR66wYIMwDUQWUGvqD%^+91H zTx2cVlIk@Xq&-^yJ`}03aJ=Vnp<|rGciYls6DrlJTlXQJ(2N+=L`rm8;l)x^zcQP1Km6w3e$0cCYqf&^K z8Bmj+Bhe-ZRcLcO%uOxVm@*Vb?A7vTrcL=3w8+Rc$DqfgD0vjSkVf0XgnusKl>`zD z3nU#}@5qoB>X{rU0zulm(vuDPm?5c5%4#P+y@Q)?TF-s`3bUK8Am1=O%4?_(O}PeU zKrZt8^xlH7DDJ>>*-~~qn|nYvD$Ze>rP9BLjr~5lEMs37X9|WA8lV75ScGTg-MgkA z@=eY7J-{`qN_?y2QLYM2>Qz5Yv?!bXFV9-f`%V+$fNG!k0z*FZQYf_Grjm71CFx1i zjvS;-PTOdx*Rh=fTssSsFeLSb@ zJu)TviVpp2xO3)sV6(*+r+BB(x~024iIJ`6EK>&HhpX!fgHmyz0D#$a$J`MpFyVnZ zHt6FTJUS3K;{|a_Cj9G>iVPYiAa|c} zc(ZBdi|+#Q@~B+|O`?l-UkDbK;!|5+;5#d)WpAa1o>f|DR0XK_o~>wvq5@Z>KJpXc zF`1YL^sSBBDNi^22fVgDO37A?l)UtbaF>9lLl1=3WP(?du_qp}DCPv(>3{S)uTH{@ zB@&uYOvWWtO#KARIoYZsr7q}d?EOw@_iA41$J^%Lu`kH*A#h7bS8Y+?D>aaWfnji* zr>i?D3jJLOJpV;M8t@gLVFiF3U0(L}avuq&JwU>^Ocjnj$QWUt&SOfd>AkO`E5g!7 zMfNQ2+|g$UK$m@TFnr6Hsyg{p7-v5i2yDEQjp*%Nf{HaB9&N!Fg_<++=i=se%62kl zj1%A2ELiffyvYn{L%CJUcSSMD)FAwHJvG{4snGSX$r{GmRjPDdQj`vCggW33!Cv9e z9ChMq^Z$t0Rb!M_$zcO8d+aRY zt-PyECYv1mT4Jxd@g-25q;$|=>7RY8FW3t zzF__DuZ_qk>DC&IQNAp`K4|_!3$4Anb8yqnJ=duZ!nMi=@Q-D6Ur}l zzLuB!dH{d6`@D>I9?XpaW0LJ;Daxe_kUG2}ET(krx13>f+aGO5GD#oSrW(1v-dqe_ z#TR)Vh)BL35H)YfpYUL{Xr|s*o(s)nL>>KzQMH1xb0y}95Y1l3iu1;+A`2HOHX=nC z9pC_)t>k%0nHjRSe2C-=EJ3dshzrs>+r9#*I`*EzC}_p@k4%`a*zT;<#h#$kL_#%FBFs5QACu*AUSZloFanT`vZH{c2A3}#?n9=H5kB0 zA8R6^DZxz2XfD9kwu>u>BlfZ}M2Or~+3<}04AQ@OwUY%Z`r7rJ*h{qV_RS%g!dP){ z%>(`#B`%eAJ>{h|8Qe|wPn8*cw^b8U%nEussDIyl0Jk3sw4-LV&A$z_bsN)O&oq0| zwUgX1*L%oIeQB9-aJ$ni!1!o)S&-KRyB8Gnhe=B#IE_N$c-NUr=ar3R1EvF)SFH^I zb^@>4A*-9N)MSK(Nii3wTkVyT%03YSk?u-dp#c9380BRz&9_T7`uLPx|JxL!l4T33 z=!8E}2r)&HC0RSsPH`mSpzYz*tPckRPBq(u=~|>?G2?CJ;!urJX!R~~{gx~udnmwJ z{)aqi+{45|%xw-wj~*+jxLszEOFQ!%fJ#9g^O^=31#-7|1I3pOkglGy$x?Tep`9lP z`C!pvwkn}sM%hq-5M{Npc(8%4$rk6NxFpk7)4IV zEJt_J99GFtN>i7Z(Y2JrZ*z#r+wo~@!^!ROQ5P_lu%}{r?lxTSN)c`=%tWtzxWH{K z#tWLBY>otep0$h5;xqJOiq}LL%}~&7*7H{Jo!`9y$0tu!k9z znK(|6x^Bj4%dT!#1~f`Z{#X9>WKiZnNXUo2y)IM@T@2PiF2NbLj-nrYI!-Q z){8US5^wBP@YrZ9M~03NkmIB-G|Y%H)zk3w%i+8r-{tQwAqcnF>PQ@;$`EHL3}K=@ zucTdzU%(;Sw_;8{NN@AAgS&&r!;QC9)b@?%tm`L0UYT*&X!eNqs4>?8hv+O&yr zqDi^`g4FNu002b)qfN`8XJPB)Y+!Buf10%a1!K0XW@WcIg7iJ3$Cwf`fk-pq!REr( zUZ43!))K|;)6bfT+McMM)Jfw;De}7W^O~EXb74@*uk{zy5(B6?`!$<`Yh!bBv+z=_ zW#JFF1*az$F9#2Psmc$5#NELZd+kIb*~B#Y3i)&zJ?C=&;3b-u~|sX&uU2WD;#Z}>&w3TxqlX+@D? z$6@kvGI2P4q%?V)Vh3-tl#*tOQhAqaE|!kvG0S&rnZ?Fhq^bR)tS#Kh&gzXj3)Jr7 zlq#{^rB+VLMrK0TMwP-cLOTpcsRGLpo_OC!ke%{L1adlBu`4BMoRTM_nrVqbv{$p| zKRN+V7Q0k#k#piZkx)}XfEz@3YC{CTveqCpoE+Ohbb>=& zgR7m7c0F=E2XI6nqtTwpynKeJzJpaZGzLp%oO7Is#~Sfok|s{0x<**9m<%*P9i>~GCxZFz@aUM1s{~#JV40AY1TE3TBA-i zM`Y@~-|MnRu^Pl;>RsB!6;%k?+z4CT^jm-)vH$MIUMnZ3(`dL>AHi?(|*kQ~a`EqQ?g^?O!c(BW=g zztW!geVf;KIv9-M16!a@k`4qw%D}{B&CAovisli=Rh)qT6pi^sW4 zpEZ#d(KMwan9klR(6BUDhniVC_6DnZFJIKsVYyK%0r9Z~=wNR_ zrB=Xitb#cyCkATaEEYW1NlPjyL=e2{0dbgmoIJkqM)9#~I|E^3lZ?g1W3%F@-bnSW z(r(nzf(UhbIARoVl<*CGF0eOC`vIXjcLDO$8w>A--#TONSRJP2$(CSdM-J+hl(kLK z(*;GC>zI=BK#B#A>+DsiMpa9;sK#tC<8FNUm(Y<|Z%e{GCoc_79n@Orw8Ty=S@%_v z@rv=)pb*t)$OOc&OGN7DnR^96JJGXjvv=Qo|xZ%w0ZgT^6(_; zByb8Yp}%&D&;dEr`Rw5Eepo;$J$4KaYF)#xZ~eG@yU(D!pWo*Z_+s&3<3gEp zc>F%D?i)$oVgIx8;SE0wP|Sof z!G&2#lTiLX?=Yjh z*z@W$V4~Q}m}a$IoI9n~M}5Ty$wE9p(ghJabGE|8%E8e=)J{ddPNKh~w`2-kfzXh2V~yh)s3tt*_gS4w>7epT94H7H3)FvjECItH|8_nj-v(v0DZ#DR#nLmB?Lap1 zuZuC;aH23m84;^cBn+vc7?~tV5X&kPHPcwNBc&B(pkK9EF&MpkMBcFi0Qv{%JsDx1 zJ1Vj>Y3IlIA~p!_ZFU9`Wm6FmG++jpsT)&m|Hs??q2d|rK@d0Io^}PS5|Y7DwdIJL zN>OTRW+D(}ovL*_D>qZ5*+ck@BCV5>r2!CIX2DV-h#{cYu?L<6EznL&w2Hlyr3t8K z^~#`cEhDchRay>Qb%A;;epqztim{9a;-aGUljAHffS7zJxhx-Bv!X*)%TyvekPJHw zXmpDQNTkTQb`IupUxf$RJ%o=Zb-URT@D8zvmolHWh^HeTTr#h%tscIaOP51@q#b>L6 zebyKrt-?nu#lYA;XQ&yj@vX5AhRCC)n7_}cO@BCr$*Wg0efNeoO@GkJhK9@5R3I%f zCS<>XIg9XwV@OLWEgb4)s8Kex$^9Tuh5N*cgn5)2fvP&z)0ZwinWQu2N` z+T!2uWkDV2m+t<)_X6E3wJ0<9O$**stL9o-DJmwb6Lxg}#DhDQphj)e48%(96cK{7 zOCq#(TGCy~ccGPq*63P9>$TmxK}}X|Pnzg<8jij&#R%^Ni-<8hp(b9oui)(fTH_YZ z`Vka8bws6o_#hv<*w-$~bE6y!b*xqk(T(sewtdKc+fJy;bvfI(5>lHVe4Z;UX#13!<8$SE1G1iu1jDU&br-LuY5&N*n zN?J!Q9eQ>fw5p1gzHqnrY}Ma6Rn35UP+a)*k<7-ZAQPUJZ6JJK-@mS(fPaE1XbCza zvm3@tpiro0$2iIYu;gERoS`>wJ@|fGFaDOW9Hu6eHUKqGTq!Qc{{lB*4Vh-HNlxJt zhA9N8acOP%non`F+l1xaKUZQ>+lb7kCm6!e^;bJBdz+X-)A6*GFv@}n(dVV3lHndH zgn9wPUC=?TQ@>O81>!;$R^hk<_RM=_$xEs;4X(8arXKPt%)&d-QDIy%+85q5!CH21 zdse&YQpN0&pJhEJPlpWLP!7&>!R(ADG5=O0)dOUGtE!bQ^r->PW(?Wye^M;xR8>oC^ZWyK3EBj%lyHk!^pQqp>mgF${yI#ZzMMUxp7_w|`u;Ft%`cxv z9UiI~ef*uD=OE4B6YCLQ`-G76pcfacK&ef>F!FsThcAce(HWe~1>a4lLDtblap2@W zBI#@}^uvTkzp>;zFfy}=c`u3dZ{)#NhtDV}Z0_b-QhHKcsa_R?4M7Dnxl&nayIOW4 zSRLIfJQS_War#+375Pyi0F}qjL)x$-mZNEP3%#COZx$q z7rL;(?>j)U?WA?{m?q8oy8PmNUhVFQGORH;C;X8?zS_yxbunWXIy(*w%}e$(~$oux9|PjFN1x3`uetZ+ZRq zXY;nR_hBxzayrz-Kk(YU{#YILT`A=2++OdD`raxd_I=-Odx;WGC1j7Oq<%6?eUmvM zh@>U>Viqg0-;9kmYvHiPz9mu!3a+Vt4R5U~z*#rGBundgK%;O) zJFc)slajB^4agi?@-|~xMs-K^Njz?*rVNCw4nA3!{Dvgco+O&YLw}8E)7Qy$QJ3OJ zU$NEvb9FU4YHd{Vk*%dgMo+RpkI?dSfTwmOlglzD5PPa#X*dqW5uk+07GwUKtAzS+ zU-lJHEc6(qy~Fb}e|vKGDWd9A>x*jK|2hWqQI4rgcMR1Y-|$u`qRXfL?kLpyx%u>P zarNcXD&;1&120x6ztfF@8?(anw)k4B+vAx13-q6oORM){03YVx);2K!0KxxgHZbYA zI2oAzM;`3I=&f$7O-Jn3XO2%)hGR@gtO&=<5gDG0reyK;O~s^^HBEGaO?KP z_9JJE&q4o(HAxZdK2n-QhV&8Fti$vuQA!}Vs9sJf&z(DRPYEHD328_eNSOB&oG5=m zt1;R8b=%FggLs93lbA==kPh+@7;+OxAXQ*2H=*V>iDz~J>0*t1lIF#@&q#no%y_;h zvPNhty@|RJmww!iQBY5h%7o` zQ;Lj4LJ_LUU3v@GkGBH+UyKc>G2Dv24f^VY(>-iFdNQjM^BuDlvD9cyS!Z2(;2s2K>JA zTLDB3s8M}muaHF5(&RQvQ439iS)VyXoEd3V=mrt+ zc%3(HsDnOWWJ!7$xW12DCcCX!e!1`r@-awQ=#rT!hd1MT`pGzU-?yKzTZw?MP(aiv zDsXS?CqZ<=MiB7{Lo7<<$T8d2!Lz=!NiymBwT7jbK?2k1CSC}wQ%bX(5UNVP#4&pe7M&lEng&J?|%39)4@xI8TW3C74$6wqbmlO<1)n^oqC0 zMa2a(A(2iim1$J5Moqx#Pw3(L`d-)&-K^SdQ`FL?@!-HVr}s?ikf=5=`(UQDQo0j| zOk_Ax9c75m?I&HM-7v&iP&$zt0PNAA)crLkD5;^quN8dRPg2Ze(5rh7TURL3nH=p& zv5~AE07QRSN~mEzC*}#2^p!b@gy`0(m5rjU*k1hUl;C;^BWr?|u?mXg%(AEe=LQr* zf5278ymKIB^n(G`=I>=wAdY~1ABX0F<>_M`;UWoU2IK7#056woqbnb zoHVVE00LQ7U^HkL4a(p}WE>$&@XgL#g6R{YP%0qqgn`P*hwEzr(8+Sx8r}fHGXK#- zL6nP7vK%kGl*LtQ<`|Zg6Bv&toBZnldRfgt16HtG{6w`M2!r}lN090rk(kQ8FL*~B zAtJC8Z2&oPhDoHnq3UKeSr@UG=>dNFL`hse`pRGkniLIz&@~k*1pc=0L#dM%MH^PU zFpyR)sUpDhRWjL_W9i2OM#)3rHmaa4EFGZ5Iz?uDzVq3BBuTuw{N^#Zi_|or0ChDH zHh?)@zTB{6Zwj$`#muQ~w<@`@EWMRQrVr4>eA|ZxUg4p z#40}pza`8{di%bn^x{K0t$=W-@rba4B}cAc8B4R?(VP};pM ziv!Jxr%b^qiDjHMvK-kZhn;!A#`-b8>TwO*7MKRELMKvb-330a%&syh!2`s5+z#Dt z^!B>>&S3kf$WVU+mMXWiV#Dn0wqYis3U{wBq(N&T|qcTf2JEbG7DZs0%ph0w1qLHO&qne)7r5Caa{0o>+hTWhz! z(?6VzAK2x?oT(Y#YDLEp93OrYND(!*%iK0N{yk2(gSa$`W+xK}yf?no{bA*2eLXtn z3b2B$Gj01ZSdL1)la&h?Y5KTe&VH@Slbu;7B5)RHra0@dX4wY={K{IPD91W#?i^=o z49%QvpiPH@%Ba~hL^C4&8Flabt zHLBbyDT`;MykAvxNC-xYe7p1`bZ1-pCp6x)diGT*+?=oeR3eh$FTD9az?~-xwp>rH zz(;Ed@E=yPK2*ZhP9B|rOI3(Baw%Z@&tHRXJ9p@w+~?8T$`BuUhWbDDSXc8y$1gL! zomibyg2Nyz#KlO3AmN(aAj9f>ZdS1>u%KWMrzxkVi*jgZu^qIB^8wK<;PI^~INOv6 zCsf$$S!vy!8xdce@WS>6J)74jridj_!5u-BFC{0O=L0}xxJ!K@urpG znjYw)n>E|VZH%roz{~MT7uP_ApyLMs!YJ~Fn4M*=YU8dkX~0J?)+~&sRQpsa9sK@H z7uypzkiqBc_TIU7%F3%*_x2F!2%L0;Y${u16o8)wj!1QoOvpJPYBdf{+{ zGw0{5G!Pxejw{WoWqOV0rmSgCV?>&s~4h4|+W7u`OI+w3k!gASj~?*p zqmotyfXb{_VH&N0CX{ExIU|Pu-X@h&g3ZP@Ayl7yKcQfEMxJW5+9tb==1rIP3K%`! z16G09$L4w991I~^_WV78lmf`+vf8wEF{5(sJqWkqz*uVF{7XP#jLCF!0L zESEJqI+Ki2F6KZr#T{4-RMS@gj9Bf#T~O!%S09gEt(lb zK~8IEwm}FDCW9HTJW4rMB^Z?CbUcFWaAi6w_8FNu0)8!Oh=gY#VuZ; zeSgs^0{sPMee)Z;f2;vTVHEir=+QEEPMH^1XEp7LC&MBpzZKA&3@9R!HvC;sBi#8A zjzlZ2!=fcrxyT&i_6)I{vmalQ=^OmF$C5h)>kJ*+=WbIQW3SSkIRwZ0!ZqtQsuXM) ztawA_;_LjJDG0YTss=#AT1W`t9F>gc72RgmXnPIBJ#-73-HHr`^1P$bOp^#k^WDK@INu_&UT;|pDg@H(S zWx{yR8?qT>Xt47z?a^C4D&~=htzypolg$L%5%gTMf^WBt>&Lmq(!;xH-p?gSUP#$# zHCRihVio8vLf{R@w)qfp%O{VIJPSRCuZR70{lx8*enxHT^WZTpuiN?mYCOclqN`w# z006>S000R8$Hs$2&&1u{#PL6X7XPL5nAY01-4b{0!Q&NaFBXxCLDO2ex|)J$L2p=U zX`&tvC9Q6(gUygpbwfVbZ4j5}9@jo%<43sK-l1_!3LASo0!N@Tyd_5dIe5}U?--O$V!)MAsKhTsI&G#VJMG`h( zLS9v?b@N5|*_!0(jc=E4kdO7nq}q?*qKEI6AQ2%+(qKeqx?l81D^YUD33iXKDLXk` zkULZ-3xEkO6=qB$idU+Qj`TE058u`4>Bflc<>Es|5X#oh$%73+FIUzc7oTyYN2_4BG5TO zB|LY+7sS>Dn77(zr<2G}$1A4^LxxbO2JErx8Ki@Y`2vb1$c)HpfjmSQF*onnE*$Ey^ zSIRzN_$*F7I1IaEn=(f5%xgUnNhlAR7bm;v_PH#1w-%Noc3ohTl9}93AAlx4cI3DA zS67H9G9g2=$cS>VK{;hXWX>4dGKwL~&D0VQkBGAJq>76FN!lRhB)nSJ5?FQ2rydV~t6mSLWU)c+Fpy zVZUdRJ7Pv#6p_}-T%D0bw~BJwxsf|3G@+@*I+H)lpj>jSHrm)&J$=xd&@aE&OiuCE^@$QjR74eePgSNOhO7swZ=KQ;I1=X&MB^{RdLV zbb4@n3^^95TG! z4o=K+j|g>Be3^v;D3u0ui#Y#jN=Yi4^xb(K@7ZNYZBckZ2KIM~p0p-TNWFJGN4GWc*zMh(%@jnzBU@ z6HvDi;z^BxqhK`#I?76;S4BJK(ViS7Si%?rU7C~vbwrUHQYxV$GLa?>Mbj6@%wlmN zw&We=CS-js+*1oA+FcQSVZ@b(e|RT@hN32>v;mb zI~-ZoH3ecS@%-R!i1TO)wXpt`$?oslR`BfAqK9s2 z^gpD(%7LlI)A}cJ$G|`HreL4riVBXPj%DZs&8Qq+@qyr9H%cB~gRP@)96Q@q+*jZJVc)lYkC$@Cm-jc9CryJN`)8}8 z!b1*VL6sa`8&QMcm`_-~1iOkFAsozYQ0um~36N^a#yAhc1kK7QU_pTM$v@nvT-jU( zS~Eu*}2WA>_ZQf*?2q^-~H3p+tp8f{()D2a5Ch z^oGR2KRB4?{jP0tk6NnyfF}ww0t%s3k`?^`TmkvpUL-)lrLqTWNF11BB#_+OAw5+s z3{QQk{)9+pfEg4K1e1jq@fXlLbbZOM9AogFJz9q*EXSA8WkvJ6hag<;UJg| zNG7LG?&1R;Qb6_LJunkv>>6(rImtL<3(1#g3eeh2FGSMV3$pxO$?#MD5B;QtFi71R zffgdga4H>Ma_FW=%bwwevWZG-G#nW7PeW0p3mn%oBsoR>f=k6RDFRem350uzz)5v` z6Z;5f`nk%-w2lQD9-v3YtRYGWAw_h^2-cB=a5TLU;+8Ae+rQbc^MMQiWW3*^&P!Jq zdr0JyXI3Q^=YD(oLrV0XANAznt{LX90M_ISrk^Iw4DeFl?C$eQ#cRe)hGG%(B+_f) zhNI{_{lD%NrOsc7nrbFS9KMfZl2~!6S*WyJTOJCO%TyNlT>E|fcwLyoM{S`)(2BJd1Eo@gd zp;3d`DR4O=bmg~nXs=y1I$IWFjZ%rS3lLx55JVG;szcvFr=C037%ST|5 z`r(510eY8$1rffF<(===UK1c=Doz1~_V8X-!Qab65%JT{-&T;)ZjtDmt>!3MO1X?x zl-<7I^Z~<(aRjKkKv#yDz{IIR$v;eiP=b1lX&$GGtDD>9>E+_|@ZvqyuIE%^Us^*U zkPeSq1N!X(T7DZ?f`U(b#camKM8|7qlb*&EgHkUO{!K*t_n9ANN)=+ENRKYngWK5YKx`gKC^AS@|X^sEroM$MfYh&6;rzypx* zJh_!V#yw^wga(6R^43QynZKCpSmtvZsW}{$-Ny3>qruWn8&NI>`XOsypC^fVs!=Qr zJK{$^LUg@T<-K;|iexD0v=TIp3SeVQR10);*}!i_`3@WE_#9T>)?)fA*d+eE_cK?V z(r2qfpU>vl3-c(kP}@!_*2-!Le4bvuzU~g*kZ-1Nb?|#!(6A6|9H(}1wX-9mrMH_- z@jvoO%CqgBpP%=`my3-KUfG8&r+w04qo<|l*8OI&;ZTw5-_#a!CXVOJ*3Iea?y= z`c;3<%S!pdPq(&FT5USnh*+7kBCm&EVXoSGT-LbT+Tfrq^_7u&`4hl*YCrKO5N*9` z*BUK8CyR#@#SAS5c9<)uj|JdxuvWxn{Zt!U+>7NkQWTL5T{}&tBCqwu4kNDJq0V4o zAXWx?37sXfv#u>65h@ap69C9W8)Qd)((&P z8a`7S{NRL9PikAf{oLx}k*KEWoaoE@p+Vx~-TXn3JMDf_3`SgE>gyeX874@pJ-tX5 z&pExqZz@$xu_vT6YWGFtxA5#+!IXy)r8}yLocEGC9U7y8^L!2gnvtD0p;o(fpXpLF ziHZF?i)`F>Cy4HkD@E*|H5NzgSkH$*Ch7tAWh9@XWs?y4@9Am_x?{GV27N^%iA!b` z8voD_XOJCqEr-k^DxpaYTK%L)d#x8*6{}5RZ2B>FkqZ>Bd8mc%9EIytocI{z>t^`0F@W=@d8R> zRQjS!o+WIpR|Hl5?fDn{SAjs}xZ@%8*^{Eopb#wU^GKm5ypHp=d}d%GM0nUA+zV)m zBSsjuj0LO*g$(b%#^9JV2$uU6D~?d%S8!k(cGxN|_L5}o`#wLMfJwoXr&SYY{BpV1OHJ?*fEZ7=0) zxMt}v?dEpSOff`i0X<@CTLhJd5(v0d9*l-lGEk027!?GovMZPvVG~f7=lPE*Zmf zTDJsg|5C+#E%36-=d;S#Xv+`M^SPtw_IK+odK9ZLf-J9RLTe*mO4v&Q-IFrVAI7SE z#JYZ5$%Om8D8Jmm?C`C0b8b%Qm4*B%ZSIIo?4PNnuU-4NqHsi5mOsQ?vuQ7Qh1<CnW8aStFg%9)dmYDGTU{Ri4hebh@7OACEG_Tplxi^E@QF z(XgI2yo8j3WEcxi9AbtbwUc^f$?;$d3SPIzwDK{qWYR+*4F7p3M0v%E>F~l`)}fLYVhAKLnNqJmrq5)D`c~mKw#6Dfs*L zBs(>h=2hB7x`1EllwKhyDr9CkOA-mF54~oa^K*FE%D~3=a*94ec=z_UxpaVOJOfozX&laaZ`Ni7n4C=VPbNNz+J*XU}tS#_qlZGkoIh3(P%_%6J)( zpo5j#H5Qszr_`isEmL079Mxm{s1wsRosV8QJphZ|4t`8rbc;mdNWe2HQc=@hR4;b0 z%&qG-zFRqgq}+JL<3-1e{PHkL$Eh}0j2vq6w;?4OvQJK#&bbn}n%+4_rMWO$$S!dmr!i_fcc%law6zDT=h**ICIu zZDjpqg8=7se;&7UCW!goulD!y*-K?hgBwO=l~I9ddH27q&ZRKhI%pSl=bxEUTpcrN z29Zf|Sfx#;5VexWcM&vag~IlKoZoKGuhWnzWaqVCy#<3`YR;?Q#5#2R-Qx#Y956Rs z!L*{jguK?C?)$06cfBC<9Z(|q>NsVg537@_ptlV$;DRmtSrvQ zYmf(9KeOA>oOQQdzu&^s&)CP^|D10J?C4xYq5ZR&SpQ#P`agQcCaxydcJ}|DFnx;F zr`n-t!w=>+DlRj2kkMrPGzm6a5)a;;G5e$uTsk+L8(czhOLY_tho}EI&d=+o&+>L6 z%#p*Zb=}fMO-+rP%Br=Uon7VS>$;-f*8TVXu)DJ}=S$jr&FTK2hU$gjbc41`kRju= zBI~PV(|-j)+E0WYB(m z`a7C)I&ByyE*M96v=eN*D*}vT_k`0AAjKlqzi2{;i8;9VKKW=iFRJU!h2L7elE zSCaI!i$o7zch+d|K!1=BQmF=F8ocEP zL6krVfYJfCq5yBLuy>$F`6Cv`F0kj0Dmjr3mzGw0>;VpyS`%ZixJ&GaU9In3lJ(B{ zmzGG~ zQZf#v;$p=KT3P}-$6de!$_50x+OS2uJ3wPgncaXoDXLAF0VauH{59N)4jnj<|JUCq z%`g>6XTo49YZal*AH0DM?yD^Pm`LAEjp0;JzAlF<+@RE*2jCpQ2# zx()cC;78k*J&QYHNZeEapCvyod;@U#n-xomwB+_$NmEM}$3{j-BE>6QjPIb6(={-J zsQ2)cnbQs7!w|`iJt3ikwh1mKdlr7SM%5*E!rPaJBk2xiXqk)&xH?P$$nP50tDvC| z*6OSD(<=hmo(L@xR(sD_dj6+1j^kMe_r}GP0@%vblJ*!bG5WZQ8lF2F99P>m|hU6~0 zru%Wj6=@rfC5dT^J>v>0y^0VlO*&f*0N~!%Fob*IQm2i{>oojmP~TUGM()TnDoiV_ zlbA-busMJH*?O9kbsu-wI9?1TX+%<)vW=`H4unuz5-IMExhTzkDX~qnK zW>E%jIRi0ob$QaIlA1cF!x_0SECeH?YEpaZ{K zz_HL$OoS$gdj<3ghfW*2Y!A>cE?L&-PZM-wBGP~Oe&L9^RZ4^nbW|KuuE~l{;(y`0 z!|MH@A5)0N7bs{A0!fzm@9v1d;86P?Thr6!(!tll@#p*b(NVasG+eM9xP3ONF^gerFkTViV#Mgm6>6P^`ZAM2@01*g0AX(OzcEW zPcwt?J;W-g0g-NI*oeZ}DNTCj?&@(cr{Jn`WTbl!S;fGD1e7rEL=$R8CN}{*rSh1p zl(p0^(>@s(MU7lzc6nUZaaqcl@M_F={4zH**7cQ+5*bRxQnfT>ayt1;2wrNBH|cT- zYX;hiVtHB$+iF+2W-i2Xw%Nu=s3a|kiV~z6jp7@KP&C?SkI_d_H-YT%TuffvzCD|b zP;BH;Ya7rlk_bYLaD_+k$`33yM$eWJQ6X)Rq_4U}ahFBYt}c6~ACEwlXKf~Mm;~k| z3BQ(?yQgreYQYT>kqlu`DCk1b!EG~sx)FR?-;8Wzgn=JnoCzhT*N2ze8G2tV z9SErSey@o3-<5&E$Hwt9dqewChe1BRlkxX`b4PFA&~pj2%(=WTQT}XIzkq}LarL{X zr0EFVTB6MEAadxH%V(ET$-~_zf2xi5x01sr2lenSah%nHgti z>^QM^NQE!Z-yo#8iY#=n>FD|RFZHHVc<#HU>lmx_JFl#QSarvAWSK}LY;BlF&O
aoUoes=b*|Fz_r5_(W4 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 ZTc<#*wfhIn#sUNO( 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≦c^f`sc1%thz8<*bOPLI;kb74^$3LT`kH4QyjEPYg^#lOTGe`6j zYlK~%1d_jqR;Z$B)+w?91dMdbU;|T}Y9d*v7{Y|IWtCLBa*`y&ftUVcgbvcvK8jn& zKeDzAq#w`*m`tpSt1XZtwk(lpP3jKhQWqF<*A-w>^UJ@9e?)a#8C?46kNHHh%Kf7> zt=O=L4AMXd_GW>LQi+)SI;M_U_oO;;Ct6bOz6C(XP%(;k@We^WTLf~fGF3js!FT+y zAx`SDA7|Q78Y;ayPhXp6K24`f7p7w!cHu0rS4T+-WL-KMgsnwnVPJ-6lD9}M&0MR6&q>!dna8-0X;QY6jVL-GnZ~8=+l1AlC#JuVqmqVxK z>Cn0oAg3wA%c`)X&imUfs_I`l5G0#?-Sq$}c6(oGv8!>r{aX1s@239U>`RvbtjNWO zaa|-wY~K+f8%F-qS$F}c;i-W(_0-QyLGTjsDVnZ{g}P@@sh{)c15p2K)DU_yAK+_<=E4nI#(_tgBL`M_+{ZW1`4>xtA&t~8%N zi7!g1;lQ+(wUrwxV2|6B8tM}RqR>PHAFcwQX+{T)QzgbHHP^v~y!{_5bOYAo339?S zXCxJ`E(ndLJ&SI)94R4nei)l#OTaVKYh-ys<)_fD?V6`hD=)D>=)-HAivjwqMKQ~= z;G^;NBM-9K=loPZXsUjkWmmMzi~cper?+zzI$q}sx1^h?E{&$F%*Ouuq5f2?DHp3& z$I6<-n^Y}~s6V!0Ie>QNl*4?neG5Egj-w#(g2es7v7+{prR^o5jc_aGyB9}HS{S{} zwrkGZ(urZITC_zV*hl)3vJp<>YXyfGvPGMz$75MqAG3s2VRKY$X!@kL)*j*oy5(-S z=WDS%Ef9R(ZC2bMt3AT_BK;RLo6c*RsHh)}9;bTSl_onrZc!r?ok6QNOfIVR+-gWP z3PX`SK6WZJj@seJr}a)#7^$Wh7{Gwg+W;T1P12YFwe#EPp{9>I5r{Yf<3Y@A@HXL- zS`hnUK$K&^7?3>pGk-$b`h|udZrfJ>jB|_fUhBatrVI-{cr7igdN{(b=Isj3k#ZR- z$%ExPQ%Hz0f!y@bjD^Z;Gkd!bO(bQVbO zRw=UGij*N$ap}JZOCofAvH#SQL$Q4m$l=a86FIB_I#Nf|aDtk<)`3Z9Hn}(>kXf&| zUL(8bLar9P;Qw)bLc!EZ^-m6?I0n}|FX+?MokmhmK-T1w&X#b2?}Y1*9GK|-W{}C- z-$~c1Y%gJnO=Tgk6!|N!hy}q*=$L#Cj;dV<-i@s7_i=zX<(-;EJT)!p_%x=dc4ZVXG)L~%)(lY;-ta3yoVe_LKwkz$gA(%6u#; znn*%#6U7U3!a;%Na@w8{GrXy3YIi7Oh6bn^r9rzjxC)JzHX78ioi>dMNvrQ5j2|dU z&q;f(1M5M0rJwoD;S#N?Az+kG&_|Rl<*#KhBix#t&~;P+RK!K0H+`@TJA+7lt$~}{ zyJD~)9iKe-#C&;91-P7AJ*F$fZQ54Myi=Y>vuaZdx|8fV;?&7~SgTIU?82 zlR+bfzyfXZ%(Py1P99&Q#^0+%$$G_-E0odnYS9U7$|39;FdHAxZ@5A20|Q(LiXg&N zJEu?C@QpC&D1tV|TnYlX8b`Qm)xsZ&pZn*>sgDmk{NL}5C{zQxhioIM^%=xNDs#lg zdh^a1q`j;>jvA%5c3HAvQWTVJ{qn*A8+X77{UFaycM&+;-|yYGz}Tio3QI|w)9|MB z?;uE)QV8NM3x1oor=Uw`mlC8UOw&c>Stf4TCPr^IL03C5$9i&CZqt;2&3k#i&lL`& z#Q`8|phg=WRW&2~7+djGob6B(aB5XmlnI|UDxjlaR1}tqEK_3yn6zLM3rYLcP#Pgl zfFgx7`oya@sn1jt=SrK{@@Jt$DvJb?%;o%Lv7l#rW3O z5)r+x;YID6JwsF4WK-qeN<)?_#_$!eM6bnDw&YAIL$9zxriyBEZShlF`HQ#c4z~4N zu+a2Y;?^}3xZ9hqE3&vAMaj=qIeK{(qWYXBSP*hqL4T-yOZsd?*Ysfg6~v|j7=D*K z$Zs-JdOn21vR}86ta{Wm$4{AXovPAxtWSrgKAX@9KjdHd-S!3tS zBl9@v6bBmsaoEPPX?|c&2}HAHVfR(_zBrp%fuBAr9#2srlyDdaEJ}|3wq&}#$5x@% z%0^YP4=;&2xTtD(Q`JmgsnlR##Vp+?HeMa^KMAlZyo~z+{zn@>+!wb91rGoKIRC%G zqyH}(pH0uv#MH#myPdA9EM0cf+-RNQ`-m*!ARwdDCH+rITsarpv{@#o*AuOpPE0E@~>KCMLhXYog-B99dHMX&li!a2>?t>w$E5eu-tjT}sWa=6WMA--RQG={ zc1}&2KwX+myVAy+ww;x>ZL89@ZQHhO+qP{RmG0`fn=hiL=kEN16MM(A*RwF~dRMvJ z<_)VQ5~f@{yWhNavTa=l&9=DQm$}5Wy}?vtvxfq%I>I-xwKof4W=nk#`6~Dqeed@C zgGw3#Is+}Eu4P09uu^Wqb`@{06fPw&Nr=Qb$uXGc4P(4Kmn4VW7lh72r_%2z(8mi| zu$*sN)omGb4vz*flGn00W&|L;r~u6)M2JXo17lRYhyFO69d-w#J(%1l_awL>(4N?w z=cp0(M-Z0`6ziRy*x~VE!h2zK$la@n{7?X50UF={8cqn=vj~L8@YFC_?!%Zh<3@4r z&$iXV16ZKi_8kp&_{A|KIUvZ7z5!;W=uf+y-@C@#G6SJkhs{9<-UeuvjU*Gj9Xh+P zLy=CLs0z}zY>H*R6NU8{R}IslRwFvAup>`!$)q`hU+}Npj7A9VG>D5rY;eSC!ksiw z_tny%ah$JPMh%ninVCN?afHgIL|ykJ^AbQwg|Pf}#~n3Llh_WN@nGBO*{0?_nCB$q zPeKHz5T(8uyuX%nvl}^{c-X1=n@`WC|8xhc^XU%O`~ij7Mt9ns%}=T^b9~%6L9a6d zb685R4EZIr3r?}pIqidr;h5*YeRFKr79M;EtiQIU3?%11ppYPiXo@~GLl)}68f3R) z*Q@S>xs(i9j!qvjEaszgZ|XCaXlQSmK<$G7q$|J01q5uH$hvdvl}2 zjDAt|b3|VH?Sz>@t+;5?64d`<$KC1n{PF2&uwbx-(O15*%-c$MF`@fTIeFLq;JPQ$-!Pq!>f>f$vSHRN^^2?Q-UwGF*rCv3F&I$ zdee0-=h{bQ=zmbdHBdQ;VkTJ~wd!x$^}7LP&EkI7gDjLd^ji6$UX{JedIX7Y^DXM0 zb`s&c2my82&5kY-OB2Z5l&~GH8WjkE3Yap#*7vB)Y7J`1@v6HG&UE__{=!T-_VWZP zBgGT1n8wWhv0EP#GOxZ^m3~);-4oH~LU!i>=l0x-5!e^2wrPF6(EmA~7f|E$<>PIg4zm!UfRH-bX;RZlSqgo${v*l?5zpnZUzyf`)>lPPPresXz#zV=$9P`&|w70+|FrU)(&KAZMn*E0X+it%Z!88kqe4{OYy!aeEm29^V`+ zz2*IW``}{~Iq8p#?w3^yBT3(5610vOXpxjQWpL>%ZInn03a%38*h|UEaj&5;p)9MQ z0UuUct@PK7JEk(AK8oSi55ni%GPIAe%me3Ejc(>p9mltyDP`8XS2xMyrMRE{z+J6+mO|4xV&^!D0Z&lpch9l5v*M^J}6@Sd(+Ay4XC)%zQMBnRdrKKr6?#Lsuv+p zbJk2PT|+DPhFy%yk0AP2Z5<$G584#+U9M=oT;gSzOoSmt5gbyLW`aip$M4wOuP-sy zr)Ko$RvL_hTn4?;5#ZUZSveK3bz;;NRf266J)nDZ=n?D#;)3z*xE>r)wtNI_(sR3Q z2S6pMNYf@&I%aB7KG!BIeSsB@%*{?a%l6=?_=gb6YF?n&8nsbk4apv~(X#cL0@e|l!G!@Xz zs<2AEe30QH|H18*peb#=D3L^2^0t*DoyT2F_Zvk1>(UIU4Mm?$Wmd{c+lXu_ih&t9 zI4B^z&VlwLPqBOxI7+#=Jy5+2DH(1umn>gV#RErVU%7kt)6U7xtLx{>f5jAs`3c0a z?e;XDSUtvFDMvaDL*I#q95%sT`g=!BU# zCKWL$G{pTN){Mi!3J`c`Iie(k36;(WMO}qN5H+8suF)O=O6q54e9B;a);nK{lJAXd zHxMnyD23YqKLhSwNQ7V*Gm?&x$pB700ZmH&IwUp*&(l(bQcl67v)5rvh~(qo^ywAr zdI88}tg>|v`?FC}MayhaY~t$<2FJC^A3g*+v+G|U@~w>)S85^yEa}BdkM=}hQ3qCT z{X3K3xoOipm}DRzs%_GyIr`Y4Q!FDlp?n1CQzfwRg40wGQsT!pR zc4e7dx1$Pa1BZMA23y0!Llkf$fu??3$cIa(ii+8GoX2ZhLLrZ~a2g%_(B;En6%N6d zzqtuh!OS@M_*R{)x0sMR((>yKy)bhOCY6mNPf;u7q8#ZUFM$B@63Z);D)ZSL><`^P znlH3}<;Sp;d_IyoO}T;eq=qS9m#LY7+{Y6bN8?W;V`Ai;`Qa7k{cQjt`cImHrg57l zvnno`-Tj|zbe{_T5qZQ1O2`qhqTDht6tS^{q6~Bt;Au13w!*;kK_pMJyFb(;I|B>$ znc+8WAH6?9_PlP9D6{Tz)Lp3R=7FE>fKbKoMI8y!dc&pyWmrWxL)c13-Bw#QrDKYV65{Mv6$?PI%Z|ZHK17rQJ(O|C<{eC~}axp$09)Bep(w#l&>DPQv?_rA;X&fCis~nEqszEVyTnK&zv=Ztb7DYVcTjyFac>>IVooDwC#7m#+`WMpL?=)Chgzz9bo;YF(qreRYTqAS$ z#QCWXYvA|x2lk+D8x=R|#-t+pCEb zHbEIdeL!rdxNpqtZ!?~|PAzV0hkKHJ^|*V)QP*qaO&;725Ke_vWo)`|w;rAEXVSHr z9T*&1fOU@uFeqI$#~_d9O_tT~eL@-#hqYE!2x2_^e7}ko@jDyOc`aMFFg*}dkyhp; zLe1=yw<&zp#SA|Tyr*-L$C9sNL=)`P%#a^_AHU0;f1Y0Dm!Q$l=t-60?a|}UvOyRHuHdRm%Yez7jS>C1d>e-5}f&nwd-FWyP3 zH>L;*UKdT$k@K`=iez@Q+@aWeRg9 zGSVG#t4^JUm?xx3)Ol%eBPAQBv=6qy)ByB)J$k~#uzG?SES-W`)ruHu#*uh8e(wG7 zy1nf0s6AVJy&S&YpGEa;yVj)Eo&xaX2G%9*vDEB-_&O9&oww203CF=R4U6*PEP}4U zFd~$TFjrx`ZlKs?zJ>-hx`PZGU=6ck9I_T%u;_SEnJ8tk4rkut$+e>}>_qFqGAx;! zBjSUtm#5anu~;wvee|lM_R%RhE{#DwURKn(l+mBsNN#GRye%57n>t~a!;0D!Lt+em zA*aF@z^-H!>zK+h-5lY^5!+FoNY%c3f`m2Leq{B`H>H?*V(z8IXze4QD0c`~#M0rL zSiP+DjAW>=8%?$_m&s`@8f5)u5UK%@0jL3k;JNNYrp!PE{Or>A_6yx^g9ZXDDeLHG z1yh<=Cb2A+kS*oPZ%;_bKhHMtz!ha**T&T9h0Rpw>+B{iHdcIYe_Ld2sirhCv8Kn- zs$2iy?<$Z+5+A8WMyDEPFrj`8e+DS0VAYbgwWJ(wFpXUPV6~<)bwFpPI;`EjHWG}x zDjq}SzN)A>su;hbdoK(9_QDzD(m86FV71aly0n9IMRCG)hyRs2E;95&sems#Nw3!X zn)O=R#Ar@+XJ=z6FI~P3E5wDel!Dnj4sNil3jQP-SG2KTtv*ZY+SbL(>(=b#3A-Gw zJic7O6m=kp%@6uuMM=uKA9)1hFUYw7Lysq)CWMg*-rg{)_n_DE<@E%0KMc2{}Q zxAo{P3O-h>-S&rWP)WDDk~GDyCt~-`X9wG@P*N*&!)q z?sZ=er@sd-t@b2>u63Zu0cu5XFwin2@@OJhQ*5#a4;Wty3>=YN%xOgQ01y}z-{|=n zo3pB^>H0~pjJZQ@A-lL|6=`_Qj~}r(x8%BcYY7p?71mDdr5kQ<{LMA1ZxffQ$Ct(m zw9ljD6O{(@#mdoaVC+pK`l3jsK8CWB87K`gx8VfwuyVn|SP1;(B~mI-HGUzu6mZyY zL4zj{yy#1abep+^v+ualM5!v>C#^#Z}Gxoj?OIVnkfw{8!837UK3Aih=t~h zb8DFAJ$-x>PhrLWka>Tj0*Q43^CZk0eNJ!S)wSzHd@!SG2@&(Xjb@f4Q3_|~^UEFX z&Pbf?GUvVjSewAZ_=h3G7hE6-VSs~@W2D7R3Jf!V%~ki$5#>>zR>vZY{GJfkFW zyso@)zzgy6Fto9rn>FDTrR3O+*+eEd8e^4@Wdy{HT*?`lZUWK$RpW=j$IOiveQtPo@f-s z+OO(wz=?90y~quawv6%{D&PkzMp|3Sj8H(2$tBZg|;*F;_RY=f}y=(tlotGYQ|mZe38=CR-yx zfef7E8Qu4@0mPL)wlk)1V`oaVq!Yx(P|a(_k{}vfK$653;Zc`B!Un1oTHc5aU8P;K zw2E9`RirT}Ss>e2p@dyPAz2Y!0H`X$RM96bOwS}|;gF?+#K-nLB9W6Du;=x>k4P}n z&p$6p-FBmrdqA+i$e7NLGKRpp6iL5cZXD3IBIv};2PMF}gb0S$P4Rn;&DcaiJk56= z20>T1|ISr;ZT|!0(MjR)Uh2jpY#GOcLEq5_mtJGj-Up`b6<@GKw?!?W1T~S*Mcbs8 zPrh&QCR$4Y0VVS2n&BW|kWokp6<+$Weo(AITp9Z?@s3_)g=w;sqPw^i& zZ?+Xnb)Zpfgodr!%O^`{Q=ptjc7r*n8c789M*-4Ewd;}ICm&Fzs;Yuzm7`jL(3Gf*U7%23MK#5V5j35%*;76# zEkl51tZF85t_2Wczsll|RE1mm?MT{@Lx{O$-;PfOzsNGi3{5=Amd7Ge?|IiWlfbRh z&S0;fK*71LkY^F;ePd>_t-k_z=Rfjy_9AieF=3>im7GE*f{|&~kNIsBw%{D>!GEG!H`_6*7v+&)qdjZ zs*&Z0qF5rV-aeE``T69`jdx^gr15;@l`fw1m8@ru{Z}ERERMsn_mBfh`m=xRJ5G7z zl=g9o-ZVxyjQc*gEPAE9fLFstQzLy&kitQcL{u)wWe|f56z3SrXH#0oou?l9KX|f( zp*{(Q8oqLX8sZfVOOK)hDQ?g=$w1 zwd;qPBJRz=Xs(J%uiy$3uj=egcWM*(a>0)&NAJg!9P?_MBMd$tOZ?*ClsQ{(YIJ}= z^?3=skMaudJB*Axx!h%jKAvh&i8Q+w&3J0rPf_^Ljta{*P_A93-a>K>e*Au5^eK!P0Kd*vWm56#pSJK&h}E# zlw{UIDmSW|al!{y?UXm7^HziHMLVUX-r74=+w8`RcAztD>BGjK?L9#zc+a;a2S-@& zlvlePMv?FoKHn}CHM-X2;7M!e?T5}6E$3wiEuE$<)m56wEKjQAo_BA32Tm$urLTih zn4NLY#%1gIWw?MY0-F0!Me7&&0gxN{Z^ z6puA9v&5zhe&`G0TIZPIBmWlq*?1i7`0_wKoYJoh--TQ)ALhYO8mW{PdrO)Z#Gn*m zhY(!&ad%D#t|UmW5pr|ZtG5HvDMP~h6fcb5Q9{<5P+RC6`9bZ-CjNPw-f=!6>J7kZ z&B=Rd2_|o9jt5C|r)ooj?k z3MyE;6J5C6c6=gU^QDoc z^vg8573LEMISuk8WMaYU^?suNh|8MAqqQrREe|Klbw-vr_u$KopvR|UKO>{XC5e|Z z_XV|xPsl5kj9UCAW6(rd5rU^n6=;w-c&hYvbyGgWz6FJ>t0*bNds&w%uX3qI%VG)E zB?FxMBaefzKXRz3PWOr96j$r$@P26^buQ8kYIWZ%9Sw#yYk5}cHBR{zzY6UgPK_s?r+jqxI#0NLYhV&y;~W{#115DvNfPP1KUxy0)xdz`Vh+V$ zjQbsR#{YdxoeT z1hZoV)#D_ct}Y<&2_oCf=O10YZa|)w97#kkq6u!x!C}o8p8Oe-Pc^ z`W=ad5C_YLZ2>l@ni4^yF0ZIJMIYD{KIjyR?fL#4iY1h5$3B)ZUdktH6QSUxY;43FM1l{-x$c~_MmgXuU4@j!1fdv>yuAzpt zPFJA}McMvq)P82XI=*dqvESc?zh_3-lr_&ha#9lN)>5#arB0|`TXcl(GOY_`E_GCh zc$xzj;zGdtJBJe>hhI=>lD6_wS0X&$#V{sGvgZr_P9}vayLjs)686OWO&p@rccj4T z#n<(9+ijBfRx+}bZDvFBIopWB?mgQ1k)CAyf@^u;n9ph;)>5j)P;=_5k27xC7~$D- z$PPn-lM6H;H~iq-^Ib{T@?lDInFv_IQL&<^4G}sa_IVx-rg9by?!5_w_HX+Y4`bk$ z1Bw$EPIveadD5)tyCa^p$+s)iB?q%^kHMEGmbQnXkuv+R_a5;Ve-jbSgn7SBr;GJ{ z7ruU;2SiyX!e@;$F9>+9m}uC9$joZih@drMRnriw|2-;ku1`t$(#vLXuI>Ev%*v!U z{N~UZAct|DBE^*V*&sQ^D)0~Y5WpP z;0IPS$3(c|&}j~?6Vta8?hLwso5!=QERzv^s4JtSQ-PNLGi~QlXR~XH@*%&~V>`7v z@BYud(WSp?tN1JtDK%JuMx<3At!G$s5NViP<2-j)8P1*JQrou^FP8rLlwYZ2s~g3& z{B?exsnqnXon7J1>FNFQFf6q=jLNL4=asf0@_82|dQP01H?=#KY@=g-C5chvbSdL~uFUIG%8^PXx-*{W~x5adRIhp`9@zRJ1>{ROq z9p4}{c|AKvFU3DEW8cEY3`Wq%s9a<@y^p#?bcZ;{hv-6=wQYo4!hUDfY>IoLdNqgfUQOFFkX=7cZqs2>#^ zX{OLV1f0ghSsG@aDZ}O3vBndZuLr|b!Wm_C^>af^8xxv{oG|=y z)KrBN!V1?jNfd^aL*qsrt)dtn?-bA<+Z}8}BUREHuYeo=ZB(b1mKDJydpzaIVbZnm z5Y)Z7;FT1CzRtF2{AzhrDnVCMX+dp19P+hvke{$U~z@&IExzQ(17u_@LSG!72o7D&XpgJ21bCMcCB6<3V*$=Rk<cZ7dlDm!#b17tamAocBzPQx~h|Fr@r>|P-E z=*H7&J$>W>mH}+{_EX5hI>;;7#-j1dk03w*WxcMz9JXqpN-hnA)d%X0joPT6Oj>nVd7JmD7%E*VO7ZKMqf5=odI|ynXzm z>UQ-&w5xU)!~Qv^zVtF#$!Y;E;i}0caKVOG;r#dazJc1hM+*sPCSm6UOuI+mVhg)# zj0-%x1$+02v0%CNu-rZ55eSuPu{4r=BU&B$wI>T7t2(S!nd(7j#q2|oq@s5-OigIU z-UBeWY<0ih{0b)wf}7(cy_7-{6RKpj#mRLaXINZ!%thUt-ACC%<#;9D9C1%)W9K74 z+Qss^;pLMU$v5THq5I74J-$GP%%dCT>F7^&&%_@MljwCbw2K-8#5FcchJ<8j}VgPJ(0-o`wQr2ujcxU%ZcB)GjZpd7_KO-N z<99gYEZ;Ivm;D2ZZQZ-)@(5{=x+3ISy{Ov7EpMz;= zn3(T6gJv#D7R0xAzCR`aYK%uzu7Fbv?+GKd7h|<|817?~5!~khG1FyWpDDCa%?}3% zDX|KkPlC`!dLoqwp7^7eW2SOH+JPLl=MBNYaC8j7{+?9GBu;Ull{egm{}58FYfHjv zBswRXa-UO9=3a`L!5}Y}{~o|4IrXC1YeY+hhZ-3~+Y0_W5G^4Y6FBB^7Vm*sQEQ4&1kSh6}A9B@Qfd%Rq~2CF*2Si$0)-?5(g1u?21C3{=P5mb)q3w5mlxG zLtkTUaHIVvr92Mb0DtpTV;hpM5IcA>^G5-S=;4=EQlFo-6?wSYv+ zrwr8E_C;yb;R59`;}Pf1HzzYySjGBtD@+QrTOZD0>Ho^Oaz2R9JJ2YjF*T%gPST*@h3({W+oMYz50_sT@q-Q zkbfZ!{w4@+|DeyH`T1iM48FHdd)kR0jriRL9rI)4Mo&{12{V(!_j=|JQV1{d03SnR zrkN%GibgDt#Lhlz#ZVQYh=v{bV`9d2rX~W`MN2i`dg57$cLC6UR%Bu$-x zWM9MQ>Hy{dVRF`vHEU{IFz;&pXJ3~A{(RqC#j61|3LpFm3;ti|NfyQ*s)*zb)wT-! z>YJxe`?Mq2cF!{~Dou5g_FGel>~J`F`6zW#q()-%02rPOI40EV4k@pRX|pT}I^IbD zU03R#%;QH|R3d9H1}NKj4~SLO%4}dctp~zjQ$E0Bu#`zarCA(bQ%oYN$qJKa-Oe|9KH!QhWrd!GF-LrOc$91o@!Csm%O z5F3B#F3Jd1;_C?;L(8;?Y~hVyGYYh7OxeSakSnA7@lzMZB#OO|z+NI9d#vO+6_=Y+*qikA#t>VsVcKa^0 z6~3C|sLCBXxZTyvoh7%oU6Y^o&f2&+zMuAl_aOx*m(niwPnsm~ZV26f-4Hen7e>`8 zO@WpZ?C%r0_o1-SD6P%Q>oCT)e<>4>H>Q!apGs29_(8W1(U4ybMXPfQUHKUCdms3n zdx7?TU7FWpnxHLwiaQZa8?PU?HOT&4r9PILx?4hQy^1evPYLTQGNp+q5q7(#wyNg5ctT?w1jSxCS{ ziJkTUl{U&Cs@;8WL%VTGX$n1H=gyu*q6IcDUn+k;zW@lAeHy4}QD9@hGIwrQg$HBHS!6@7QA zw_CTn??{L0^A&BKY1_`C=Q9*Kds7yg!H=ec5?g|~FeV>ZVB$8+9m8<)w)H6ylehg> zRt3CUu;ur44Pb_FCB@>D_Dmfy;nEBy4T%q=-fz5+MCQpWGn&epEHsV0oN#C^7u1Zf zdFIy)1d&jvatzc{w62o@!{t7G@wLXA)~SKzs^W{M`A-@OS6_-w=} zD&_9MI5Aglt9vGdcBM$Io7~sVN4yp*`0R2YcoBgnUv=)B7xGW3R~By^3^|S(B&B@q znq8hIWHu+=*|~;Bwla-z-rb(R6kTkD4xy)YBiXxEQ>22Dykp*VNlH3(cX1-`?(V+< z3-kIx7jxGBc8MrP9KlH(w^&KNEqw%C>~ggkrJE<`749A8IW>4?R;Gqc_026Dji9e5 zTc5Lm`zw6^G4^{rQZhm!00H51|38^nRy{ihgFjB@{~xEO*IHlym{_DAJvsa0?U-sL zC`AX7$b_^B(eVb-Vc<7mAy&1lhDcInM-t)OCGmsSGBn;;b>GZX+&1GuS!{N7<;-jWti);D#oD6^?kh>Xn|u|v?C z?!A^&52R7lF?=?#Ib>{HKv+k3`+ri}fO9by0r}-eTt{CKMXG(0MwY^4k$l-Mdtz4{ zzi3mSo;zt!M-a@J9tr{HS8-{aG5FC-u9$Eo5}<()-%vJM$_z( zc^5;jMXSWyvZu3LVC`_RZ^UvJQhiN%7{Dh``k1iK(1S)V}2uT;%(GE1jSz*=fWkH}wS@>&}MRD2#osfc(j zfcVg~47Ff8jYG0wJ0c#{`S_GydmVVk@np3(ka0+8C?&_h?5nvpjMf7z4J!L>1HhVF zUQ+@IcM&Tn8%E_K(9t6w{$vW-kQ4KdPv)k8TzQA!A{9s2&iPTsMsMsT@DX(u#%{xK z@L{$p1Vn|C#;WP*QaKFBO4COneI+9?gQ-dYZff=V_Kl6TIdgYGcx-XCA^9tMGf=0> z0H}DG1gZLA1OBoYq-Be9U!2(TUWP?LKPqQdS$J<7cZIR-{J>->bR>!m zGhqesqJyFJZ~5*PXH9X2&g2P;dn)oKk=4%2ZBoYeHLKbffVbh$tr$`*hIK5Tf|G8z zt_tY(k8*V^Q7XAXJ{v!HG`7n$oawdZR(d~QAIy*|Pmjm`BZxCI4y!2;Kw(DpH)AH^IP`^X$2Tj3Es*0DpTl@mX%JbI zFxGjah+=}DhIsz(0105sl@n-7DfBu8K_PHs1E?jW?-v52u-Nj;A`L7^HM4Xeb-NO< zK4L+Mc!-CR1sUuQlS;m+T56eBxmfhp1Q=xVahU&G%}cXNNh^s|}RFqtHf(miNMSfG+LkHkSbU_lP* z2~=T>89Qz4#&Oza*y;Uhh$eq_5)^~YFqB$0N{ -

- Bashbot - A Telegram bot written in bash. -

-

Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadelwartz).

-

Contributions by JuanPotato, BigNerd95, TiagoDanin, and iicc1.

-

Released to the public domain wherever applicable. Elsewhere, consider it released under the WTFPLv2.

-

Prerequisites

-

Uses JSON.sh, but no more TMUX.

-

Even bashbot is written in bash, it depends on commands typically availible in a Unix/Linux Environment. More concret on the common commands provided by coreutils, busybox or toybox, see Developer Notes

-

Bashbot Documentation and Downloads are availible on www.github.com

-

Documentation

- -

Your really first bashbot in a nutshell

-

To install and run bashbot you need acess to a linux/unix/bsd command line. If you don’t know how to get accces to a linux/unix/bsd like command line you should stop reading here :-(

-

In addition you need a Telegram client and a mobile phone to register an account. If you don’t want to register for Telegram you should stop reading here ;-)

-

After you’re registered to Telegram send a message to [@botfather](https://telegram.me/botfather), create a new Telegram Bot token and write it down. You need the token to install the bot.

-

Now open a linux/unix/bsd terminal and check if bash is installed: which bash && echo "bash installed!". If you get an error message bash is not installed.

-

Create a new directory and change to it: mkdir tbb; cd tbb and download the latest ’*.tar.gz’ file from https://github.com/topkecleon/telegram-bot-bash/releases. This can be done with the commands:

-
wget -q https://github.com/$(wget -q https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - | egrep '/.*/.*/.*tar.gz' -o)
-

Extract the ’*.tar.gz’ file and change to bashbot directory: tar -xzf *.tar.gz; cd telegram-bot-bash, install bashbot: ./bashbot.sh init and enter your bot token when asked. All other questions can be answered by hitting the <Return> key.

-

Thats all, now you can start your bot with ./bashbot.sh start and send him messages:

-
/start
-
-You are Botadmin
-*Available commands*:
-*• /start*: _Start bot and get this message_.
-*• /help*: _Get this message_.
-*• /info*: _Get shorter info message about this bot_....
-
-/info
-
-his is bashbot, the Telegram bot written entirely in bash.
-It features background tasks and interactive chats, and can serve as an interface for CLI programs.
-

For more Information on how to install, customize and use your new bot, read the Documentation

-
-

Security Considerations

-

Running a Telegram Bot means it is connected to the public and you never know whats send to your Bot.

-

Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with ‘quoting hell’ and globbing, see Implications of wrong quoting

-

Whenever you are processing input from from untrusted sources (messages, files, network) you must be as carefull as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everthing. In addition delete unused scripts and examples from your Bot, e.g. scripts ‘notify’, ‘calc’, ‘question’, and disable all not used commands.

-

A powerful tool to improve your scripts is shellcheck. You can use it online or install shellcheck locally. Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it’s not allowed to push changes without passing all shellcheck tests. In addition bashbot has a test suite to check if important functionality is working as expected.

-

Run your Bot as a restricted user

-

I recommend to run your bot as a user, with almost no access rights. All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. For the same reason ervery file your Bot can read is in danger to be disclosed. Restict your Bots access rigths to the absolute minimum.

-

Never run your Bot as root, this is the most dangerous you can do! Usually the user ‘nobody’ has almost no rights on Unix/Linux systems. See Expert use on how to run your Bot as an other user.

-

Secure your Bot installation

-

Your Bot configuration must no be readable from other users. Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in!

-

Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in token must be protected against other users. No one exept you must have write access to the Bot files. The Bot must be restricted to have write access to count and tmp-bot-bash only, all other files must be write protected.

-

To set access rights for your bashbot installation to a reasonable default run sudo ./bashbot.sh init after every update or change to your installation directory.

-

FAQ

-

Is this Bot insecure?

-

Bashbot is not more (in)secure as any other Bot written in any other language, we have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks …

-

Why Bash and not the much better xyz?

-

Well, thats a damn good question … may be because I’m an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side:

-
    -
  • bashbot will run everywhere where bash is availible, from ebedded linux to mainframe
  • -
  • easy to integrate with other shell script, e.g. for sending system message / health status
  • -
  • no need to install or learn a new programming language, library or framework
  • -
  • no database, not event driven, not OO …
  • -
-

Can I have the single bashbot.sh file back?

-

At the beginning bashbot was simply the file bashbot.sh you can copy everywhere and run the bot. Now we have ‘commands.sh’, ‘mycommands.sh’, ’modules/*.sh’ and much more.

-

Hey no Problem, if you are finished with your cool bot run dev/make-standalone.sh to create a stripped down Version of your bot containing only ‘bashbot.sh’ and ‘commands.sh’! For more information see Create a stripped down Version of your Bot

-

Can I send messages from CLI and scripts?

-

Of course, you can send messages from CLI and scripts, simply install bashbot as described here, send the messsage ‘/start’ to set yourself as botadmin and stop the bot with ./bashbot.sh kill.

-

Run the following commands in your bash shell or script while you are in the installation directory:

-
# prepare bash / script to send commands
-export BASHBOT_HOME="$(pwd)"
-source ./bashbot.sh source
-
-# send me a test message
-send_message "$(cat "$BOTADMIN")" "test"
-
-# send me output of a system command
-send_message "$(<"$BOTADMIN")" "$(df -h)"
-

For more information see Expert Use

-

Why do I get “EXPECTED value GOT EOF” on start?

-

May be your IP is blocked by telegram. You can test this by running curl or wget manually:

-
curl -m 10  https://api.telegram.org/bot
-#curl: (28) Connection timed out after 10001 milliseconds
-
-wget -t 1 -T 10 https://api.telegram.org/bot
-#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out.
-

This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a tor proxy on your server you may uncomment the BASHBOT_CURL_ARGS line in ‘mycommands.sh’

-

@Gnadelwartz

-

That’s it!

-

If you feel that there’s something missing or if you found a bug, feel free to submit a pull request!

-


VERSION
v0.90-dev2-19-g5779acc

- - diff --git a/DIST/telegram-bot-bash/README.md b/DIST/telegram-bot-bash/README.md deleted file mode 100644 index 58dd994..0000000 --- a/DIST/telegram-bot-bash/README.md +++ /dev/null @@ -1,184 +0,0 @@ -

-Bashbot - A Telegram bot written in bash. -

-Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadelwartz). - -Contributions by JuanPotato, BigNerd95, TiagoDanin, and iicc1. - -Released to the public domain wherever applicable. -Elsewhere, consider it released under the [WTFPLv2](http://www.wtfpl.net/txt/copying/). - -## Prerequisites -Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX. - -Even bashbot is written in bash, it depends on commands typically availible in a Unix/Linux Environment. -More concret on the common commands provided by [coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), [busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or [toybox](https://landley.net/toybox/help.html), see [Developer Notes](doc/7_develop.md#common-commands) - - -Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and [Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are availible on www.github.com - -## Documentation -* [Introdution to Telegram Bots](https://core.telegram.org/bots) - * [One Bot to rule them all](https://core.telegram.org/bots#3-how-do-i-create-a-bot) - * [Bot commands](https://core.telegram.org/bots#commands) -* [Install Bashbot](doc/0_install.md) - * Install release - * Install from githup - * Update Bashbot - * Notes on Updates -* [Create a new Telegram Bot with botfather](doc/1_firstbot.md) -* [Getting Started](doc/2_usage.md) - * Managing your Bot - * Recieve data - * Send messages - * Send files, locations, keyboards -* [Advanced Features](doc/3_advanced.md) - * Access Control - * Interactive Chats - * Background Jobs - * Inline queries -* [Expert Use](doc/4_expert.md) - * Handling UTF-8 character sets - * Run as other user or system service - * Scedule bashbot from Cron - * Use from CLI and Scripts - * Customize Bashbot Environment -* [Best Practices](doc/5_practice.md) - * Customize commands.sh - * Seperate logic from commands - * Test your Bot with shellcheck -* [Function Reference](doc/6_reference.md) - * Sending Messages, Files, Keyboards - * User Access Control - * Inline Queries - * Background and Interactive Jobs -* [Deveoper Notes](doc/7_develop.md) - * Debug bashbot - * Modules, addons, events - * Setup your environment - * Bashbot testsuite -* [Examples Dir](examples/README.md) - -### Your really first bashbot in a nutshell -To install and run bashbot you need acess to a linux/unix/bsd command line. If you don't know how to get accces to a linux/unix/bsd like command line you should stop reading here :-( - -In addition you need a [Telegram client](https://telegram.org) and a mobile phone to [register an account](https://telegramguide.com/create-a-telegram-account/). -If you don't want to register for Telegram you should stop reading here ;-) - -After you're registered to Telegram send a message to [@botfather](https://telegram.me/botfather), -[create a new Telegram Bot token](doc/1_firstbot.md) and write it down. You need the token to install the bot. - -Now open a linux/unix/bsd terminal and check if bash is installed: ```which bash && echo "bash installed!"```. -If you get an error message bash is not installed. - -Create a new directory and change to it: ```mkdir tbb; cd tbb``` and download the latest '*.tar.gz' file from -[https://github.com/topkecleon/telegram-bot-bash/releases](https://github.com/topkecleon/telegram-bot-bash/releases). This can be done with the commands: -```bash -wget -q https://github.com/$(wget -q https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - | egrep '/.*/.*/.*tar.gz' -o) -``` - -Extract the '*.tar.gz' file and change to bashbot directory: ```tar -xzf *.tar.gz; cd telegram-bot-bash```, -install bashbot: ```./bashbot.sh init``` and enter your bot token when asked. All other questions can be answered -by hitting the \ key. - -Thats all, now you can start your bot with ```./bashbot.sh start``` and send him messages: -``` -/start - -You are Botadmin -*Available commands*: -*• /start*: _Start bot and get this message_. -*• /help*: _Get this message_. -*• /info*: _Get shorter info message about this bot_.... - -/info - -his is bashbot, the Telegram bot written entirely in bash. -It features background tasks and interactive chats, and can serve as an interface for CLI programs. -``` -For more Information on how to install, customize and use your new bot, read the [Documentation](#Documentation) - ----- - -## Security Considerations -Running a Telegram Bot means it is connected to the public and you never know whats send to your Bot. - -Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' and globbing, see [Implications of wrong quoting](https://unix.stackexchange.com/questions/171346/security-implications-of-forgetting-to-quote-a-variable-in-bash-posix-shells) - -Whenever you are processing input from from untrusted sources (messages, files, network) you must be as carefull as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everthing. In addition delete unused scripts and examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable all not used commands. - -A powerful tool to improve your scripts is ```shellcheck```. You can [use it online](https://www.shellcheck.net/) or [install shellcheck locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it's not allowed to push changes without passing all shellcheck tests. -In addition bashbot has a [test suite](doc/7_develop.md) to check if important functionality is working as expected. - -### Run your Bot as a restricted user -**I recommend to run your bot as a user, with almost no access rights.** -All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. -For the same reason ervery file your Bot can read is in danger to be disclosed. Restict your Bots access rigths to the absolute minimum. - -**Never run your Bot as root, this is the most dangerous you can do!** Usually the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert use](doc/4_expert.md) on how to run your Bot as an other user. - -### Secure your Bot installation -**Your Bot configuration must no be readable from other users.** Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in! - -Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in ```token``` must be protected against other users. No one exept you must have write access to the Bot files. The Bot must be restricted to have write access to ```count``` and ```tmp-bot-bash``` only, all other files must be write protected. - -To set access rights for your bashbot installation to a reasonable default run ```sudo ./bashbot.sh init``` after every update or change to your installation directory. - -## FAQ - -### Is this Bot insecure? -Bashbot is not more (in)secure as any other Bot written in any other language, we have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks ... - -### Why Bash and not the much better xyz? -Well, thats a damn good question ... may be because I'm an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side: - -- bashbot will run everywhere where bash is availible, from ebedded linux to mainframe -- easy to integrate with other shell script, e.g. for sending system message / health status -- no need to install or learn a new programming language, library or framework -- no database, not event driven, not OO ... - -### Can I have the single bashbot.sh file back? -At the beginning bashbot was simply the file ```bashbot.sh``` you can copy everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', 'modules/*.sh' and much more. - -Hey no Problem, if you are finished with your cool bot run ```dev/make-standalone.sh``` to create a stripped down Version of your bot containing only -'bashbot.sh' and 'commands.sh'! For more information see [Create a stripped down Version of your Bot](doc/7_develop.md) - -### Can I send messages from CLI and scripts? -Of course, you can send messages from CLI and scripts, simply install bashbot as [described here](#Your-really-first-bashbot-in-a-nutshell), -send the messsage '/start' to set yourself as botadmin and stop the bot with ```./bashbot.sh kill```. - -Run the following commands in your bash shell or script while you are in the installation directory: - -```bash -# prepare bash / script to send commands -export BASHBOT_HOME="$(pwd)" -source ./bashbot.sh source - -# send me a test message -send_message "$(cat "$BOTADMIN")" "test" - -# send me output of a system command -send_message "$(<"$BOTADMIN")" "$(df -h)" -``` -For more information see [Expert Use](doc/8_custom.md) - - -### Why do I get "EXPECTED value GOT EOF" on start? -May be your IP is blocked by telegram. You can test this by running curl or wget manually: -```bash -curl -m 10 https://api.telegram.org/bot -#curl: (28) Connection timed out after 10001 milliseconds - -wget -t 1 -T 10 https://api.telegram.org/bot -#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out. -``` -This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a tor proxy on your server you may uncomment the ```BASHBOT_CURL_ARGS``` line in 'mycommands.sh' - - -@Gnadelwartz - -## That's it! - -If you feel that there's something missing or if you found a bug, feel free to submit a pull request! - -#### $$VERSION$$ v0.90-dev2-19-g5779acc diff --git a/DIST/telegram-bot-bash/README.txt b/DIST/telegram-bot-bash/README.txt deleted file mode 100644 index e1343e8..0000000 --- a/DIST/telegram-bot-bash/README.txt +++ /dev/null @@ -1,258 +0,0 @@ -

-Bashbot - A Telegram bot written in bash. -

-Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M -(@gnadelwartz). - -Contributions by JuanPotato, BigNerd95, TiagoDanin, and iicc1. - -Released to the public domain wherever applicable. -Elsewhere, consider it released under the -[WTFPLv2](http://www.wtfpl.net/txt/copying/). - -## Prerequisites -Uses [JSON.sh](http://github.com/dominictarr/JSON.sh), but no more TMUX. - -Even bashbot is written in bash, it depends on commands typically availible in -a Unix/Linux Environment. -More concret on the common commands provided by -[coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), -[busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) or -[toybox](https://landley.net/toybox/help.html), see [Developer -Notes](doc/7_develop.md#common-commands) - - -Bashbot [Documentation](https://github.com/topkecleon/telegram-bot-bash) and -[Downloads](https://github.com/topkecleon/telegram-bot-bash/releases) are -availible on www.github.com - -## Documentation -* [Introdution to Telegram Bots](https://core.telegram.org/bots) - * [One Bot to rule them -all](https://core.telegram.org/bots#3-how-do-i-create-a-bot) - * [Bot commands](https://core.telegram.org/bots#commands) -* [Install Bashbot](doc/0_install.md) - * Install release - * Install from githup - * Update Bashbot - * Notes on Updates -* [Create a new Telegram Bot with botfather](doc/1_firstbot.md) -* [Getting Started](doc/2_usage.md) - * Managing your Bot - * Recieve data - * Send messages - * Send files, locations, keyboards -* [Advanced Features](doc/3_advanced.md) - * Access Control - * Interactive Chats - * Background Jobs - * Inline queries -* [Expert Use](doc/4_expert.md) - * Handling UTF-8 character sets - * Run as other user or system service - * Scedule bashbot from Cron - * Use from CLI and Scripts - * Customize Bashbot Environment -* [Best Practices](doc/5_practice.md) - * Customize commands.sh - * Seperate logic from commands - * Test your Bot with shellcheck -* [Function Reference](doc/6_reference.md) - * Sending Messages, Files, Keyboards - * User Access Control - * Inline Queries - * Background and Interactive Jobs -* [Deveoper Notess](doc/7_develop.md) - * Debug bashbot - * Modules, addons, events - * Setup your environment - * Bashbot testsuite -* [Examples Dir](examples/README.md) - -### Your really first bashbot in a nutshell -To install and run bashbot you need acess to a linux/unix/bsd command line. If -you don't know how to get accces to a linux/unix/bsd like command line you -should stop reading here :-( - -In addition you need a [Telegram client](https://telegram.org) and a mobile -phone to [register an -account](https://telegramguide.com/create-a-telegram-account/). -If you don't want to register for Telegram you should stop reading here ;-) - -After you're registered to Telegram send a message to -[@botfather](https://telegram.me/botfather), -[create a new Telegram Bot token](doc/1_firstbot.md) and write it down. You -need the token to install the bot. - -Now open a linux/unix/bsd terminal and check if bash is installed: ```which -bash && echo "bash installed!"```. -If you get an error message bash is not installed. - -Create a new directory and change to it: ```mkdir tbb; cd tbb``` and download -the latest '*.tar.gz' file from -[https://github.com/topkecleon/telegram-bot-bash/releases](https://github.com/to -pkecleon/telegram-bot-bash/releases). This can be done with the commands: -```bash -wget -q https://github.com/$(wget -q -https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - | egrep -'/.*/.*/.*tar.gz' -o) -``` - -Extract the '*.tar.gz' file and change to bashbot directory: ```tar -xzf -*.tar.gz; cd telegram-bot-bash```, -install bashbot: ```./bashbot.sh init``` and enter your bot token when asked. -All other questions can be answered -by hitting the \ key. - -Thats all, now you can start your bot with ```./bashbot.sh start``` and send -him messages: -``` -/start - -You are Botadmin -*Available commands*: -*• /start*: _Start bot and get this message_. -*• /help*: _Get this message_. -*• /info*: _Get shorter info message about this bot_.... - -/info - -his is bashbot, the Telegram bot written entirely in bash. -It features background tasks and interactive chats, and can serve as an -interface for CLI programs. -``` -For more Information on how to install, customize and use your new bot, read -the [Documentation](#Documentation) - ----- - -## Security Considerations -Running a Telegram Bot means it is connected to the public and you never know -whats send to your Bot. - -Bash scripts in general are not designed to be bullet proof, so consider this -Bot as a proof of concept. Bash programmers often struggle with 'quoting hell' -and globbing, see [Implications of wrong -quoting](https://unix.stackexchange.com/questions/171346/security-implications-o -f-forgetting-to-quote-a-variable-in-bash-posix-shells) - -Whenever you are processing input from from untrusted sources (messages, files, -network) you must be as carefull as possible, e.g. set IFS appropriate, disable -globbing (set -f) and quote everthing. In addition delete unused scripts and -examples from your Bot, e.g. scripts 'notify', 'calc', 'question', and disable -all not used commands. - -A powerful tool to improve your scripts is ```shellcheck```. You can [use it -online](https://www.shellcheck.net/) or [install shellcheck -locally](https://github.com/koalaman/shellcheck#installing). Shellcheck is used -extensive in bashbot development to enshure a high code quality, e.g. it's not -allowed to push changes without passing all shellcheck tests. -In addition bashbot has a [test suite](doc/7_develop.md) to check if important -functionality is working as expected. - -### Run your Bot as a restricted user -**I recommend to run your bot as a user, with almost no access rights.** -All files your Bot have write access to are in danger to be overwritten/deleted -if your bot is hacked. -For the same reason ervery file your Bot can read is in danger to be disclosed. -Restict your Bots access rigths to the absolute minimum. - -**Never run your Bot as root, this is the most dangerous you can do!** Usually -the user 'nobody' has almost no rights on Unix/Linux systems. See [Expert -use](doc/4_expert.md) on how to run your Bot as an other user. - -### Secure your Bot installation -**Your Bot configuration must no be readable from other users.** Everyone who -can read your Bots token can act as your Bot and has access to all chats your -Bot is in! - -Everyone with read access to your Bot files can extract your Bots data. -Especially your Bot Token in ```token``` must be protected against other users. -No one exept you must have write access to the Bot files. The Bot must be -restricted to have write access to ```count``` and ```tmp-bot-bash``` only, -all other files must be write protected. - -To set access rights for your bashbot installation to a reasonable default run -```sudo ./bashbot.sh init``` after every update or change to your installation -directory. - -## FAQ - -### Is this Bot insecure? -Bashbot is not more (in)secure as any other Bot written in any other language, -we have done our best to make it as secure as possible. But YOU are responsible -for the bot commands you wrote and you should know about the risks ... - -### Why Bash and not the much better xyz? -Well, thats a damn good question ... may be because I'm an Unix/Linux admin -from stone age. Nevertheless there are more reasons from my side: - -- bashbot will run everywhere where bash is availible, from ebedded linux to -mainframe -- easy to integrate with other shell script, e.g. for sending system message / -health status -- no need to install or learn a new programming language, library or framework -- no database, not event driven, not OO ... - -### Can I have the single bashbot.sh file back? -At the beginning bashbot was simply the file ```bashbot.sh``` you can copy -everywhere and run the bot. Now we have 'commands.sh', 'mycommands.sh', -'modules/*.sh' and much more. - -Hey no Problem, if you are finished with your cool bot run -```dev/make-standalone.sh``` to create a stripped down Version of your bot -containing only -'bashbot.sh' and 'commands.sh'! For more information see [Create a stripped -down Version of your Bot](doc/7_develop.md) - -### Can I send messages from CLI and scripts? -Of course, you can send messages from CLI and scripts, simply install bashbot -as [described here](#Your-really-first-bashbot-in-a-nutshell), -send the messsage '/start' to set yourself as botadmin and stop the bot with -```./bashbot.sh kill```. - -Run the following commands in your bash shell or script while you are in the -installation directory: - -```bash -# prepare bash / script to send commands -export BASHBOT_HOME="$(pwd)" -source ./bashbot.sh source - -# send me a test message -send_message "$(cat "$BOTADMIN")" "test" - -# send me output of a system command -send_message "$(<"$BOTADMIN")" "$(df -h)" -``` -For more information see [Expert Use](doc/8_custom.md) - - -### Why do I get "EXPECTED value GOT EOF" on start? -May be your IP is blocked by telegram. You can test this by running curl or -wget manually: -```bash -curl -m 10 https://api.telegram.org/bot -#curl: (28) Connection timed out after 10001 milliseconds - -wget -t 1 -T 10 https://api.telegram.org/bot -#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... -failed: Connection timed out. -``` -This may happen if to many wrong requests are sent to api.telegram.org, e.g. -using a wrong token or not existing API calls. If you have a fixed IP you can -ask telegram service to unblock your ip or change your IP. If you are running a -tor proxy on your server you may uncomment the ```BASHBOT_CURL_ARGS``` line in -'mycommands.sh' - - -@Gnadelwartz - -## That's it! - -If you feel that there's something missing or if you found a bug, feel free to -submit a pull request! - -#### $$VERSION$$ v0.90-dev2-19-g5779acc diff --git a/DIST/telegram-bot-bash/addons/antiFlood.sh.dist b/DIST/telegram-bot-bash/addons/antiFlood.sh.dist deleted file mode 100644 index 25fdc61..0000000 --- a/DIST/telegram-bot-bash/addons/antiFlood.sh.dist +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash -# file: addons/antiFlood.sh.dist -# -# this addon counts how many files, e.g. stickers, are sent to -# a chat and takes actions if threshold is reached -# - -# used events: -# -# BASHBOT_EVENT_TEXT message containing message text received -# BASHBOT_EVENT_CMD a command is recieved -# BASHBOT_EVENT_FILE file received -# -# all global variables and functions can be used in registered functions. -# -# parameters when loaded -# $1 event: init, startbot ... -# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic -# -# prameters on events -# $1 event: inline, message, ..., file -# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic -# -# shellcheck disable=SC2140 - -# export used events -export BASHBOT_EVENT_TEXT BASHBOT_EVENT_CMD BASHBOT_EVENT_FILE - -# any global variable defined by addons MUST be prefixed by addon name -ANTIFLOOD_ME="antiflood" - -declare -Ax ANTIFLOOD_CHATS ANTIFLOOD_ACTUALS - -ANTIFLOOD_DEFAULT="5" # 5 files per minute -ANTIFLOOD_BAN="5" # 5 minutes - -# initialize after installation or update -if [[ "$1" = "init"* ]]; then - : # nothing -fi - - -# register on startbot -if [[ "$1" = "start"* ]]; then - #load existing chat settings on start - jssh_readDB "ANTIFLOOD_CHATS" "${ADDONDIR:-./addons}/$ANTIFLOOD_ME" - - # register to CMD - BASHBOT_EVENT_CMD["${ANTIFLOOD_ME}"]="${ANTIFLOOD_ME}_cmd" - - # command /antiflood starts addon, $1 floodlevel $2 bantime - antiflood_cmd(){ - case "${CMD[0]}" in - "/antifl"*) - ANTIFLOOD_CHATS["${CHAT[ID]}","level"]="${ANTIFLOOD_DEFAULT}" - ANTIFLOOD_CHATS["${CHAT[ID]}","ban"]="${ANTIFLOOD_BAN}" - [[ "${CMD[1]}" =~ ^[0-9]+$ ]] && ANTIFLOOD_CHATS["${CHAT[ID]}","level"]="${CMD[1]}" - [[ "${CMD[2]}" =~ ^[0-9]+$ ]] && ANTIFLOOD_CHATS["${CHAT[ID]}","ban"]="${CMD[2]}" - # antiflood_save & - ;; - esac - } - - # register to inline and command - BASHBOT_EVENT_TEXT["${ANTIFLOOD_ME}"]="${ANTIFLOOD_ME}_multievent" - BASHBOT_EVENT_FILE["${ANTIFLOOD_ME}"]="${ANTIFLOOD_ME}_multievent" - - antiflood_multievent(){ - # not started - [ "${ANTIFLOOD_CHATS["${CHAT[ID]}","level"]}" = "" ] && return - # count flood messages - [ "$1" = "text" ] && [ "${#MESSAGE[0]}" -lt "5" ] && (( ANTIFLOOD_ACTUALS["${CHAT[ID]}","${USER[ID]}"] += 1 )) - # shellcheck disable=SC2154 - [ "$1" = "file" ] && (( ANTIFLOOD_ACTUALS["${CHAT[ID]}","${USER[ID]}","file"] += 1 )) - # antiflood_check & - } -fi diff --git a/DIST/telegram-bot-bash/addons/example.sh.dist b/DIST/telegram-bot-bash/addons/example.sh.dist deleted file mode 100644 index c27ef81..0000000 --- a/DIST/telegram-bot-bash/addons/example.sh.dist +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash -# file: addons/example.sh.dist -# -# Addons can register to bashbot events at statup -# by providing their name and a callback per event -# -# If an event occours each registered event function is called. -# -# Events run in the same context as the main bashbot event loop -# so variables set here are persistent as long bashbot is running. -# -# Note: For the same reason event function MUST return imideatly! -# compute intensive tasks must be run in a nonblocking subshell, -# e.g. "(long running) &" -# - -# Availible events: -# on events startbot and init, this file is sourced -# -# BASHBOT_EVENT_INLINE inline query received -# BASHBOT_EVENT_MESSAGE any type of message received -# BASHBOT_EVENT_TEXT message containing message text received -# BASHBOT_EVENT_CMD a command is recieved -# BASHBOT_EVENT_REPLYTO reply to message received -# BASHBOT_EVENT_FORWARD forwarded message received -# BASHBOT_EVENT_CONTACT contact received -# BASHBOT_EVENT_LOCATION location or venue received -# BASHBOT_EVENT_FILE file received -# -# BAHSBOT_EVENT_TIMER this event is a bit special as it fires every Minute -# and has 3 meanings: oneshot, everytime, every X minutes. -# -# all global variables and functions can be used in registered functions. -# -# parameters when loaded -# $1 event: init, startbot ... -# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic -# -# prameters on events -# $1 event: inline, message, ..., file -# $2 debug: use "[[ "$2" = *"debug"* ]]" if you want to output extra diagnostic -# - -# export used events -export BASHBOT_EVENT_INLINE BASHBOT_EVENT_CMD BASHBOT_EVENT_REPLY BASHBOT_EVENT_TIMER - -# any global variable defined by addons MUST be prefixed by addon name -EXAMPLE_ME="example" - -# initialize after installation or update -if [[ "$1" = "init"* ]]; then - : # nothing to do -fi - - -# register on startbot -if [[ "$1" = "start"* ]]; then - # register to reply - BASHBOT_EVENT_REPLY["${EXAMPLE_ME}"]="${EXAMPLE_ME}_reply" - - # any function defined by addons MUST be prefixed by addon name - # function local variables can have any name, but must be LOCAL - example_reply(){ - local msg="message" - send_markdown_message "${CHAT[ID]}" "User *${USER[USERNAME]}* replied to ${msg} from *${REPLYTO[USERNAME]}*" & - } - - # register to inline and command - BASHBOT_EVENT_INLINE["${EXAMPLE_ME}"]="${EXAMPLE_ME}_multievent" - BASHBOT_EVENT_CMD["${EXAMPLE_ME}"]="${EXAMPLE_ME}_multievent" - - # any function defined by addons MUST be prefixed by addon name - # function local variables can have any name, but must be LOCAL - example_multievent(){ - local type="$1" - local msg="${MESSAGE[0]}" - # shellcheck disable=SC2154 - [ "${type}" = "inline" ] && msg="${iQUERY[0]}" - send_normal_message "${CHAT[ID]}" "${type} received: ${msg}" & - } - - BASHBOT_EVENT_TIMER["${EXAMPLE_ME}after5min","-5"]="${EXAMPLE_ME}_after5min" - - # any function defined by addons MUST be prefixed by addon name - # function local variables can have any name, but must be LOCAL - example_after5min(){ - send_markdown_message "$(< "${BOTADMIN}")" "This is a one time event after 5 Minutes!" & - } - - BASHBOT_EVENT_TIMER["${EXAMPLE_ME}every2min","2"]="${EXAMPLE_ME}_every2min" - - # any function defined by addons MUST be prefixed by addon name - # function local variables can have any name, but must be LOCAL - example_every2min(){ - send_markdown_message "$(< "${BOTADMIN}")" "This a a every 2 minute event ..." & - } -fi diff --git a/DIST/telegram-bot-bash/bashbot.rc.dist b/DIST/telegram-bot-bash/bashbot.rc.dist deleted file mode 100755 index 7ff1028..0000000 --- a/DIST/telegram-bot-bash/bashbot.rc.dist +++ /dev/null @@ -1,86 +0,0 @@ -#!/bin/sh -# description: Start or stop telegram-bash-bot -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 -# shellcheck disable=SC2009 -# shellcheck disable=SC2181 - -# -### BEGIN INIT INFO -# Provides: bashbot -# Required-Start: $network $syslog -# Required-Stop: $network -# Default-Start: 2 3 5 -# Default-Stop: 0 1 6 -# Description: Start or stop telegram-bot-bash server -### END INIT INFO - -# save default values -TERM="" # disable bashbot clear and color output -runas="nobody" -runcmd="echo Dry run:" # not actived until you edit lines below - -####################### -# Configuration Section - -# edit the next line to fit the user you want to run bashbot, e.g. nobody: -# runas="nobody" - -# uncomment one of the following lines to fit your system -# runcmd="su $runas -s /bin/bash -c " # runasuser with *su* -# runcmd="runuser $runas -s /bin/bash -c " # runasuser with *runuser* - -# edit the values of the following lines to fit your config: -start="/usr/local/telegram-bot-bash/bashbot.sh" # location of your bashbot.sh script -name='' # your bot name as given to botfather, e.g. mysomething_bot - -# END Configuration -####################### - -lockfile="$(dirname $start)/lockfile" -[ "$name" = "" ] && name="$runas" - -case "$1" in -'start') - $runcmd "$start start" # >/dev/null 2>&1 /dev/null 2>&1 - fi - ;; -'stop') - $runcmd "$start kill" - RETVAL=$? - if [ "$RETVAL" = "0" ]; then - rm -f "$lockfile" - fi - ;; -'status') - ps -f -u "$runas" | grep "$name" | grep -qF "bashbot.sh startbot" - if [ "$?" = "0" ]; then - echo "bashbot ($name) is running" - RETVAL=0 - else - echo "bashbot ($name) is stopped" - RETVAL=1 - fi - ;; -'restart'|'reload') - $0 stop; $0 start - RETVAL=$? - ;; -'restartback') - $0 suspendback; $0 resumeback - RETVAL=$? - ;; -'suspendback'|'resumeback'|'killback') - $runcmd "$start $1" - RETVAL=$? - ;; -*) - echo "Usage: $0 { start | stop | restart | reload | restartback | suspendback | resumeback | killback }" - RETVAL=1 - ;; -esac -exit $RETVAL - diff --git a/DIST/telegram-bot-bash/bashbot.sh b/DIST/telegram-bot-bash/bashbot.sh deleted file mode 100755 index 66308b1..0000000 --- a/DIST/telegram-bot-bash/bashbot.sh +++ /dev/null @@ -1,784 +0,0 @@ -#!/bin/bash -# file: bashbot.sh -# do not edit, this file will be overwritten on update - -# bashbot, the Telegram bot written in bash. -# Written by Drew (@topkecleon) and Daniil Gentili (@danogentili), KayM (@gnadelwartz). -# Also contributed: JuanPotato, BigNerd95, TiagoDanin, iicc1. -# https://github.com/topkecleon/telegram-bot-bash - -# Depends on JSON.sh (http://github.com/dominictarr/JSON.sh) (MIT/Apache), -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-16-g74cb204 -# -# Exit Codes: -# - 0 sucess (hopefully) -# - 1 can't change to dir -# - 2 can't write to tmp, count or token -# - 3 user / command / file not found -# - 4 unkown command -# - 5 cannot connect to telegram bot -# shellcheck disable=SC2140 - -# are we runnig in a terminal? -if [ -t 1 ] && [ "$TERM" != "" ]; then - CLEAR='clear' - RED='\e[31m' - GREEN='\e[32m' - ORANGE='\e[35m' - NC='\e[0m' -fi - -# get location and name of bashbot.sh -SCRIPT="$0" -REALME="${BASH_SOURCE[0]}" -SCRIPTDIR="$(dirname "${REALME}")" -RUNDIR="$(dirname "$0")" - -MODULEDIR="${SCRIPTDIR}/modules" - - -if [ "${SCRIPT}" != "${REALME}" ] || [ "$1" = "source" ]; then - SOURCE="yes" -else - SCRIPT="./$(basename "${SCRIPT}")" -fi - -if [ "$BASHBOT_HOME" != "" ]; then - SCRIPTDIR="$BASHBOT_HOME" - [ "${BASHBOT_ETC}" = "" ] && BASHBOT_ETC="$BASHBOT_HOME" - [ "${BASHBOT_VAR}" = "" ] && BASHBOT_VAR="$BASHBOT_HOME" -fi - -ADDONDIR="${BASHBOT_ETC:-./addons}" - -RUNUSER="${USER}" # USER is overwritten by bashbot array - -if [ "${SOURCE}" != "yes" ] && [ "$BASHBOT_HOME" = "" ] && ! cd "${RUNDIR}" ; then - echo -e "${RED}ERROR: Can't change to ${RUNDIR} ...${NC}" - exit 1 -fi - -if [ ! -w "." ]; then - echo -e "${ORANGE}WARNING: ${RUNDIR} is not writeable!${NC}" - ls -ld . -fi - - -TOKENFILE="${BASHBOT_ETC:-.}/token" -if [ ! -f "${TOKENFILE}" ]; then - if [ "${CLEAR}" = "" ] && [ "$1" != "init" ]; then - echo "Running headless, run ${SCRIPT} init first!" - exit 2 - else - ${CLEAR} - echo -e "${RED}TOKEN MISSING.${NC}" - echo -e "${ORANGE}PLEASE WRITE YOUR TOKEN HERE OR PRESS CTRL+C TO ABORT${NC}" - read -r token - printf '%s\n' "${token}" > "${TOKENFILE}" - fi -fi - -BOTADMIN="${BASHBOT_ETC:-.}/botadmin" -if [ ! -f "${BOTADMIN}" ]; then - if [ "${CLEAR}" = "" ]; then - echo "Running headless, set botadmin to AUTO MODE!" - printf '%s\n' '?' > "${BOTADMIN}" - else - ${CLEAR} - echo -e "${RED}BOTADMIN MISSING.${NC}" - echo -e "${ORANGE}PLEASE WRITE YOUR TELEGRAM ID HERE OR ENTER '?'${NC}" - echo -e "${ORANGE}TO MAKE FIRST USER TYPING '/start' TO BOTADMIN${NC}" - read -r admin - [ "${admin}" = "" ] && admin='?' - printf '%s\n' "${admin}" > "${BOTADMIN}" - fi -fi - -BOTACL="${BASHBOT_ETC:-.}/botacl" -if [ ! -f "${BOTACL}" ]; then - echo -e "${ORANGE}Create empty ${BOTACL} file.${NC}" - printf '\n' >"${BOTACL}" -fi - -DATADIR="${BASHBOT_VAR:-.}/data-bot-bash" -if [ ! -d "${DATADIR}" ]; then - mkdir "${DATADIR}" -elif [ ! -w "${DATADIR}" ]; then - echo -e "${RED}ERROR: Can't write to ${DATADIR}!.${NC}" - ls -ld "${DATADIR}" - exit 2 -fi - -COUNTFILE="${BASHBOT_VAR:-.}/count" -if [ ! -f "${COUNTFILE}" ]; then - printf '\n' >"${COUNTFILE}" -elif [ ! -w "${COUNTFILE}" ]; then - echo -e "${RED}ERROR: Can't write to ${COUNTFILE}!.${NC}" - ls -l "${COUNTFILE}" - exit 2 -fi - -BOTTOKEN="$(< "${TOKENFILE}")" -URL="${BASHBOT_URL:-https://api.telegram.org/bot}${BOTTOKEN}" - -ME_URL=$URL'/getMe' - -UPD_URL=$URL'/getUpdates?offset=' -GETFILE_URL=$URL'/getFile' - -declare -rx SCRIPT SCRIPTDIR MODULEDIR RUNDIR ADDONDIR TOKENFILE BOTADMIN BOTACL DATADIR COUNTFILE -declare -rx BOTTOKEN URL ME_URL UPD_URL GETFILE_URL - -declare -ax CMD -declare -Ax UPD BOTSENT USER MESSAGE URLS CONTACT LOCATION CHAT FORWARD REPLYTO VENUE iQUERY -export res CAPTION - - -COMMANDS="${BASHBOT_ETC:-.}/commands.sh" -if [ "${SOURCE}" = "yes" ]; then - for modules in ${MODULEDIR:-.}/*.sh ; do - # shellcheck source=./modules/aliases.sh - [ -r "${modules}" ] && source "${modules}" "source" - done -else - if [ ! -f "${COMMANDS}" ] || [ ! -r "${COMMANDS}" ]; then - echo -e "${RED}ERROR: ${COMMANDS} does not exist or is not readable!.${NC}" - ls -l "${COMMANDS}" - exit 3 - fi - # shellcheck source=./commands.sh - source "${COMMANDS}" "source" -fi - - -################# -# BASHBOT INTERNAL functions -# $1 URL, $2 filename in DATADIR -# outputs final filename -download() { - local empty="no.file" file="${2:-${empty}}" - if [[ "$file" = *"/"* ]] || [[ "$file" = "."* ]]; then file="${empty}"; fi - while [ -f "${DATADIR:-.}/${file}" ] ; do file="$RAMDOM-${file}"; done - getJson "$1" >"${DATADIR:-.}/${file}" || return - printf '%s\n' "${DATADIR:-.}/${file}" -} - -# $1 postfix, e.g. chatid -# $2 prefix, back- or startbot- -procname(){ - printf '%s\n' "$2${ME}_$1" -} - -# $1 sting to search for proramm incl. parameters -# retruns a list of PIDs of all current bot proceeses matching $1 -proclist() { - # shellcheck disable=SC2009 - ps -fu "${UID}" | grep -F "$1" | grep -v ' grep'| grep -F "${ME}" | sed 's/\s\+/\t/g' | cut -f 2 -} - -# $1 sting to search for proramm to kill -killallproc() { - local procid; procid="$(proclist "$1")" - if [ "${procid}" != "" ] ; then - # shellcheck disable=SC2046 - kill $(proclist "$1") - sleep 1 - procid="$(proclist "$1")" - # shellcheck disable=SC2046 - [ "${procid}" != "" ] && kill $(proclist -9 "$1") - fi -} - - -# returns true if command exist -_exists() -{ - [ "$(LC_ALL=C type -t "$1")" = "file" ] -} - -# execute function if exists -_exec_if_function() { - [ "$(LC_ALL=C type -t "${1}")" != "function" ] || "$@" -} -# returns true if function exist -_is_function() -{ - [ "$(LC_ALL=C type -t "$1")" = "function" ] -} - -declare -xr DELETE_URL=$URL'/deleteMessage' -delete_message() { - sendJson "${1}" 'message_id: '"${2}"'' "${DELETE_URL}" -} - -get_file() { - [ "$1" = "" ] && return - sendJson "" '"file_id": '"${1}""${GETFILE_URL}" - printf '%s\n' "${URL}"/"$(jsonGetString <<< "${res}" '"result","file_path"')" -} - -# curl is preffered, but may not availible on ebedded systems -TIMEOUT="${BASHBOT_TIMEOUT}" -[[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT="20" - -if [ "${BASHBOT_WGET}" = "" ] && _exists curl ; then - # simple curl or wget call, output to stdout - getJson(){ - # shellcheck disable=SC2086 - curl -sL ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" "$1" - } - # usage: sendJson "chat" "JSON" "URL" - sendJson(){ - local chat=""; - [ "${1}" != "" ] && chat='"chat_id":'"${1}"',' - # shellcheck disable=SC2086 - res="$(curl -s ${BASHBOT_CURL_ARGS} -m "${TIMEOUT}" -d '{'"${chat} $2"'}' -X POST "${3}" \ - -H "Content-Type: application/json" | "${JSONSHFILE}" -s -b -n )" - BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")" - BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "$res")" - } - #$1 Chat, $2 what , $3 file, $4 URL, $5 caption - sendUpload() { - [ "$#" -lt 4 ] && return - if [ "$5" != "" ]; then - # shellcheck disable=SC2086 - res="$(curl -s ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1" -F "$2=@$3;${3##*/}" -F "caption=$5" | "${JSONSHFILE}" -s -b -n )" - else - # shellcheck disable=SC2086 - res="$(curl -s ${BASHBOT_CURL_ARGS} "$4" -F "chat_id=$1" -F "$2=@$3;${3##*/}" | "${JSONSHFILE}" -s -b -n )" - fi - BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")" - } -else - # simple curl or wget call outputs result to stdout - getJson(){ - # shellcheck disable=SC2086 - wget -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - "$1" - } - # usage: sendJson "chat" "JSON" "URL" - sendJson(){ - local chat=""; - [ "${1}" != "" ] && chat='"chat_id":'"${1}"',' - # shellcheck disable=SC2086 - res="$(wget -t 2 -T "${TIMEOUT}" ${BASHBOT_WGET_ARGS} -qO - --post-data='{'"${chat} $2"'}' \ - --header='Content-Type:application/json' "${3}" | "${JSONSHFILE}" -s -b -n )" - BOTSENT[OK]="$(JsonGetLine '"ok"' <<< "$res")" - BOTSENT[ID]="$(JsonGetValue '"result","message_id"' <<< "$res")" - } - sendUpload() { - sendJson "$1" '"text":"Sorry, wget does not support file upload"' "${MSG_URL}" - BOTSENT[OK]="false" - } -fi - -# convert common telegram entities to JSON -# title caption description markup inlinekeyboard -title2Json(){ - local title caption desc markup keyboard - [ "$1" != "" ] && title=',"title":"'$1'"' - [ "$2" != "" ] && caption=',"caption":"'$2'"' - [ "$3" != "" ] && desc=',"description":"'$3'"' - [ "$4" != "" ] && markup=',"parse_mode":"'$4'"' - [ "$5" != "" ] && keyboard=',"reply_markup":"'$5'"' - echo "${title}${caption}${desc}${markup}${keyboard}" -} - -# get bot name -getBotName() { - getJson "$ME_URL" | "${JSONSHFILE}" -s -b -n | JsonGetString '"result","username"' -} - -# pure bash implementaion, done by KayM (@gnadelwartz) -# see https://stackoverflow.com/a/55666449/9381171 -JsonDecode() { - local out="$1" remain="" U="" - local regexp='(.*)\\u[dD]([0-9a-fA-F]{3})\\u[dD]([0-9a-fA-F]{3})(.*)' - while [[ "${out}" =~ $regexp ]] ; do - U=$(( ( (0xd${BASH_REMATCH[2]} & 0x3ff) <<10 ) | ( 0xd${BASH_REMATCH[3]} & 0x3ff ) + 0x10000 )) - remain="$(printf '\\U%8.8x' "${U}")${BASH_REMATCH[4]}${remain}" - out="${BASH_REMATCH[1]}" - done - echo -e "${out}${remain}" -} - -JsonGetString() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]"\(.*\)"$/\1/p' -} -JsonGetLine() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]//p' -} -JsonGetValue() { - sed -n -e '0,/\['"$1"'\]/ s/\['"$1"'\][ \t]\([0-9.,]*\).*/\1/p' -} -# read JSON.sh style data and asssign to an ARRAY -# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling -Json2Array() { - # shellcheck source=./commands.sh - [ "$1" = "" ] || source <( printf "$1"'=( %s )' "$(sed -E -e 's/\t/=/g' -e 's/=(true|false)/="\1"/')" ) -} -# output ARRAY as JSON.sh style data -# $1 ARRAY name, must be declared with "declare -A ARRAY" before calling -Array2Json() { - local key - declare -n ARRAY="$1" - for key in "${!ARRAY[@]}" - do - printf '["%s"]\t"%s"\n' "${key//,/\",\"}" "${ARRAY[${key}]//\"/\\\"}" - done -} - -################ -# processing of updates starts here -process_updates() { - local max num debug="$1" - max="$(sed <<< "${UPDATE}" '/\["result",[0-9]*\]/!d' | tail -1 | sed 's/\["result",//g;s/\].*//g')" - Json2Array 'UPD' <<<"${UPDATE}" - for ((num=0; num<=max; num++)); do - process_client "$num" "${debug}" - done -} -process_client() { - local num="$1" debug="$2" - CMD=( ) - iQUERY[ID]="${UPD["result",${num},"inline_query","id"]}" - if [ "${iQUERY[ID]}" = "" ]; then - [[ "${debug}" = *"debug"* ]] && cat <<< "$UPDATE" >>"MESSAGE.log" - process_message "${num}" "${debug}" - else - [[ "${debug}" = *"debug"* ]] && cat <<< "$UPDATE" >>"INLINE.log" - process_inline "${num}" "${debug}" - fi - ##### - # process inline and message events - # first classic commnad dispatcher - # shellcheck source=./commands.sh - source "${COMMANDS}" "${debug}" & - - # then all registered addons - if [ "${iQUERY[ID]}" = "" ]; then - event_message "${debug}" - else - event_inline "${debug}" - fi - - # last count users - tmpcount="COUNT${CHAT[ID]}" - grep -q "$tmpcount" <"${COUNTFILE}" &>/dev/null || cat <<< "$tmpcount" >>"${COUNTFILE}" -} - -declare -Ax BASBOT_EVENT_INLINE BASBOT_EVENT_MESSAGE BASHBOT_EVENT_CMD BASBOT_EVENT_REPLY BASBOT_EVENT_FORWARD -declare -Ax BASBOT_EVENT_CONTACT BASBOT_EVENT_LOCATION BASBOT_EVENT_FILE BASHBOT_EVENT_TEXT BASHBOT_EVENT_TIMER - -start_timer(){ - # send alarm every ~60 s - while :; do - sleep 59.5 - kill -ALRM $$ - done; -} - -EVENT_TIMER="0" -event_timer() { - local event timer debug="$1" - (( EVENT_TIMER += 1 )) - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_TIMER[@]}" - do - timer="${BASHBOT_EVENT_TIMER[${event}]##*,}" - [[ ! "$timer" =~ ^-*[0-9]+$ ]] && continue - [[ "$timer" =~ ^-*0+$ ]] && continue - if [ "$(( EVENT_TIMER % timer ))" = "0" ]; then - _exec_if_function "${BASHBOT_EVENT_TIMER[${event}]}" "timer" "${debug}" - [ "$(( EVENT_TIMER % timer ))" -lt "0" ] && \ - unset BASHBOT_EVENT_TIMER["${event}"] - fi - done -} - -event_inline() { - local event debug="$1" - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_INLINE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_INLINE[${event}]}" "inline" "${debug}" - done -} -event_message() { - local event debug="$1" - # ${MESSAEG[*]} event_message - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_MESSAGE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_MESSAGE[${event}]}" "messsage" "${debug}" - done - - # ${TEXT[*]} event_text - if [ "${MESSAGE[0]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_TEXT[@]}" - do - _exec_if_function "${BASHBOT_EVENT_TEXT[${event}]}" "text" "${debug}" - done - - # ${CMD[*]} event_cmd - if [ "${CMD[0]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_CMD[@]}" - do - _exec_if_function "${BASHBOT_EVENT_CMD[${event}]}" "command" "${debug}" - done - fi - fi - - # ${REPLYTO[*]} event_replyto - if [ "${REPLYTO[UID]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_REPLYTO[@]}" - do - _exec_if_function "${BASHBOT_EVENT_REPLYTO[${event}]}" "replyto" "${debug}" - done - fi - - # ${FORWARD[*]} event_forward - if [ "${FORWARD[UID]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_FORWARD[@]}" - do - _exec_if_function && "${BASHBOT_EVENT_FORWARD[${event}]}" "forward" "${debug}" - done - fi - - # ${CONTACT[*]} event_contact - if [ "${CONTACT[FIRST_NAME]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_CONTACT[@]}" - do - _exec_if_function "${BASHBOT_EVENT_CONTACT[${event}]}" "contact" "${debug}" - done - fi - - # ${VENUE[*]} event_location - # ${LOCALTION[*]} event_location - if [ "${LOCATION[*]}" != "" ] || [ "${VENUE[TITLE]}" != "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_LOCATION[@]}" - do - _exec_if_function "${BASHBOT_EVENT_LOCATION[${event}]}" "location" "${debug}" - done - fi - - # ${URLS[*]} event_file - if [ "${URLS[*]}" = "" ]; then - # shellcheck disable=SC2153 - for event in "${!BASHBOT_EVENT_FILE[@]}" - do - _exec_if_function "${BASHBOT_EVENT_FILE[${event}]}" "file" "${debug}" - done - fi - -} -process_inline() { - local num="${1}" - iQUERY[0]="$(JsonDecode "${UPD["result",${num},"inline_query","query"]}")" - iQUERY[USER_ID]="${UPD["result",${num},"inline_query","from","id"]}" - iQUERY[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","first_name"]}")" - iQUERY[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","last_name"]}")" - iQUERY[USERNAME]="$(JsonDecode "${UPD["result",${num},"inline_query","from","username"]}")" - # event_inline & -} -process_message() { - local num="$1" - # Message - MESSAGE[0]="$(JsonDecode "${UPD["result",${num},"message","text"]}" | sed 's#\\/#/#g')" - MESSAGE[ID]="${UPD["result",${num},"message","message_id"]}" - - # Chat - CHAT[ID]="${UPD["result",${num},"message","chat","id"]}" - CHAT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","last_name"]}")" - CHAT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","chat","first_name"]}")" - CHAT[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","chat","username"]}")" - CHAT[TITLE]="$(JsonDecode "${UPD["result",${num},"message","chat","title"]}")" - CHAT[TYPE]="$(JsonDecode "${UPD["result",${num},"message","chat","type"]}")" - CHAT[ALL_ADMIN]="${UPD["result",${num},"message","chat","all_members_are_administrators"]}" - CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]="${CHAT[ALL_ADMIN]}" # backward compatibility - - # User - USER[ID]="${UPD["result",${num},"message","from","id"]}" - USER[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","first_name"]}")" - USER[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","from","last_name"]}")" - USER[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","from","username"]}")" - - # in reply to message from - REPLYTO[UID]="${UPD["result",${num},"message","reply_to_message","from","id"]}" - if [ "${REPLYTO[UID]}" != "" ]; then - REPLYTO[0]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","text"]}")" - REPLYTO[ID]="${UPD["result",${num},"message","reply_to_message","message_id"]}" - REPLYTO[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","first_name"]}")" - REPLYTO[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","last_name"]}")" - REPLYTO[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","reply_to_message","from","username"]}")" - fi - - # forwarded message from - FORWARD[UID]="${UPD["result",${num},"message","forward_from","id"]}" - if [ "${FORWARD[UID]}" != "" ]; then - FORWARD[ID]="${MESSAGE[ID]}" # same as message ID - FORWARD[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","first_name"]}")" - FORWARD[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","last_name"]}")" - FORWARD[USERNAME]="$(JsonDecode "${UPD["result",${num},"message","forward_from","username"]}")" - fi - - # Audio - URLS[AUDIO]="$(get_file "${UPD["result",${num},"message","audio","file_id"]}")" - # Document - URLS[DOCUMENT]="$(get_file "${UPD["result",${num},"message","document","file_id"]}")" - # Photo - URLS[PHOTO]="$(get_file "${UPD["result",${num},"message","photo",0,"file_id"]}")" - # Sticker - URLS[STICKER]="$(get_file "${UPD["result",${num},"message","sticker","file_id"]}")" - # Video - URLS[VIDEO]="$(get_file "${UPD["result",${num},"message","video","file_id"]}")" - # Voice - URLS[VOICE]="$(get_file "${UPD["result",${num},"message","voice","file_id"]}")" - - # Contact - CONTACT[FIRST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","first_name"]}")" - if [ "${CONTACT[FIRST_NAME]}" != "" ]; then - CONTACT[USER_ID]="$(JsonDecode "${UPD["result",${num},"message","contact","user_id"]}")" - CONTACT[LAST_NAME]="$(JsonDecode "${UPD["result",${num},"message","contact","last_name"]}")" - CONTACT[NUMBER]="${UPD["result",${num},"message","contact","phone_number"]}" - CONTACT[VCARD]="$(JsonGetString '"result",'"${num}"',"message","contact","vcard"' <<<"${UPDATE}")" - fi - - # vunue - VENUE[TITLE]="$(JsonDecode "${UPD["result",${num},"message","venue","title"]}")" - if [ "${VENUE[TITLE]}" != "" ]; then - VENUE[ADDRESS]="$(JsonDecode "${UPD["result",${num},"message","venue","address"]}")" - VENUE[LONGITUDE]="${UPD["result",${num},"message","venue","location","longitude"]}" - VENUE[LATITUDE]="${UPD["result",${num},"message","venue","location","latitude"]}" - VENUE[FOURSQUARE]="${UPD["result",${num},"message","venue","foursquare_id"]}" - fi - - # Caption - CAPTION="$(JsonDecode "${UPD["result",${num},"message","caption"]}")" - - # Location - LOCATION[LONGITUDE]="${UPD["result",${num},"message","location","longitude"]}" - LOCATION[LATITUDE]="${UPD["result",${num},"message","location","latitude"]}" - - # split message in command and args - if [[ "${MESSAGE[0]}" = "/"* ]]; then - set -f; unset IFS - # shellcheck disable=SC2206 - CMD=( ${MESSAGE[0]} ) - set +f - fi -} - -######################### -# main get updates loop, should never terminate -start_bot() { - local DEBUG="$1" - local OFFSET=0 - local mysleep="100" # ms - local addsleep="100" - local maxsleep="$(( ${BASHBOT_SLEEP:-5000} + 100 ))" - [[ "${DEBUG}" = *"debug" ]] && exec &>>"DEBUG.log" - [ "${DEBUG}" != "" ] && date && echo "Start BASHBOT in Mode \"${DEBUG}\"" - [[ "${DEBUG}" = "xdebug"* ]] && set -x - #cleaup old pipes and empty logfiles - find "${DATADIR}" -type p -delete - find "${DATADIR}" -size 0 -name "*.log" -delete - # load addons on startup - for addons in ${ADDONDIR:-.}/*.sh ; do - # shellcheck source=./modules/aliases.sh - [ -r "${addons}" ] && source "${addons}" "startbot" "${DEBUG}" - done - # start timer events - if _is_function start_timer ; then - # shellcheck disable=SC2064 - trap "event_timer $DEBUG" ALRM - start_timer & - # shellcheck disable=SC2064 - trap "kill -9 $!; exit" EXIT INT HUP TERM QUIT - fi - while true; do - UPDATE="$(getJson "$UPD_URL$OFFSET" | "${JSONSHFILE}" -s -b -n)" - - # Offset - OFFSET="$(grep <<< "${UPDATE}" '\["result",[0-9]*,"update_id"\]' | tail -1 | cut -f 2)" - ((OFFSET++)) - - if [ "$OFFSET" != "1" ]; then - mysleep="100" - process_updates "${DEBUG}" - fi - # adaptive sleep in ms rounded to next lower second - [ "${mysleep}" -gt "999" ] && sleep "${mysleep%???}" - # bash aritmetic - ((mysleep+= addsleep , mysleep= mysleep>maxsleep ?maxsleep:mysleep)) - done -} - -# initialize bot environment, user and permissions -bot_init() { - local DEBUG="$1" - # upgrade from old version - local OLDTMP="${BASHBOT_VAR:-.}/tmp-bot-bash" - [ -d "${OLDTMP}" ] && { mv -n "${OLDTMP}/"* "${DATADIR}"; rmdir "${OLDTMP}"; } - [ -f "modules/inline.sh" ] && rm -f "modules/inline.sh" - # load addons on startup - for addons in ${ADDONDIR:-.}/*.sh ; do - # shellcheck source=./modules/aliases.sh - [ -r "${addons}" ] && source "${addons}" "init" "${DEBUG}" - done - #setup bashbot - [[ "${UID}" -eq "0" ]] && RUNUSER="nobody" - echo -n "Enter User to run basbot [$RUNUSER]: " - read -r TOUSER - [ "$TOUSER" = "" ] && TOUSER="$RUNUSER" - if ! id "$TOUSER" &>/dev/null; then - echo -e "${RED}User \"$TOUSER\" not found!${NC}" - exit 3 - else - # shellcheck disable=SC2009 - oldbot="$(ps -fu "$TOUSER" | grep startbot | grep -v -e 'grep' -e '\-startbot' )" - [ "${oldbot}" != "" ] && \ - echo -e "${ORANGE}Warning: At least one not upgraded TMUX bot is running! You must stop it with kill command:${NC}\\n${oldbot}" - echo "Adjusting user \"${TOUSER}\" files and permissions ..." - [ -w "bashbot.rc" ] && sed -i '/^[# ]*runas=/ s/runas=.*$/runas="'$TOUSER'"/' "bashbot.rc" - chown -R "$TOUSER" . ./* - chmod 711 . - chmod -R a-w ./* - chmod -R u+w "${COUNTFILE}" "${DATADIR}" "${BOTADMIN}" ./*.log 2>/dev/null - chmod -R o-r,o-w "${COUNTFILE}" "${DATADIR}" "${TOKENFILE}" "${BOTADMIN}" "${BOTACL}" 2>/dev/null - ls -la - fi -} - -if ! _is_function send_message ; then - echo -e "${RED}ERROR: send_message is not availible, did you deactivate ${MODULEDIR}/sendMessage.sh?${NC}" - exit 1 -fi - -JSONSHFILE="${BASHBOT_JSONSH:-${RUNDIR}/JSON.sh/JSON.sh}" -[[ "${JSONSHFILE}" != *"/JSON.sh" ]] && echo -e "${RED}ERROR: \"${JSONSHFILE}\" ends not with \"JSONS.sh\".${NC}" && exit 3 - -if [ ! -f "${JSONSHFILE}" ]; then - echo "Seems to be first run, Downloading ${JSONSHFILE}..." - [[ "${JSONSHFILE}" = "${RUNDIR}/JSON.sh/JSON.sh" ]] && mkdir "JSON.sh" 2>/dev/null && chmod +w "JSON.sh" - getJson "https://cdn.jsdelivr.net/gh/dominictarr/JSON.sh/JSON.sh" >"${JSONSHFILE}" - chmod +x "${JSONSHFILE}" -fi - -if [ "${SOURCE}" != "yes" ] && [ "$1" != "init" ] && [ "$1" != "help" ] && [ "$1" != "" ]; then - ME="$(getBotName)" - if [ "$ME" = "" ]; then - echo -e "${RED}ERROR: Can't connect to Telegram Bot! May be your TOKEN is invalid ...${NC}" - exit 1 - fi -fi - -# source the script with source as param to use functions in other scripts -# do not execute if read from other scripts - -if [ "${SOURCE}" != "yes" ]; then - - ############## - # internal options only for use from bashbot and developers - case "$1" in - "outproc") # forward output from interactive and jobs to chat - [ "$3" = "" ] && echo "No file to read from" && exit 3 - [ "$2" = "" ] && echo "No chat to send to" && exit 3 - while read -r line ;do - [ "$line" != "" ] && send_message "$2" "$line" - done - rm -f -r "${DATADIR:-.}/$3" - [ -s "${DATADIR:-.}/$3.log" ] || rm -f "${DATADIR:-.}/$3.log" - exit - ;; - "startbot" ) - start_bot "$2" - exit - ;; - "init") # adjust users and permissions - bot_init "$2" - exit - ;; - esac - - - ############### - # "official" arguments as shown to users - SESSION="${ME:-unknown}-startbot" - BOTPID="$(proclist "${SESSION}")" - - case "$1" in - "count") - echo "A total of $(wc -l <"${COUNTFILE}") users used me." - exit - ;; - "broadcast") - NUMCOUNT="$(wc -l <"${COUNTFILE}")" - echo "Sending the broadcast $* to $NUMCOUNT users." - [ "$NUMCOUNT" -gt "300" ] && sleep="sleep 0.5" - shift - while read -r f; do send_markdown_message "${f//COUNT}" "$*"; $sleep; done <"${COUNTFILE}" - ;; - "status") - if [ "${BOTPID}" != "" ]; then - echo -e "${GREEN}Bot is running.${NC}" - exit - else - echo -e "${ORANGE}Bot not running.${NC}" - exit 5 - fi - ;; - - "start") - # shellcheck disable=SC2086 - [ "${BOTPID}" != "" ] && kill ${BOTPID} - nohup "$SCRIPT" "startbot" "$2" "${SESSION}" &>/dev/null & - echo "Session Name: ${SESSION}" - if [ "$(proclist "${SESSION}")" != "" ]; then - echo -e "${GREEN}Bot started successfully.${NC}" - else - echo -e "${RED}An error occurred while starting the bot.${NC}" - exit 5 - fi - ;; - "kill"|"stop") - if [ "${BOTPID}" != "" ]; then - # shellcheck disable=SC2086 - if kill ${BOTPID}; then - echo -e "${GREEN}OK. Bot stopped successfully.${NC}" - else - echo -e "${RED}An error occured while stopping bot.${NC}" - exit 5 - fi - fi - exit - ;; - "resumeb"* | "killb"* | "suspendb"*) - _is_function job_control || { echo -e "${RED}Module background is not availible!${NC}"; exit 3; } - job_control "$1" - ;; - "help") - less "README.txt" - exit - ;; - *) - echo -e "${RED}${REALME}: BAD REQUEST${NC}" - echo -e "${RED}Available arguments: start, stop, kill, status, count, broadcast, help, suspendback, resumeback, killback${NC}" - exit 4 - ;; - esac - - # warn if root - if [[ "${UID}" -eq "0" ]] ; then - echo -e "\\n${ORANGE}WARNING: ${SCRIPT} was started as ROOT (UID 0)!${NC}" - echo -e "${ORANGE}You are at HIGH RISK when running a Telegram BOT with root privilegs!${NC}" - fi -fi # end source diff --git a/DIST/telegram-bot-bash/commands.sh b/DIST/telegram-bot-bash/commands.sh deleted file mode 100644 index 3cf4aff..0000000 --- a/DIST/telegram-bot-bash/commands.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -# file: commands.sh -# do not edit this file, instead place all your commands in mycommands.sh - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-14-gafc669c -# - -# adjust your language setting here, e.g.when run from other user or cron. -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# to change the default info message overwrite bashbot_info in mycommands.sh -bashbot_info='This is bashbot, the Telegram bot written entirely in bash. -It features background tasks and interactive chats, and can serve as an interface for CLI programs. -It currently can send, recieve and forward messages, custom keyboards, photos, audio, voice, documents, locations and video files. -' - -# to change the default help messages overwrite in mycommands.sh -bashbot_help='*Available commands*: -*• /start*: _Start bot and get this message_. -*• /help*: _Get this message_. -*• /info*: _Get shorter info message about this bot_. -*• /question*: _Start interactive chat_. -*• /cancel*: _Cancel any currently running interactive chats_. -*• /kickme*: _You will be autokicked from the chat_. -*• /leavechat*: _The bot will leave the group with this command _. -Written by Drew (@topkecleon), Daniil Gentili (@danogentili) and KayM(@gnadelwartz). -Get the code in my [GitHub](http://github.com/topkecleon/telegram-bot-bash) -' - -# load modues on startup and always on on debug -if [ "${1}" = "source" ] || [[ "${1}" = *"debug"* ]] ; then - # load all readable modules - for modules in ${MODULEDIR:-.}/*.sh ; do - # shellcheck source=./modules/aliases.sh - [ -r "${modules}" ] && source "${modules}" "${1}" - done -fi - -# defaults to no inline and nonsense home dir -export INLINE="0" -export FILE_REGEX='/home/user/allowed/.*' - - -# load mycommands -# shellcheck source=./commands.sh -[ -r "${BASHBOT_ETC:-.}/mycommands.sh" ] && source "${BASHBOT_ETC:-.}/mycommands.sh" "${1}" - - -if [ "${1}" != "source" ];then - # detect inline commands.... - # no default commands, all processing is done in myinlines() - if [ "$INLINE" != "0" ] && [ "${iQUERY[ID]}" != "" ]; then - # forward iinline query to optional dispatcher - _exec_if_function myinlines - - # regular (gobal) commands ... - # your commands are in mycommands() - else - - ################### - # user defined commands must placed in mycommands - _exec_if_function mycommands - - # run commands if true (0) is returned or if mycommands dose not exist - # shellcheck disable=SC2181 - if [ "$?" = "0" ]; then - case "${MESSAGE}" in - ################################################ - # GLOBAL commands start here, edit messages only - '/info'*) - _markdown_message "${bashbot_info}" - ;; - '/start'*) - send_action "${CHAT[ID]}" "typing" - _is_botadmin && _markdown_message "You are *BOTADMIN*." - if _is_botadmin || _is_allowed "start" ; then - _markdown_message "${bashbot_help}" - else - _message "You are not allowed to start Bot." - fi - ;; - - '/help'*) - _markdown_message "${bashbot_help}" - ;; - '/leavechat'*) # bot leave chat if user is admin in chat - if _is_admin ; then - _markdown_message "*LEAVING CHAT...*" - _leave - fi - ;; - - '/kickme'*) - _kick_user "${USER[ID]}" - _unban_user "${USER[ID]}" - ;; - - '/cancel'*) - checkproc - if [ "$res" -eq 0 ] ; then killproc && _message "Command canceled.";else _message "No command is currently running.";fi - ;; - *) # forward messages to optional dispatcher - _exec_if_function send_interactive "${CHAT[ID]}" "${MESSAGE}" - ;; - esac - fi - fi -fi diff --git a/DIST/telegram-bot-bash/doc/0_install.md b/DIST/telegram-bot-bash/doc/0_install.md deleted file mode 100644 index 7c5dadd..0000000 --- a/DIST/telegram-bot-bash/doc/0_install.md +++ /dev/null @@ -1,91 +0,0 @@ -#### [Home](../README.md) - -## Install bashbot - -1. Go to the directory you want to install bashbot, e.g. - * your $HOME directory (install and run with your user-ID) - * /usr/local if you want to run as service -2. [Download latest release zip from github](https://github.com/topkecleon/telegram-bot-bash/releases) and extract all files. -3. Change into the directory ```telegram-bot-bash``` -4. Acticate the bot example commands ``cp mycommands.sh.dist mycommands.sh``` -5. Run ```./bashbot.sh init``` to setup the environment and enter your Bots token given by botfather. - -Edit 'mycommands.sh to your needs. -Now your Bot is ready to start ... - -**If you are new to Bot development read [Bots: An introduction for developers](https://core.telegram.org/bots)** - -### Install from Github - -As an alternative to download the zip files, you can clone the github repository to get the latest improvements/fixes. - -1. Go to the directory you want to install bashbot, e.g. - * your $HOME directory (install and run with your user-ID) - * /usr/local if you want to run as service -2. Run ```git clone https://github.com/topkecleon/telegram-bot-bash.git``` -3. Change into the directory ```telegram-bot-bash``` -4. Run ``` test/ALL-tests.sh``` and if everthing finish OK ... -5. Run ```sudo ./bashbot.sh init``` to setup the environment and enter your Bots token given by botfather. - -### Update bashbot - -**Note: all files including 'mycommands.sh' may overwritten, make a backup!** - -1. Go to the directory where you had installed bashbot, e.g. - * your $HOME directory - * /usr/local -2. [Download latest release zip from github](https://github.com/topkecleon/telegram-bot-bash/releases) -3. Stop all running instances of bashbot -4. Extract all files to your existing bashbot dir -5. Run ```sudo ./bashbot.sh init``` to setup your environment after the update - -If you modified 'commands.sh' move your changes to 'mycommands.sh', this avoids overwrrite of you changes on updates. - -Now you can restart your bashbot instances. - -### Notes on Updates - -#### removal of TMUX -From version 0.80 on TMUX is no longer needed and the bachsbot command 'attach' is deleted. Old function 'inproc' -is replaced by 'send_interactive'. send_interactive does checks if an interactive job is running internaly. -Pls check if you make use of inproc and remove it including the old checks, e.g. -```bash -if tmux ls | grep -v send | grep -q "$copname"; then inproc; fi -# or -[ checkprog ] && inproc -``` -must be replaced by ```send_interactive "${CHATD[ID]}" "${MESSAGE}"``` - -### Do not edit commands.sh -From version 0.60 on your commands must be placed in 'mycommands.sh'. If you update from a version with your commands -in 'commands.sh' move all your commands and functions to 'mycommands.sh'. - -From version 0.80 on 'commands.sh' will be overwritten on update! - -#### Location of var / tmp / data dirs -From version 0.70 on the tmp dir is renamed to 'data-bot-bash' to reflect the fact that not only temporary files are stored. an existing 'tmp-bot-bash' will be automatically renamed after update. - -From version 0.50 on the temporary files are no more placed in '/tmp'. instead a dedicated tmp dir is used. - -#### Changes to send_keyboard in v0.6 -From Version 0.60 on keybord format for ```send_keyboard``` and ```send_message "mykeyboardstartshere ..."``` was changed. -Keybords are now defined in JSON Array notation e.g. "[ \\"yes\\" , \\"no\\" ]". -This has the advantage that you can create any type of keyboard supported by Telegram. -The old format is supported for backward compatibility, but may fail for corner cases. - -*Example Keyboards*: - -- yes no in two rows: - - OLD format: 'yes' 'no' *(two strings)* - - NEW format: '[ "yes" ] , [ "no" ]' *(two arrays with a string)* -- new layouts made easy with NEW format: - - Yes No in one row: '[ "yes" , "no" ]' - - Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]' - - numpad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]' - - - -#### [Next Create Bot](1_firstbot.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/1_firstbot.md b/DIST/telegram-bot-bash/doc/1_firstbot.md deleted file mode 100644 index 7341491..0000000 --- a/DIST/telegram-bot-bash/doc/1_firstbot.md +++ /dev/null @@ -1,69 +0,0 @@ -#### [Home](../README.md) -## Create a Telegram Bot with botfather -**[BotFather is the one bot to rule them all](https://core.telegram.org/bots#3-how-do-i-create-a-bot). It will help you create new bots and change settings for existing ones.** [Commands known by Botfather](https://core.telegram.org/bots#generating-an-authorization-token) - -### Creating a new Bot - -1. Message @botfather https://telegram.me/botfather with the following -text: `/newbot` - If you don't know how to message by username, click the search -field on your Telegram app and type `@botfather`, you should be able -to initiate a conversation. Be careful not to send it to the wrong -contact, because some users has similar usernames to `botfather`. - - ![botfather initial conversation](http://i.imgur.com/aI26ixR.png) - -2. @botfather replies with `Alright, a new bot. How are we going to -call it? Please choose a name for your bot.` - -3. Type whatever name you want for your bot. - -4. @botfather replies with `Good. Now let's choose a username for your -bot. It must end in bot. Like this, for example: TetrisBot or -tetris_bot.` - -5. Type whatever username you want for your bot, minimum 5 characters, -and must end with `bot`. For example: `telesample_bot` - -6. @botfather replies with: - - Done! Congratulations on your new bot. You will find it at -telegram.me/telesample_bot. You can now add a description, about -section and profile picture for your bot, see /help for a list of -commands. - - Use this token to access the HTTP API: - 123456789:AAG90e14-0f8-40183D-18491dDE - - For a description of the Bot API, see this page: -https://core.telegram.org/bots/api - -7. Note down the 'token' mentioned above. - -8. Type `/setprivacy` to @botfather. - - ![botfather later conversation](http://i.imgur.com/tWDVvh4.png) - -9. @botfather replies with `Choose a bot to change group messages settings.` - -10. Type `@telesample_bot` (change to the username you set at step 5 -above, but start it with `@`) - -11. @botfather replies with - - 'Enable' - your bot will only receive messages that either start -with the '/' symbol or mention the bot by username. - 'Disable' - your bot will receive all messages that people send to groups. - Current status is: ENABLED - -12. Type `Disable` to let your bot receive all messages sent to a -group. This step is up to you actually. - -13. @botfather replies with `Success! The new status is: DISABLED. /help` - - -#### [Prev Installation](0_install.md) -#### [Next Getting started](2_usage.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/2_usage.md b/DIST/telegram-bot-bash/doc/2_usage.md deleted file mode 100644 index 3d2b711..0000000 --- a/DIST/telegram-bot-bash/doc/2_usage.md +++ /dev/null @@ -1,225 +0,0 @@ -#### [Home](../README.md) -## Gettting Started - -The Bots standard commands are in the commands dispatcher ```commands.sh```, Do not edit this file! Add your commands and functions to ```mycommands.sh```. In 'mycommands.sh.dist' you find examples how to add own commands and overwrite existing ones. See [Best practices](5_practice.md) for more information. - -Once you're done with editing start the Bot with ```./bashbot.sh start```. To stop the Bot run ```./bashbot.sh kill``` - -If something doesn't work as expected, debug with ```./bashbot.sh startbot DEBUG```, where DEBUG can be 'debug', 'xdebug' or 'xdebugx'. -See [Bashbot Development](7_develop.md) for more information. - -To use the functions provided in this script in other scripts simply source bashbot: ```source bashbot.sh source```. see [Expert Use](8_expert.md#Expert-use) - -Have FUN! - ----- - -### Files -``` -. -├── bashbot.sh # main bashbot script - do not edit -├── commands.sh # command dispatcher - do not edit -├── mycommands.sh # place your functions and commands here! -├── JSON.sh # bashbots JSON parser, see https://github.com/dominictarr/JSON.sh -│ -├── modules # optional functions, sourced by commands.sh -│   ├── aliases.sh # to disable modules rename them xxx.sh.off -│   ├── answerInline.sh -│   ├── jsonDB.sh -│   ├── background.sh -│   ├── chatMember.sh -│   └── sendMessage.sh # main send message functions, do not disable -│ -├── addons # optional addons, disbaled by default -│   ├── example.sh # to enable addons change their XXX_ENABLE to true -│   └── xxxxxage.sh -│ -├── bashbot.rc # start/stop script if you run basbot as service -│ -├── examples # example scripts and configs for bashbot -│   └── bashbot.cron # example crontab -│ -├── doc # Documentation and License -├── html -├── LICENSE -├── README.html -├── README.md -└── README.txt - -``` - ----- - -## Managing your Bot -#### Note: running bashbot as root is highly danger and not recommended. See Expert use. - -### Start / Stop -Start or Stop your Bot use the following commands: -```bash -./bashbot.sh start -``` -```bash -./bashbot.sh kill -``` - -### User count -To count the total number of users that ever used the bot run the following command: -```bash -./bashbot.sh count -``` - -### Sending broadcasts to all users -To send a broadcast to all of users that ever used the bot run the following command: -```bash -./bashbot.sh broadcast "Hey! I just wanted to let you know that the bot's been updated!" -``` - ----- - -## Recieve data -Evertime a Message is recieved, you can read incoming data using the following variables: - -### Regular Messages - -* ```${MESSAGE}```: Current message -* ```${MESSAGE[ID]}```: ID of current message -* ```$CAPTION```: Captions -* ```$REPLYTO```: Original message wich was replied to -* ```$USER```: This array contains the First name, last name, username and user id of the sender of the current message. - * ```${USER[ID]}```: User id - * ```${USER[FIRST_NAME]}```: User's first name - * ```${USER[LAST_NAME]}```: User's last name - * ```${USER[USERNAME]}```: Username -* ```$CHAT```: This array contains the First name, last name, username, title and user id of the chat of the current message. - * ```${CHAT[ID]}```: Chat id - * ```${CHAT[FIRST_NAME]}```: Chat's first name - * ```${CHAT[LAST_NAME]}```: Chat's last name - * ```${CHAT[USERNAME]}```: Username - * ```${CHAT[TITLE]}```: Title - * ```${CHAT[TYPE]}```: Type - * ```${CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]}```: All members are administrators (true if true) -* ```$REPLYTO```: This array contains the First name, last name, username and user id of the ORIGINAL sender of the message REPLIED to. - * ```${REPLYTO[ID]}```: ID of message wich was replied to - * ```${REPLYTO[UID]}```: Original user's id - * ```${REPLYTO[FIRST_NAME]}```: Original user's first name - * ```${REPLYTO[LAST_NAME]}```: Original user's' last name - * ```${REPLYTO[USERNAME]}```: Original user's username -* ```$FORWARD```: This array contains the First name, last name, username and user id of the ORIGINAL sender of the FORWARDED message. - * ```${FORWARD[ID]}```: Same as MESSAGE[ID] if message is forwarded - * ```${FORWARD[UID]}```: Original user's id - * ```${FORWARD[FIRST_NAME]}```: Original user's first name - * ```${FORWARD[LAST_NAME]}```: Original user's' last name - * ```${FORWARD[USERNAME]}```: Original user's username -* ```$URLS```: This array contains documents, audio files, voice recordings and stickers as URL. - * ```${URLS[AUDIO]}```: Audio files - * ```${URLS[VIDEO]}```: Videos - * ```${URLS[PHOTO]}```: Photos (maximum quality) - * ```${URLS[VOICE]}```: Voice recordings - * ```${URLS[STICKER]}```: Stickers - * ```${URLS[DOCUMENT]}```: Any other file -* ```$CONTACT```: This array contains info about contacts sent in a chat. - * ```${CONTACT[ID]}```: User id - * ```${CONTACT[NUMBER]}```: Phone number - * ```${CONTACT[FIRST_NAME]}```: First name - * ```${CONTACT[LAST_NAME]}```: Last name - * ```${CONTACT[VCARD]}```: User's complete Vcard -* ```$LOCATION```: This array contains info about locations sent in a chat. - * ```${LOCATION[LONGITUDE]}```: Longitude - * ```${LOCATION[LATITUDE]}```: Latitude -* ```$VENUE```: This array contains info about venue (a place) sent in a chat. - * ```${VENUE[TITLE]}```: Name of the place - * ```${VENUE[ADDRESS]}```: Address of the place - * ```${VENUE[LONGITUDE]}```: Longitude - * ```${VENUE[LATITUDE]}```: Latitude - * ```${VENUE[FOURSQUARE]}```: Fouresquare ID - -### Inline queries -Evertime a Message is recieved, you can read incoming data using the following variables: - -* ```${iQUERY}```: Current inline query -* ```$iQUERY```: This array contains the ID, First name, last name, username and user id of the sender of the current inline query. - * ```${iQUERY[ID]}```: Inline query ID - * ```${iQUERY[USER_ID]}```: User's id - * ```${iQUERY[FIRST_NAME]}```: User's first name - * ```${iQUERY[LAST_NAME]}```: User's last name - -## Usage of bashbot functions - -#### sending messages -To send messages use the ```send_xxx_message``` functions. - -To send regular text without any markdown use: -```bash -send_text_message "${CHAT[ID]}" "lol" -``` -To send text with markdown: -```bash -send_markdown_message "${CHAT[ID]}" "lol *bold*" -``` -To send text with html: -```bash -send_html_message "${CHAT[ID]}" "lol bold" -``` - -To forward messages use the ```forward``` function: -```bash -forward "${CHAT[ID]}" "from_chat_id" "message_id" -``` - -If your Bot is Admin in a Chat you can delete every message, if not you can delete only your messages. -To delete a message with a known ${MESSAGE[ID]} you can simple use: -```bash -delete_message "${CHAT[ID]}" "${MESSAGE[ID]}" -``` - -#### send_message -In addition there is a universal send_massage function which can output any type of message. -This function is used to process output from external scrips like interactive chats or background jobs. -**For safety and performance reasons I recommend to use send_xxxx_message functions above for sending messages** -```bash -send_message "${CHAT[ID]}" "lol" -``` -To send html or markdown put the following strings before the text, depending on the parsing mode you want to enable: -```bash -send_message "${CHAT[ID]}" "markdown_parse_mode lol *bold*" -``` -```bash -send_message "${CHAT[ID]}" "html_parse_mode lol bold" -``` -This function also allows a third parameter that disables additional function parsing (for safety use this when reprinting user input): -```bash -send_message "${CHAT[ID]}" "lol" "safe" -``` -**See also [Interactive chats](3_advanced.md#Interactive-Chats)** - - -#### Send files, locations, keyboards. -To send images, videos, voice files, photos etc. use the ```send_photo``` function (remember to change the safety Regex @ line 14 of command.sh to allow sending files only from certain directories): -```bash -send_file "${CHAT[ID]}" "/home/user/doge.jpg" "Lool" -``` -To send custom keyboards use the ```send_keyboard``` function: -```bash -send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" '[ "Yep" , "No" ]' # note the simgle quotes! -send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" "[ \\"Yep\\" , \\"No\\" ]" # within double quotes you must excape the inside double quots -``` -To send locations use the ```send_location``` function: -```bash -send_location "${CHAT[ID]}" "Latitude" "Longitude" -``` -To send venues use the ```send_venue``` function: -```bash -send_venue "${CHAT[ID]}" "Latitude" "Longitude" "Title" "Address" "optional foursquare id" -``` -To send a chat action use the ```send_action``` function. -Allowed values: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for locations. -```bash -send_action "${CHAT[ID]}" "action" -``` -**See also [Bashbot function reference](6_reference.md#Interactive_Chats)** - -#### [Prev Create Bot](1_firstbot.md) -#### [Next Advanced Usage](3_advanced.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/3_advanced.md b/DIST/telegram-bot-bash/doc/3_advanced.md deleted file mode 100644 index 9954963..0000000 --- a/DIST/telegram-bot-bash/doc/3_advanced.md +++ /dev/null @@ -1,184 +0,0 @@ -#### [Home](../README.md) -## Advanced Features - -### Access control -Bashbot offers functions to check what Telegram capabilities like 'chat admin' or 'chat creator' the given user has: - -```bash -# return true if user is admin/owner of the bot -# -> botadmin is stored in file './botadmin' -user_is_botadmin "user" - -# return true if user is creator or admin of a chat -user_is_admin "chat" "user" - -# return true if user is creator of a chat or it's a one to one chat -user_is_creator "chat" "user" - -# examples: -user_is_botadmin "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*." - -user_is_admin "${CHAT[ID]}" "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *CHATADMIN*." - -``` -In addition you can check individual capabilities of users as you must define in the file ```./botacl```: -```bash -# file: botacl -# a user not listed here, will return false from 'user_is_allowed' -# -# Format: -# user:ressource:chat - -# allow user 123456789 access to all resources in all chats -123456789:*:* - -# allow user 12131415 to start bot in all chats -12131415:start:* - -# allow user 987654321 only to start bot in chat 98979695 -987654321:start:98979695 - -# * are only allowed on the right hand side and not for user! -# the following exaples are NOT valid! -*:*:* -*:start:* -*:*:98979695 -``` -You must use the function ```user_is_allowed``` to check if a user has the capability to do something. Example: Check if user has capability to start bot. -```bash - case "$MESSAGE" in - ################################################ - # GLOBAL commands start here, only edit messages - '/start'*) - user_is_botadmin "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*." - if user_is_allowed "${USER[ID]}" "start" "${CHAT[ID]}" ; then - bot_help "${CHAT[ID]}" - else - send_normal_message "${CHAT[ID]}" "You are not allowed to start Bot." - ;; - esac -``` -**See also [Bashbot User Access Control functions](6_reference.md#User-Access-Control)** - -### Interactive Chats -To create interactive chats, write *(or edit the 'exmaples/question.sh' script)* a bash *(or C or python)* script, make it executable -and then use the 'startproc' function to start the script. -The output of the script will be sent to the user and user input will be sent to the script. -To stop the script use the function 'killprog' - -The output of the script will be processed by 'send_messages' to enable you to not only send text, but also keyboards, files, locations and more. -Each newline in the output will start an new message to the user, to have line breaks in your message you can use 'mynewlinestartshere'. - -To open up a keyboard in an interactive script, print out the keyboard layout in the following way: -```bash -echo "Text that will appear in chat? mykeyboardstartshere [ \"Yep, sure\" , \"No, highly unlikely\" ]" -``` -Same goes for files: -```bash -echo "Text that will appear in chat? myfilelocationstartshere /home/user/doge.jpg" -``` -And buttons: -```bash -echo "Text that will appear in chat. mybtextstartshere Klick me myburlstartshere https://dealz.rrr.de" -``` -And locations: -```bash -echo "Text that will appear in chat. mylatstartshere 45 mylongstartshere 45" -``` -And venues: -```bash -echo "Text that will appear in chat. mylatstartshere 45 mylongstartshere 45 mytitlestartshere my home myaddressstartshere Diagon Alley N. 37" -``` -You can combine them: -```bash -echo "Text that will appear in chat? mykeyboardstartshere [ \"Yep, sure\" , \"No, highly unlikely\" ] myfilelocationstartshere /home/user/doge.jpg mylatstartshere 45 mylongstartshere 45" -``` -Please note that you can either send a location or a venue, not both. To send a venue add the mytitlestartshere and the myaddressstartshere keywords. - -New in v0.6: To insert a linebreak in your message you can insert ```mynewlinestartshere``` in your echo command: -```bash -echo "Text that will appear in one message mynewlinestartshere with this text on a new line" -``` - -New in v0.7: In case you must extend a message already containing a location, a file, a keyboard etc., -with additionial text simply add ``` mytextstartshere additional text``` at the end of the string: -```bash -out="Text that will appear mylatstartshere 45 mylongstartshere 45" -[[ "$out" != *'in chat'* ]] && out="$out mytextstartshere in chat." -echo "$out" -``` -Note: Interactive Chats run independent from main bot and continue running until your script exits or you /cancel if from your Bot. - -### Background Jobs - -A background job is similar to an interactive chat, but runs in the background and does only output massages and does not get user input. In contrast to interactive chats it's possible to run multiple background jobs. To create a background job write a script or edit 'examples/notify.sh' script and use the funtion ```background``` to start it: -```bash -background "examples/notify.sh" "jobname" -``` -All output of the script will be sent to the user, to stop a background job use: -```bash -killback "jobname" -``` -You can also suspend and resume the last running background jobs from outside bashbot, e.g. in your startup schripts: -```bash -./bashbot.sh suspendback -./bashbot.sh resumeback -``` - -If you want to kill all background jobs permantly run: -```bash -./bashbot.sh killback - -``` -Note: Background Jobs run independent from main bot and continue running until your script exits or you stop if from your Bot. Backgound Jobs will continue running if your Bot is stopeda and must be terminated, e.g. by ```bashbot.sh killback``` - -### Inline queries -**Inline queries** allow users to send commands to your bot from every chat without going to a private chat. An inline query is started if the user type the bots name, e.g. @myBot. Everything after @myBot is immediatly send to the bot. - -In order to enable **inline mode**, send `/setinline` command to [@BotFather](https://telegram.me/botfather) and provide the placeholder text that the user will see in the input field after typing your bot’s name. - -The following commands allows you to send ansers to *inline queries*. To enable bashbot to process inline queries set ```INLINE="1"``` in 'mycommands.sh'. - -To send messsages or links through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "article" "Title of the result" "Content of the message to be sent" -``` -To send photos in jpeg format and less than 5MB, from a website through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "photo" "A valid URL of the photo" "URL of the thumbnail" -``` -To send standard gifs from a website (less than 1MB) through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "gif" "gif url" -``` -To send mpeg4 gifs from a website (less than 1MB) through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "mpeg4_gif" "mpeg4 gif url" -``` -To send videos from a website through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "video" "valid video url" "Select one mime type: text/html or video/mp4" "URL of the thumbnail" "Title for the result" -``` -To send photos stored in Telegram servers through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "cached_photo" "identifier for the photo" -``` -To send gifs stored in Telegram servers through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "cached_gif" "identifier for the gif" -``` -To send mpeg4 gifs stored in Telegram servers through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "cached_mpeg4_gif" "identifier for the gif" -``` -To send stickers through an *inline query*: -```bash -answer_inline_query "${iQUERY[ID]}" "cached_sticker" "identifier for the sticker" -``` -See also [answer_inline_multi, answer_inline_compose](6_reference.md#answer_inline_multi) and [mycommands.sh](../mycommands.sh) for more information. - -#### [Prev Getting started](2_usage.md) -#### [Next Expert Use](4_expert.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/4_expert.md b/DIST/telegram-bot-bash/doc/4_expert.md deleted file mode 100644 index 46837aa..0000000 --- a/DIST/telegram-bot-bash/doc/4_expert.md +++ /dev/null @@ -1,352 +0,0 @@ -#### [Home](../README.md) -## Expert Use - -### Handling UTF-8 character sets -UTF-8 is a variable length encoding of Unicode. UTF-8 is recommended as the default encoding in JSON, XML and HTML, also Telegram make use of it. - -The first 128 characters are regular ASCII, so it's a superset of and compatible with ASCII environments. The next 1,920 characters need -two bytes for encoding and covers almost all ```Latin``` alphabets, also ```Greek```, ```Cyrillic```, -```Hebrew```, ```Arabic``` and more. See [Wikipedia](https://en.wikipedia.org/wiki/UTF-8) for more details. - -#### Setting up your Environment -In general ```bash``` and ```GNU``` utitities are UTF-8 aware if you to setup your environment -and your scripts accordingly: - -1. Your Terminal and Editor must support UTF-8: - Set Terminal and Editor locale to UTF-8, eg. in ```Settings/Configuration``` select UTF-8 (Unicode) as Charset. - -2. Set ```Shell``` environment to UTF-8 in your ```.profile``` and your scripts. The usual settings are: - -```bash -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' -``` - If you use other languages, eg. german or US english, change the shell settings to: -```bash -export 'LC_ALL=de_DE.UTF-8' -export 'LANG=de_DE.UTF-8' -export 'LANGUAGE=de_DE.UTF-8' -``` -```bash -export 'LC_ALL=en_US.UTF-8' -export 'LANG=de_en_US.UTF-8' -export 'LANGUAGE=den_US.UTF-8' -``` -3. make shure your bot scripts use the correct settings, eg. include the lines above at the beginning of your scripts - -To display all availible locales on your system run ```locale -a | more```. [Gentoo Wiki](https://wiki.gentoo.org/wiki/UTF-8) - -#### Bashbot UTF-8 Support -Bashbot handles all messages transparently, regardless of the charset in use. The only exception is when converting from JSON data to strings. - -Telegram use JSON to send / recieve data. JSON encodes strings as follow: Characters not ASCII *(>127)* are escaped as sequences of ```\uxxxx``` to be regular ASCII. In addition multibyte characters, *e.g. Emoticons or Arabic characters*, are send in double byte UTF-16 notation. -The Emoticons ``` 😁 😘 ❤️ 😊 👍 ``` are encoded as: ``` \uD83D\uDE01 \uD83D\uDE18 \u2764\uFE0F \uD83D\uDE0A \uD83D\uDC4D ``` - -**This "mixed" JSON encoding needs special handling and can not decoded from** ```echo -e``` or ```printf '%s\\n'``` - -Most complete support for decoding of multibyte characters can only be provided if python is installed on your system. -**Without phyton bashbot falls back to an internal, pure bash implementation which may not work for some corner cases**. - - -### Run as other user or system service -Bashbot is desingned to run manually by the user who installed it. Nevertheless it's possible to run it by an other user-ID, as a system service or sceduled from cron. This is onyl recommended for experiend linux users. - -Setup the environment for the user you want to run bashbot and enter desired username, e.g. nobody : -```bash -sudo ./bashbot.sh init -``` - -Edit the file ```bashbot.rc``` and edit the following lines to fit your configuration: -```bash -####################### -# Configuration Section - -# edit the next line to fit the user you want to run bashbot, e.g. nobody: -runas="nobody" - -# uncomment one of the following lines -# runcmd="su $runas -s /bin/bash -c " # runasuser with su -# runcmd="runuser $runas -s /bin/bash -c " # runasuser with runuser - -# edit the values of the following lines to fit your config: -start="/usr/local/telegram-bot-bash/bashbot.sh" # location of your bashbot.sh script -name='' # your bot name as given to botfather, e.g. mysomething_bot - -# END Configuration -####################### -``` -From now on use 'bashbot.rc' to manage your bot: -```bash -sudo ./bashbot.rc start -``` -Type ```ps -ef | grep bashbot``` to verify your Bot is running as the desired user. - -If your Bot is started by 'bashbot.rc', you must use 'bashbot.rc' also to manage your Bot! The following commands are availible: -```bash -sudo ./bashbot.rc start -sudo ./bashbot.rc stop -sudo ./bashbot.rc status -sudo ./bashbot.rc suspendback -sudo ./bashbot.rc resumeback -sudo ./bashbot.rc killback -``` -To change back the environment to your user-ID run ```sudo ./bashbot.sh init``` again and enter your user name. - -To use bashbot as a system servive include a working ```bashbot.rc``` in your init system (systemd, /etc/init.d). - -### Scedule bashbot from Cron -An example crontab is provided in ```examples/bashbot.cron```. - -- If you are running bashbot with your user-ID, copy the examples lines to your crontab and remove username ```nobody```. -- if you run bashbot as an other user or a system service edit ```examples/bashbot.cron``` to fit your needs and replace username```nobody``` with the username you want to run bashbot. copy the modified file to ```/etc/cron.d/bashbot``` - - -### Use bashbot from CLI and scripts -You can use bashbot to send *messages*, *locations*, *venues*, *pictures* etc. from command line and scripts -by sourcing it: - -*usage:* . bashbot.sh source - -Before sourcing 'bahsbot.sh' for interactive and script use, you should export and set BASHBOT_HOME to bashbots installation dir, -e.g. '/usr/local/telegram-bot-bash'. see [Bashbot Environemt](#Bashbot-environment) - -**Note:** *If you don't set BASHBOT_HOME bashbot will use the actual directory as NEW home directory -which means it will create all needed files and ask for bot token and botadmin if you are not in the real bot home!* - -*Examples:* -```bash -# if you are in the bashbot directory -. bashbot.sh source - -# same, but more readable in scripts -source ./bashbot.sh source - -# use bashbot config in BASHBOT_HOME from any directory -export BASHBOT_HOME=/usr/local/telegram-bot-bash -source ${BASHBOT_HOME}/bashbot.sh source - -# use / create new config in current directory -unset BASHBOT_HOME -source /path/to/bashbot.sh source - -``` - -#### Environment variable exported from bashbot -If you have sourced 'bashbot.sh' you have the following bashot internal variables availible: -```bash -COMMANDS # default: ./commands.sh" -MODULEDIR # default: ./modules" -TOKENFILE # default: ./token" -BOTADMIN # default: ./botadmin" -BOTACL # default: ./botacl" -TMPDIR # default: ./data-bot-bash" -COUNTFILE # default: ./count" - -BOTTOKEN # default: content of ${TOKENFILE} -URL # telegram api URL - default: https://api.telegram.org/bot${BOTTOKEN}" -``` - -#### Interacctive use -For testing your setup or sending messages yoursel you can use bashbot functions from bash command line: -```bash -# are we running bash? -echo $SHELL -/bin/bash - -# source bashbot.sh WITHOUT BASHBOT_HOME set -./bashbot.sh source - -# output bashbot internal variables -echo $COMMANDS $MODULEDIR $TOKENFILE $BOTADMIN $BOTACL $TMPDIR $COUNTFILE -./commands.sh ./modules ./token ./botadmin ./botacl ./data-bot-bash ./count - - -# source bashbot.sh WITH BASHBOT_HOME set -export BASHBOT_HOME=/usr/local/telegram-bot-bash -source ./bashbot.sh source - -# output bashbot internal variables -echo $COMMANDS $MODULEDIR $TOKENFILE $BOTADMIN $BOTACL $TMPDIR $COUNTFILE -/usr/local/telegram-bot-bash/commands.sh /usr/local/telegram-bot-bash/modules /usr/local/telegram-bot-bash/token -/usr/local/telegram-bot-bash/botadmin /usr/local/telegram-bot-bash/botacl /usr/local/telegram-bot-bash/data-bot-bash -/usr/local/telegram-bot-bash/count - -``` -After sourcing you can use bashbot functions to send Messages, Locations, Pictures etc. to any Telegram -User or Chat you are in. See [Send Messages](2_usage.md#sending-messages). - -*Examples:* You can test this by sending messages to yourself: -```bash -# fist Hello World -send_normal_message "$(< $BOTADMIN)" "Hello World! This is my first message" - -# now with some markdown and HTML -send_markdown_message "$(< $BOTADMIN)" '*Hello World!* _This is my first markdown message_' -send_html_message "$(< $BOTADMIN)" 'Hello World! This is my first HTML message' -send_keyboard "$(< $BOTADMIN)" 'Do you like it?' '[ "Yep" , "No" ]' -``` -Now something more useful ... -```bash -# sending output from system commands: -send_normal_message "$(< $BOTADMIN)" "$(date)" - -send_normal_message "$(< $BOTADMIN)" "$(uptime)" - -send_normal_message "$(< $BOTADMIN)" '`'$(free)'`' - -# same but markdown style 'code' (monospaced) -send_markdown_message "$(< $BOTADMIN)" "\`$(free)\`" -``` - - -### Bashbot environment -This section describe how you can customize bashbot to your needs by setting environment variables. - - -#### Change file locations -In standard setup bashbot is self containing, this means you can place 'telegram-bot-bash' any location -and run it from there. All files - programm, config, data etc - will reside in 'telegram-bot-bash'. - -If you want to have other locations for config, data etc, define and export the following environment variables. -**Note: all specified directories and files must exist or running 'bashbot.sh' will fail.** - -##### BASHBOT_ETC -Location of the files ```commands.sh```, ```mycommands.sh```, ```token```, ```botadmin```, ```botacl``` ... -```bash - unset BASHBOT_ETC # keep in telegram-bot-bash (default) - export BASHBOT_ETC "" # keep in telegram-bot-bash - - export BASHBOT_ETC "/etc/bashbot" # unix like config location - - export BASHBOT_ETC "/etc/bashbot/bot1" # multibot configuration bot 1 - export BASHBOT_ETC "/etc/bashbot/bot2" # multibot configuration bot 2 -``` - - e.g. /etc/bashbot - -##### BASHBOT_VAR -Location of runtime data ```data-bot-bash```, ```count``` -```bash - unset BASHBOT_VAR # keep in telegram-bot-bash (default) - export BASHBOT_VAR "" # keep in telegram-bot-bash - - export BASHBOT_VAR "/var/spool/bashbot" # unix like config location - - export BASHBOT_VAR "/var/spool/bashbot/bot1" # multibot configuration bot 1 - export BASHBOT_VAR "/var/spool/bashbot/bot2" # multibot configuration bot 2 -``` - -##### BASHBOT_JSONSH -Full path to JSON.sh script, default: './JSON.sh/JSON.sh', must end with '/JSON.sh'. -```bash - unset BASHBOT_JSONSH # telegram-bot-bash/JSON.sh/JSON.sh (default) - export BASHBOT_JSONSH "" # telegram-bot-bash/JSON.sh/JSON.sh - - export BASHBOT_JSONSH "/usr/local/bin/JSON.sh" # installed in /usr/local/bin - -``` - -##### BASHBOT_HOME -Set bashbot home directory, where bashot will look for additional files. -If BASHBOT_ETC, BASHBOT_VAR or BASHBOT_JSONSH are set the have precedence over BASHBOT_HOME. - -This is also usefull if you want to force bashbot to always use full pathnames instead of relative ones. -```bash - unset BASHBOT_HOME # autodetection (default) - export BASHBOT_HOME "" # autodetection - - export BASHBOT_HOME "/usr/local/telegram-bot-bash" # unix like location - export BASHBOT_HOME "/usr/local/bin" # Note: you MUST set ETC, VAR and JSONSH to other locations to make this work! -``` - ----- - -#### Change config values - -##### BASHBOT_URL -Uses given URL instead of offical telegram API URL, useful if you have your own telegram server or for testing. - -```bash - unset BASHBOT_URL # use Telegram URL https://api.telegram.org/bot (default) - - export BASHBOT_URL "" # use use Telegram https://api.telegram.org/bot - - export BASHBOT_URL "https://my.url.com/bot" # use your URL https://my.url.com/bot - -``` - -##### BASHBOT_TOKEN - -##### BASHBOT_WGET -Bashbot uses ```curl``` to communicate with telegram server. if ```curl``` is not availible ```wget``` is used. -If 'BASHBOT_WGET' is set to any value (not undefined or not empty) wget is used even is curl is availible. -```bash - unset BASHBOT_WGET # use curl (default) - export BASHBOT_WGET "" # use curl - - export BASHBOT_WGET "yes" # use wget - export BASHBOT_WGET "no" # use wget! - -``` - -##### BASHBOT_SLEEP -Instead of polling permanently or with a fixed delay, bashbot offers a simple adaptive polling. -If messages are recieved bashbot polls with no dealy. If no messages are availible bashbot add 100ms delay -for every poll until the maximum of BASHBOT_SLEEP ms. -```bash - unset BASHBOT_SLEEP # 5000ms (default) - export BASHBOT_SLEEP "" # 5000ms - - export BASHBOT_SLEEP "1000" # 1s maximum sleep - export BASHBOT_SLEEP "10000" # 10s maximum sleep - export BASHBOT_SLEEP "1" # values < 1000 disables sleep (not recommended) - -``` - -#### Testet configs as of v0.90 release -**Note: Environment variables are not stored, you must setup them before every call to bashbot.sh, e.g. from a script.** - -##### simple Unix like config, for one bot. bashbot is installed in '/usr/local/telegram-bot-bash' -```bash - # Note: all dirs and files must exist! - export BASHBOT_ETC "/etc/bashbot" - export BASHBOT_VAR "/var/spool/bashbot" - - /usr/local/telegram-bot-bash/bashbot.sh start -``` - -##### Unix like config for one bot. bashbot.sh is installed in '/usr/bin' -```bash - # Note: all dirs and files must exist! - export BASHBOT_ETC "/etc/bashbot" - export BASHBOT_VAR "/var/spool/bashbot" - export BASHBOT_JSONSH "/var/spool/bashbot" - - /usr/local/bin/bashbot.sh start -``` - -##### simple multibot config, everything is keept inside 'telegram-bot-bash' dir -```bash - # config for running Bot 1 - # Note: all dirs and files must exist! - export BASHBOT_ETC "./mybot1" - export BASHBOT_VAR "./mybot1" - - /usr/local/telegram-bot-bash/bashbot.sh start -``` - -```bash - # config for running Bot 2 - # Note: all dirs and files must exist! - export BASHBOT_ETC "./mybot2" - export BASHBOT_VAR "./mybot2" - - /usr/local/telegram-bot-bash/bashbot.sh start -``` - -#### [Prev Advanced Use](3_advanced.md) -#### [Next Best Practice](5_practice.md) - -#### $$VERSION$$ v0.90-dev2-19-g5779acc - diff --git a/DIST/telegram-bot-bash/doc/5_practice.md b/DIST/telegram-bot-bash/doc/5_practice.md deleted file mode 100644 index 751cd77..0000000 --- a/DIST/telegram-bot-bash/doc/5_practice.md +++ /dev/null @@ -1,156 +0,0 @@ -#### [Home](../README.md) -## Best Practices - -### New to bot development? - -If you are new to Bot development read [Bots: An introduction for developers](https://core.telegram.org/bots) and consult [Telegram Bot API Documentation](https://core.telegram.org/bots/api/). - -In addition you should know about [BotFather, the one bot to rule them all](https://core.telegram.org/bots#3-how-do-i-create-a-bot). It will help you create new bots and change settings for existing ones. [Commands known by Botfather](https://core.telegram.org/bots#generating-an-authorization-token) - -If you dont't have a github account, it may time to [sepup a free account now](https://github.com/pricing) - -### Add commands to mycommands.sh only -To ease updates never change ```bashbot.sh```, instead your commands and functions must go to ```mycommands.sh``` . Insert your Bot commands in the ```case ... esac``` block of the 'mycommands()' function: -```bash -# file: mycommands.sh -# your additional bahsbot commands - -# uncomment the following lines to overwrite info and help messages - bashbot_info='This is *MY* variant of _bashbot_, the Telegram bot written entirely in bash. -' - - bashbot_help='*Available commands*: -/echo message - _echo the given messsage_ -' - -# NOTE: command can have @botname attached, you must add * in case tests... -mycommands() { - - case "$MESSAGE" in - '/echo'*) # example echo command - send_normal_message "${CHAT[ID]}" "$MESSAGE" - ;; - # ..... - esac -} -``` - -### Overwrite, extend and disable global commands - -You can overwrite a global bashbot command by placing the same commands in ```mycommands.sh``` and add ```return 1``` -ad the end of your command, see '/kickme' below. - -To disable a global bashbot command place create a command simply containing 'return 1', see '/leave' below. - -In case you want to add some processing to the global bashbot command add ```return 0```, then both command will be executed. - -**Learn more about [Bot commands](https://core.telegram.org/bots#commands).** - -```bash -# file: commands.sh - - case "$MESSAGE" in - ########## - # command overwrite examples - 'info'*) # output date in front of regular info - send_normal_message "${CHAT[ID]}" "$(date)" - return 0 - ;; - '/kickme'*) # this will replace the /kickme command - send_markdown_mesage "${CHAT[ID]}" "*This bot will not kick you!*" - return 1 - ;; - '/leave'*) # disable all commands starting with leave - return 1 - ;; - esac -``` - - -### Seperate logic from commands - -If a command need more than 2-3 lines of code, you should use a function to seperate logic from command. Place your functions in ```mycommands.sh``` and call the from your command. Example: -```bash -# file: mycommands.sh -# your additional bahsbot commands - -mycommands() { - - case "$MESSAGE" in - '/process'*) # logic for /process is done in process_message - result="$(process_message "$MESSAGE")" - send_normal_message "${CHAT[ID]}" "$result" - ;; - esac - -} - -# place your functions here - -process_message() { - local ARGS="${1#/* }" # remove command - local TEXT OUTPUT="" - - # process every word in MESSAGE, avoid globbing - set -f - for WORD in $ARGS - do - # process links - if [[ "$WORD" == "https://"* ]]; then - REPORT="$(dosomething_with_link "$WORD")" - # no link, add as text - else - TEXT="$(echo "${TEXT} $WORD")" - continue - fi - # compose result - OUTPUT="* ${REPORT} ${WORD} ${TEXT}" - TEXT="" - done - - # return result, reset globbing in case we had no ARGS - echo "${OUTPUT}${TEXT}" -} - -``` - -### Test your Bot with shellcheck -Shellcheck is a static linter for shell scripts providing excellent tips and hints for shell coding pittfalls. You can [use it online](https://www.shellcheck.net/) or [install it on your system](https://github.com/koalaman/shellcheck#installing). -All bashbot scripts are linted by shellcheck. - -Shellcheck examples: -```bash -$ shellcheck -x mybotcommands.inc.sh - -Line 17: - TEXT="$(echo "${TEXT} $WORD")" - ^-- SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'. - -``` -As you can see my ```mybotcommands.inc.sh``` contains an useless echo command in 'TEXT=' assigment and can be replaced by ```TEXT="${TEXT}${WORD}"``` -```bash -$ shellcheck -x examples/notify -OK -$ shellcheck -x examples/question -OK -$ shellcheck -x commands.sh -OK -$ shellcheck -x bashbot.sh - -In bashbot.sh line 123: - text="$(echo "$text" | sed 's/ mynewlinestartshere /\r\n/g')" # hack for linebreaks in startproc scripts - ^-- SC2001: See if you can use ${variable//search/replace} instead. - - -In bashbot.sh line 490: - CONTACT[USER_ID]="$(sed -n -e '/\["result",'$PROCESS_NUMBER',"message","contact","user_id"\]/ s/.*\][ \t]"\(.*\)"$/\1/p' <"$TMP")" - ^-- SC2034: CONTACT appears unused. Verify it or export it. -``` -The example show two warnings in bashbots scripts. The first is a hint you may use shell substitions instead of sed, this is fixed and much faster as the "echo | sed" solution. -The second warning is about an unused variable, this is true because in our examples CONTACT is not used but assigned in case you want to use it :-) - -#### [Prev Best Practice](5_practice.md) -#### [Next Functions Reference](6_reference.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/6_reference.md b/DIST/telegram-bot-bash/doc/6_reference.md deleted file mode 100644 index e262996..0000000 --- a/DIST/telegram-bot-bash/doc/6_reference.md +++ /dev/null @@ -1,692 +0,0 @@ -#### [Home](../README.md) -## Bashbot function reference - -### Send, forward, delete messages - -##### send_action -```send_action``` shows users what your bot is currently doing. - -*usage:* send_action "${CHAT[ID]}" "action" - -*"action":* ```typing```, ```upload_photo```, ```record_video```, ```upload_video```, ```record_audio```, ```upload_audio```, ```upload_document```, ```find_location```. - -*alias:* _action "action" - -*example:* -```bash -send_action "${CHAT[ID]}" "typing" -send_action "${CHAT[ID]}" "record_audio" -``` - - -##### send_normal_message -```send_normal_message``` sends text only messages to the given chat. - -*usage:* send_normal_message "${CHAT[ID]}" "message" - -*alias:* _normal_message "message" - -*example:* -```bash -send_normal_message "${CHAT[ID]}" "this is a text message" -``` - - -##### send_markdown_message -```send_markdown_message``` sends markdown style messages to the given chat. -Telegram supports a [reduced set of Markdown](https://core.telegram.org/bots/api#markdown-style) only - -*usage:* send_markdown_message "${CHAT[ID]}" "markdown message" - -*alias:* _markdown "message" - -*example:* -```bash -send_markdown_message "${CHAT[ID]}" "this is a markdown message, next word is *bold*" -send_markdown_message "${CHAT[ID]}" "*bold* _italic_ [text](link)" -``` - -##### send_html_message -```send_html_message``` sends HTML style messages to the given chat. -Telegram supports a [reduced set of HTML](https://core.telegram.org/bots/api#html-style) only - -*usage:* send_html_message "${CHAT[ID]}" "html message" - -*alias:* _html_message "message" - -*example:* -```bash -send_normal_message "${CHAT[ID]}" "this is a markdown message, next word is bold" -send_normal_message "${CHAT[ID]}" "bold italic> italic>/em> Text" -``` - -##### forward_message -```forward_mesage``` forwards a messsage to the given chat. - -*usage:* forward_message "chat_to" "chat_from" "${MESSAGE[ID]}" - -*old call:* forward "${CHAT[ID]}" "$FROMCHAT" "${MESSAGE[ID]}" - -*alias:* _forward "$FROMCHAT" "${MESSAGE[ID]}" - -See also [Text formating options](https://core.telegram.org/bots/api#formatting-options) - ----- - -##### delete_message -If your Bot is admin of a Chat he can delete every message, if not he can delete only his messages. - -*usage:* delete_message "${CHAT[ID]}" "${MESSAGE[ID]}" - -*alias:* _del_message "${MESSAGE[ID]}" - -See also [deleteMessage limitations](https://core.telegram.org/bots/api#deletemessage) - ----- - -##### send_message -```send_message``` sends any type of message to the given chat. Type of output is steered by keywords within the message. - -The main use case for send_message is to process the output of interactive chats and background jobs. **For regular Bot commands I recommend using of the dedicated send_xxx_message() functions from above.** - -*usage:* send_message "${CHAT[ID]}" "message" - -*example:* - see [Usage](2_usage.md#send_message) and [Advanced Usage](3_advanced.md#Interactive-Chats) - ----- - -### File, Location, Venue, Keyboard - - -##### send_file -send_file allows you to send different type's of files, e.g. photos, stickers, audio, media, etc. [see more](https://core.telegram.org/bots/api#sending-files) - -Starting with version 0.80 send_file implements the following rules: - -- file names must not contain ".." -- file names must not start with "." -- file names not starting wit "/" are realtive to $TMPDIR, e.g. ./data-bot-bash -- abolute filenames must match $FILE_REGEX - -*usage:* send_file "${CHAT[ID]}" "file" "caption" - -*example:* -```bash -send_file "${CHAT[ID]}" "/home/user/doge.jpg" "Lool" -send_file "${CHAT[ID]}" "https://www.domain,com/something.gif" "Something" -``` - -##### send_location -*usage:* send_location "${CHAT[ID]}" "Latitude" "Longitude" - - -##### send_venue -*usage:* send_venue "${CHAT[ID]}" "Latitude" "Longitude" "Title" "Address" "foursquare id (optional)" - - ----- - -##### send_keyboard -Note: since version 0.6 send_keyboard was changed to use native "JSON Array" notation as used from Telegram. Example Keybord Array definitions: - -- yes no in two rows: - - OLD format: 'yes' 'no' (two strings) - - NEW format: '[ "yes" ] , [ "no" ]' (two arrays with a string) -- new layouts made easy with NEW format: - - Yes No in one row: '[ "yes" , "no" ]' - - Yes No plus Maybe in 2.row: '[ "yes" , "no" ] , [ "maybe" ]' - - numpad style keyboard: '[ "1" , "2" , "3" ] , [ "4" , "5" , "6" ] , [ "7" , "8" , "9" ] , [ "0" ]' - -*usage:* send_keyboard "chat-id" "message" "keyboard" - -*alias:* _keyboard "message" "keyboard" - -*example:* -```bash -send_keyboard "${CHAT[ID]}" "Say yes or no" "[ \\"yes\" , \\"no\" ]"" -send_keyboard "${CHAT[ID]}" "Say yes or no" "[ \\"yes\\" ] , [ \\"no\\" ]" -send_keyboard "${CHAT[ID]}" "Enter digit" "[ \\"1\\" , \\"2\\" , \\"3\\" ] , [ \\"4\\" , \\"5\\" , \\"6\\" ] , [ \\"7\\" , \\"8\\" , \\"9\\" ] , [ \\"0\\" ]" -``` - -##### remove_keyboard -*usage:* remove_keybord "$CHAT[ID]" "message" - -*alias:* _del_keyboard "message" - -*See also: [Keyboard Markup](https://core.telegram.org/bots/api/#replykeyboardmarkup)* - ----- - -##### send_button -*usage:* send_button "chat-id" "message" "text" "URL" - -*alias:* _button "text" "URL" - -*example:* -```bash -send_button "${CHAT[ID]}" "MAKE MONEY FAST!!!" "Visit my Shop" "https://dealz.rrr.de" -``` - -##### send_inline_keyboard -This allows to place multiple inline buttons in a row. The inline buttons must specified as a JSON array in the following format: - -```[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ]``` - -Each button consists of a pair of text and URL values, sourrounded by '{ }', multiple buttons are seperated by '**,**' and everthing is wrapped in '[ ]'. - -*usage:* send_inline_keyboard "chat-id" "message" "[ {"text":"text", "url":"url"} ...]" - -*alias:* _inline_keyboard "[{"text":"text", "url":"url"} ...]" - -*example:* -```bash -send_inline_keyboard "${CHAT[ID]}" "MAKE MONEY FAST!!!" '[{"text":"Visit my Shop", url"":"https://dealz.rrr.de"}]' -send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"button 1", url"":"url 1"}, {"text":"button 2", url"":"url 2"} ]' -send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"b 1", url"":"u 1"}, {"text":"b 2", url"":"u 2"}, {"text":"b 2", url"":"u 2"} ]' -``` - -*See also [Inline keyboard markup](https://core.telegram.org/bots/api/#inlinekeyboardmarkup)* - ----- - -### User Access Control - -##### kick_chat_member -If your Bot is a chat admin he can kick and ban a user. - -*usage:* kick_chat_member "${CHAT[ID]}" "${USER[ID]}" - -*alias:* _kick_user "${USER[ID]}" - -##### unban_chat_member -If your Bot is a chat admine can unban a kicked user. - -*usage:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}" - -*alias:* _unban "${USER[ID]}" - -##### leave_chat -Your Bot will leave the chat. - -*usage:* leave_chat "${CHAT[ID]}" - -*alias:* _leave - -```bash -if _is_admin ; then - send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*" - leave_chat "${CHAT[ID]}" -fi -``` - -'See also [kick Chat Member](https://core.telegram.org/bots/api/#kickchatmember)* - ----- - -##### user_is_botadmin -Return true (0) if user is admin of bot, user id if botadmin is read from file './botadmin'. - -*usage:* user_is_botadmin "${USER[ID]}" - -*alias:* _is_botadmin - -*example:* -```bash - _is_botadmin && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*." -``` - -##### user_is_creator -Return true (0) if user is creator of given chat or chat is a private chat. - -*usage:* user_is_creator "${CHAT[ID]}" "${USER[ID]}" - -*alias:* _is_creator - -##### user_is_admin -Return true (0) if user is admin or creator of given chat. - -*usage:* user_is_admin "${CHAT[ID]}" "${USER[ID]}" - -*alias:* _is_admin - -*example:* -```bash -if _is_admin ; then - send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*" - leave_chat "${CHAT[ID]}" -fi -``` - -*See also [Chat Member](https://core.telegram.org/bots/api/#chatmember)* - -##### user_is_allowed -Bahsbot supports User Access Control, see [Advanced Usage](3_advanced.md) - -*usage:* user_is_allowed "${USER[ID]}" "what" "${CHAT[ID]}" - -*example:* -```bash -if ! user_is_allowed "${USER[ID]}" "start" "${CHAT[ID]}" ; then - send_normal_message "${CHAT[ID]}" "You are not allowed to start Bot." -fi -``` - ----- - -### Inline Queries - answer direct queries to bot -You must include ```source modules/inline.sh``` in 'commands.sh' to have the following functions availible. - -Inline Queries allows users to interact with your bot directly without sending extra commands. -As an answer to an inline query you can send back one or more results to the Telegram client. -The Telegram client will then show the results to the user and let him select one. - -##### answer_inline_query -answer_inline_query is provided for backward compatibility with older versions of bashbot. -It send back only one response to an inline query. - -*usage:* answer_inline_query "$i{QUERY[ID]}" "type" "type arg 1" ... "type arg n" - -*example:* - see [Advanced Usage](3_advanced.md#Inline-queries) - - -##### answer_inline_multi -anser_inline_multi allows you to send back a list of responses. responses must be seperated by ','. - -*usage:* answer_inline_multi "${iQUERY[ID]}" "res, res, ... res" - -*example:* -```bash -# note the starting " and ending " !! -answer_inline_multi "${iQUERY[ID]}" " - $(inline_query_compose "1" "photo" "https://avatars0.githubusercontent.com/u/13046303") , - ... - $(inline_query_compose "n" "photo" "https://avatars1.githubusercontent.com/u/4593242") - " -``` - -#### inline_query_compose -inline_query_compose composes one response element to to send back. - -*usage:* inline_query_compose ID type args .... - -``` - ID = unique ID for this response, 1-64 byte long - type = type of answer, e.g. article, photo, video, location ... - args = mandatory arguments in the order they are described in telegram documentation -``` - -Currently the following types and arguments are implemented (optional arguments in parenthesis) -``` - "article"|"message" title message (markup description) - - "photo" photo_URL (thumb_URL title description caption) - "gif" photo_URL (thumb_URL title caption) - "mpeg4_gif" mpeg_URL (thumb_URL title caption) - "video" video_URL mime_type thumb_URL title (caption) - "audio" audio_URL title (caption) - "voice" voice_URL title (caption) - "document" title document_URL mime_type (caption description) - - "location" latitude longitude title - "venue" latitude longitude title (adress foursquare) - "contact" phone first (last thumb) - - "cached_photo" file (title description caption) - "cached_gif" file (title caption) - "cached_mpeg4_gif" file (title caption) - "cached_sticker" file - "cached_document" title file (description caption) - "cached_video" file title (description caption) - "cached_voice" file title (caption) - "cached_audio" file title (caption) -``` -see [InlineQueryResult for more information](https://core.telegram.org/bots/api#inlinequeryresult) about response types and their arguments. - ----- - - -### Background and Interactive jobs -You must include ```source modules/background.sh``` in 'commands.sh' to have the following functions availible. - -##### start_proc -```startproc``` starts a script, the output of the script is sent to the user or chat, user input will be sent back to the script. see [Advanced Usage](3_advanced.md#Interactive-Chats) - -*usage:* start_proc "${CHAT[ID]}" "script" - -*alias:* startproc "script" - -*example:* -```bash -startproc 'examples/calc.sh' -``` - - -##### check_proc -Return true (0) if an interactive script is running in the chat. - -*usage:* check_prog "${CHAT[ID]}" - -*alias:* checkprog - -*example:* -```bash -if ! check_proc "${CHAT[ID]}" ; then - startproc "examples/calc.sh" -else - send_normal_message "${CHAT[ID]}" "Calc already running ..." -fi -``` - -##### kill_proc -Kill the interactive script running in the chat - -*usage:* kill_proc "${CHAT[ID]}" - -*alias:* killproc - -*example:* -```bash -if check_proc "${CHAT[ID]}" ; then - killproc && send_message "${CHAT[ID]}" "Command canceled." -else - send_message "${CHAT[ID]}" "Command is not running." -fi -``` - ----- - -##### start_back -Starts a script as a background job and attaches a jobname to it. All output from a background job is sent to the associated chat. - -In contrast to interactive chats, background jobs do not recieve user input and can run forever. In addition you can suspend and restart running jobs, e.g. after reboot. - -*usage:* start_back "${CHAT[ID]}" "script" "jobname" - -*alias:* background "script" "jobname" - -*example:* -```bash -background "examples/notify.sh" "notify" -``` - -##### check_back -Return true (0) if an background job is active in the given chat. - -*usage:* check_back "${CHAT[ID]}" "jobname" - -*alias:* checkback "jobname" - -*example:* -```bash -if ! checkback "notify" ; then - send_normal_message "${CHAT[ID]}" "Start notify" - background "examples/notify.sh" "notify" -else - send_normal_message "${CHAT[ID]}" "Process notify already running." -fi -``` - -##### kill_back - -*usage:* kill_back "${CHAT[ID]}" "jobname" - -*alias:* killback "jobname" - -*example:* -```bash -checkback "notify" -if [ "$res" -eq 0 ] ; then - send_normal_message "${CHAT[ID]}" "Kill notify" - killback "notify" -else - send_normal_message "${CHAT[ID]}" "Process notify not run." -fi -``` - ----- - -##### send_interactive -Form version 0.80 on forward_message is used to forward messages to interactive job. It replaces the old 'inproc' commands used for TMUX. -Usually message is automatically forwarded in 'commands.sh', but you can forward messages wihle processing also or send your own messages. - -*usage:* send_interactive "${CHAT[ID]}" "message" - -*replaces:*' incproc - -### Aliases - shortcuts for often used funtions -You must include ```source modules/aliases.sh``` in 'commands.sh' to have the following functions availible. - -##### _is_botadmin - -*usage:* _is_botadmin - -*alias for:* user_is_botadmin "${USER[ID]}" - -##### _is_admin - -*usage:* _is_admin - -*alias for:* user_is_admin "${CHAT[ID]}" "${USER[ID]}" - -##### _is_allowed - -*usage:* _is_allowed "what" - -*alias for:* user_is_allowed "${USER[ID]}" "what" "${CHAT[ID]}" - ----- - -##### _kick_user - -*usage:* _kick_user "${USER[ID]}" - -*alias for:* kick_chat_member "${CHAT[ID]}" "${USER[ID]}" - -##### _unban - -*usage:* _unban "${USER[ID]}" - -*alias for:* unban_chat_member "${CHAT[ID]}" "${USER[ID]}" - -##### _leave - -*usage:* _leave - -*alias for:* leave_chat "${CHAT[ID]}" - ----- - -##### _message - -*usage:* _message "message" - -*alias for:* send_normal_message "${CHAT[ID]}" "message" - -##### _normal_message - -*usage:* _normal_message "message" - -*alias for:* send_normal_message "${CHAT[ID]}" "message" - -##### _html_message - -*usage:* _html_message "message" - -*alias for:* send_html_message "${CHAT[ID]}" "message" - -##### _markdown_message - -*usage:* _markdown_message "message" - -*alias for:* send_markdown_message "${CHAT[ID]}" "message" - ----- - -#### _inline_button -*usage:* _inline_button "${1}" "${2}" - -*alias for:* send_inline_button "${CHAT[ID]}" "" "${1}" "${2}" - -#### _inline_keyboard -*usage:* _inline_keyboard "${1}" - -*alias for:* _inline_keyboard "${CHAT[ID]}" "" "${1}" - -#### _keyboard_numpad -*usage:* _keyboard_numpad - -*alias for:* send_keyboard "${CHAT[ID]}" "" '["1","2","3"],["4","5","6"],["7","8","9"],["-","0","."]' "yes" - -#### _keyboard_yesno -*usage:* _keyboard_yesno - -*alias for:* send_keyboard '["yes","no"]' - -#### _del_keyboard -*usage:* _del_keyboard - -*alias for:* remove_keyboard "${CHAT[ID]}" "" - - - -### Helper functions - -##### download -Download the fiven URL ans returns the final filename in TMPDIR. If the given filename exists,the filename is prefixed with a -random number. filename is not allowed to contain '/' or '..'. - -*usage:* download URL filename - -*example:* -```bash -file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")" -echo "$file" -> ./data-bot-bash/avatar.jpg -file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")" -echo "$file" -> ./data-bot-bash/12345-avatar.jpg -``` - -##### _exists -Returns true if the given function exist, can be used to check if a module is loaded. - -*usage* _exists command - -*example:* -```bash -_exists "curl" && _message "Command curl is not installed!" -``` - -##### _is_function -Returns true if the given function exist, can be used to check if a module is loaded. - -*usage* _is_function function - -*example:* -```bash -_is_function "background" && _message "you can run background jobs!" -``` - - ----- - -### Bashbot internal functions -These functions are for internal use only and must not used in your bot commands. - -##### procname -Returns PrefixBotname_Postfix - -*usage:* procname postfix prefix - -*example:* -```bash -# returns botname, if already set -procname -# returns unique identifier for everthing related to chat -procname "${CHAT[ID]}" -# returns unique identifier for job, regardless of chat -procname "" "back-jobname-" -# returns unique identifier for a job related to a chat -# e.g. fifo, cmd and logfile name -procname "${CHAT[ID]}" "back-jobname-" -``` - -##### proclist -Returns process IDs of current bot processes containing string 'pattern' in name or argument. - -*usage:* proclist pattern - -*example:* -```bash -# list PIDs of all background processes -proclist "back-" -# list PIDs of all processes of a job -proclist "back-jobname-" -# list PIDs of all processes for a chat -proclist "_${CHAT[ID]}" -# list PIDs of all bot processes -proclist -``` -##### killallproc -kill all current bot processes containing string 'pattern' in name or argument - -*usage:* killallproc pattern - -*example:* -```bash -# kill all background processes -killallproc "back-" -# kill all processes for a chat -killallproc "_${CHAT[ID]}" -# kill all bot processes, including YOURSELF! -killallproc -``` -##### get_file -*usage:* url="$(get_file "${CHAT[ID]}" "message")" - ----- - -##### send_text -*usage:* send_text "${CHAT[ID]}" "message" - ----- - -##### JsonDecode -Outputs decoded string to STDOUT - -*usage:* JsonDecode "string" - -##### JsonGetString -Reads JSON fro STDIN and Outputs found String to STDOUT - -*usage:* JsonGetString `"path","to","string"` - -##### JsonGetValue -Reads JSON fro STDIN and Outputs found Value to STDOUT - -*usage:* JsonGetValue `"path","to","value"` - ----- - -##### get_chat_member_status -*usage:* get_chat_member_status "${CHAT[ID]}" "${USER[ID]}" - -this may get an official function ... - ----- - -##### process_client -Every Message sent to your Bot is processd by this function. It parse the send JSON and assign the found Values to bash variables. - -##### process_updates -If new updates are availible, this functions gets the JSON from Telegram and dispatch it. - ----- -##### getBotName -The name of your bot is availible as bash variable "$ME", there is no need to call this function if Bot is running. - -*usage:* ME="$(getBotNiname)" - -#### [Prev Best Practice](5_practice.md) -#### [Next Notes for Developers](7_develop.md) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - diff --git a/DIST/telegram-bot-bash/doc/7_develop.md b/DIST/telegram-bot-bash/doc/7_develop.md deleted file mode 100644 index 4cbd1bc..0000000 --- a/DIST/telegram-bot-bash/doc/7_develop.md +++ /dev/null @@ -1,276 +0,0 @@ -#### [Home](../README.md) - -## Notes for bashbot developers -This section is about help and best practices for new bashbot developers. The main focus on is creating new versions of bashbot, not on develop your individual bot. Nevertheless the rules and tools described here can also help you with your bot development. - -bashbot development is done on github. If you want to provide fixes or new features [fork bashbot on githup](https://help.github.com/en/articles/fork-a-repo) and provide changes as [pull request on github](https://help.github.com/en/articles/creating-a-pull-request). - -### Debugging Bashbot -In normal mode of operation all bashbot output is discarded. -To get these messages (and more) you can start bashbot in the current shell ```./bashbot.sh startbot```. Now you can see all output or erros from bashbot. -In addition you can change the change the level of verbosity by adding a third argument after startbot. -``` - "debug" redirects all output to "DEBUG.log", in addtion every update is logged in "MESSAGE.LOG" and "INLINE.log" - "debugterm" same as debug but output and errors are sent to terminal - - "xdebug" same as debug plus set bash option '-x' to log any executed command - "xdebugterm" same as xdebug but output and errors are sent to terminal -``` - -### Modules and Addons -**Modules** live in ```modules/*.sh``` and are bashbot functions factored out in seperate files, gouped by functionality. Main reason for creating modules was -to keep 'bashbot.sh' small, while extending functionality. In addition not every functionality is needed by a bot, so you can -disable modules by removing them, e.g. rename the respective module files to 'module.sh.off'. - -Modules must use onyl functions provided by 'bahsbot.sh' or the module itself, no depedencies to other modules or addons must exist. -If a module function is called from 'bashbot.sh', bashbot must work if the module is disabled, so it's madatory to use '_is_function' -or '_execute_if_function' if a module function is called. - -**Addons** live in ```addons/*.sh.dist``` and are disabled by default. To activate an addon remove the '.dist' from filename, e.g. ```cp addons/example.sh.dist addons/example.sh```. Addons must register themself to BASHBOT_EVENTS at startup, e.g. to call a function everytime a message is recieved. -Registering to EVENTS is similar on how 'commands.sh' is ececuted, but more flexible and one major difference: -**Addons are executed in the context of the main script**, while 'commands.sh' is executed as a seperate process. - -This is why event functions are time critical and must return as fast as possible. Spawn actions as a seperate process or function with '&', e.g. -send a message as respone from an addon: ```send_message "${CHAT[ID]}" "Message to send ..." &```. - -#### Bashbot Events -Addons can register functions to bashbot events at startup by providing their name and a callback function. -If an event occours each registered function for the event is called. - -Events run in the same context as the main bashbot loop, so variables set here are persistent as long bashbot is running. - -Note: For the same reason event function MUST return imideatly! Time consuming tasks must be run in background or as a subshell, e.g. "long running &" - -Availible events: - -* BASHBOT_EVENT_INLINE an inline query is received -* BASHBOT_EVENT_MESSAGE any type of message is received -* BASHBOT_EVENT_TEXT a message containing text is received -* BASHBOT_EVENT_CMD a command is recieved (fist word starts with /) -* BASHBOT_EVENT_REPLYTO a reply to a message is received -* BASHBOT_EVENT_FORWARD a forwarded message is received -* BASHBOT_EVENT_CONTACT a contact is received -* BASHBOT_EVENT_LOCATION a location or a venue is received -* BASHBOT_EVENT_FILE a file is received - -*usage*: BASHBOT_EVENT_xxx["uniqe-name"]="callback" - -"unique-name" can be every alphanumeric string incl. '-' and '_'. Per convention it should be name of the addon followed by an internal identyfier. - -*Example:* Register a function to echo to any Text send to the bot -```bash -# register callback: -BASHBOT_EVENT_TEXT["example_1"]="example_echo" - -# function called if a text is recieved -example_echo() { - # all availible bashbot functions and variables can be used - send_normal_message "${CHAT[ID]}" "${MESSAGE[0]}" & # note the &! -} -``` -* BAHSBOT_EVENT_TIMER is executed every minute and can be uses in variants: oneshot, every minute, every X minutes. - -Registering to BASHBOT_EVENT_TIMER works a little different, you have to add a timing argument to the index name. - -*usage: * BAHSBOT_EVENT_TIMER["name","time"], where time is: - -* -x execute ONCE in x minutes -* 0 ignored -* 1 execute every minute -* x execute every x minutes - -*Examples:* -```bash -# register callback: -BAHSBOT_EVENT_TIMER["example_every","1"]="example_everymin" - -# function called every minute -example_everymin() { - # timer events has no chat id, so send to yourself - send_normal_message "$(< "${BOTADMIN})" "$(date)" & # note the &! -} - -# register other callback: -BAHSBOT_EVENT_TIMER["example_10min","-10"]="example_in10min" - -BAHSBOT_EVENT_TIMER["example_every5","5"]="example_every5min" - - -``` - ----- - -#### Create a stripped down Version of your Bot -Currently bashbot is more a bot development environment than a bot, containing examples, developer scripts, modules, documentation and more. -You don't need all these files after you're finished with your cool new bot. - -Let's create a stripped down version: - -- delete all modules you do not need from 'modules', e.g. 'modules/inline.sh' if you don't use inline queries -- delete not needed standard commands and messages from 'commands.sh' -- delete not needed commands and functions from 'mycommands.sh' -- run ```dev/make-standalone.sh``` to create a a stripped down version of your bo - -Now have a look at the directory 'standalone', here you find the files 'bashbot.sh' and 'commands.sh' containing everything to run your bot. -[Download make-standalone.sh](https://github.com/topkecleon/telegram-bot-bash/blob/master/dev/make-standalone.sh) from github. - -### Setup your develop environment - -1. install git, install [shellcheck](5_practice.md#Test-your-Bot-with-shellcheck) -2. setup your [environment for UTF-8](4_expert.md#Setting-up-your-Environment) -3. clone your bashbot fork to a new directory ```git clone https://github.com//telegram-bot-bash.git```, replace `````` with your username on github -4. create and change to your develop branch ```git checkout -b ```, replace `````` with the name you want to name it, e.g. 'develop' -5. give your (dev) fork a new version tag: ```git tag vx.xx```(optional) -6. setup github hooks by running ```dev/install-hooks.sh``` (optional) - -#### Test, Add, Push changes -A typical bashbot develop loop looks as follow: - -1. start developing - *change, copy, edit bashbot files ...* -2. after changing a bash sript: ```shellcheck -x scipt.sh``` -3. ```dev/all-tests.sh``` - *in case if errors back to 2.* -4. ```dev/git-add.sh``` - *check for changed files, update version string, run git add* -5. ```git commit -m "COMMIT MESSAGE"; git push``` - - -**If you setup your dev environment with hooks and use the scripts above, versioning, addding and testing is done automatically.** - -#### common commands -We state bashbot is a bash only bot, but this is not true. bashbot is a bash script using bash features PLUS external commands. -Usually bash is used in a unix/linux environment where many (GNU) commands are availible, but if commands are missing, bashbot may not work. - -To avoid this and make bashbot working on as many platforms as possible - from embedded linux to mainframe - I recommed to restrict -ourself to the common commands provided by bash and coreutils/busybox/toybox. -See [Bash Builtins](https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html), -[coreutils](https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands), -[busybox](https://en.wikipedia.org/wiki/BusyBox#Commands) and [toybox](https://landley.net/toybox/help.html) - -Availible commands in bash, coreutils, busybox and toybox. Do you find curl on the list? -```bash - .*, [*, [[*, basename, break, builtin*, bzcat, caller*, cat, cd*, chattr, - chgrp, chmod, chown, clear, command*, continue *, cp, cut, date, declare*, - dc, dd, df, diff, dirname, du, echo*, eval*, exec*, exit *, expr*, find, - fuser, getopt*, grep, hash*, head, hexdump, id, kill, killall, last, length, - less, let*, ln, local*, logname, ls, lsattr, lsmod, man, mapfile*, md5sum, mkdir, - mkfifo, mknod, more, mv, nice, nohup, passwd, patch, printf*, ps, pwd*, read*, - readarray*, readonly* return*, rm, rmdir, sed, seq, sha1sum, shift*, sleep, - source*, sort, split, stat, strings, su, sync, tail, tar, tee, test, - time, times*, timeout, touch, tr, trap*, true, umask*, usleep, uudecode, - uuencode, wc, wget, which, who, whoami, xargs, yes -``` -commands marked with \* are bash builtins, all others are external programms. Calling an external programm is more expensive then using bulitins -or using an internal replacement. Here are some examples of internal replacement for external commands: -```bash -HOST="$(hostname)" -> HOST="$HOSTNAME" - -seq 1 100 -> {0..100} - -data="$(cat file)" -> data="$(<"file")" - -DIR="$(dirname $0) -> DIR=""${0%/*}/"" - -IAM="($basename $0)" -> IAM="${0##*/}* - -VAR="$(( 1 + 2 ))" -> (( var=1+2 )) - -INDEX="$(( ${INDEX} + 1 ))" -> (( INDEX++ )) - -``` -For more examples see [Pure bash bible](https://github.com/dylanaraps/pure-bash-bible) - -#### Prepare a new version -After some development it may time to create a new version for the users. a new version can be in sub version upgrade, e.g. for fixes and smaller additions or -a new release version for new features. To mark a new version use ```git tag NEWVERSION``` and run ```dev/version.sh``` to update all version strings. - -Usually I start with pre versions and when everything looks good I push out a release candidate (rc) and finally the new version. -``` - v0.x-devx -> v0.x-prex -> v0.x-rc -> v0.x ... 0.x+1-dev ... -``` - -If you release a new Version run ```dev/make-distribution.sh``` to create the zip and tar.gz archives in the dist directory and attach them to the github release. Do not forget to delete directory dist afterwards. - -#### Versioning - -Bashbot is tagged with version numbers. If you start a new development cycle you can tag your fork with a version higher than the current version. -E.g. if you fork 'v0.60' the next develop version should tagged as ```git tag "v0.61-dev"``` for fixes or ```git tag "v0.70-dev"``` for new features. - -To get the current version name of your develepment fork run ```git describe --tags```. The output looks like ```v0.70-dev-6-g3fb7796``` where your version tag is followed by the number of commits since you tag your branch and followed by the latest commit hash. see also [comments in version.sh](../dev/version.sh) - -To update the Version Number in files run ```dev/version.sh files```, it will update the line '#### $$VERSION$$ ###' in all files to the current version name. -To update version in all files run 'dev/version.sh' without parameter. - - -#### Shellcheck - -For a shell script running as a service it's important to be paranoid about quoting, globbing and other common problems. So it's a must to run shellchek on all shell scripts before you commit a change. this is automated by a git hook activated in Setup step 6. - -To run shellcheck for a single script run ```shellcheck -x script.sh```, to check all schripts run ```dev/hooks/pre-commit.sh```. - - -### bashbot tests -Starting with version 0.70 bashbot has a test suite. To start testsuite run ```dev/all-tests.sh```. all-tests.sh will return 'SUCCESS' only if all tests pass. - -#### enabling / disabling tests - -All tests are placed in the directory ```test```. To disable a test remove the execute flag from the '*-test.sh' script, to (re)enable a test make the script executable again. - - -#### creating new tests -To create a new test run ```test/ADD-test-new.sh``` and answer the questions, it will create the usually needed files and dirs: - -Each test consists of a script script named after ```p-name-test.sh``` *(where p is test pass 'a-z' and name the name -of your test)* and an optional dir ```p-name-test/``` *(script name minus '.sh')* for additional files. - -Tests with no dependency to other tests will run in pass 'a', tests which need an initialized bahsbot environment must run in pass 'd' or later. -A temporary test environment is created when 'ALL-tests.sh' starts and deleted after all tests are finished. - -The file ```ALL-tests.inc.sh``` must be included from all tests and provide the test environment as shell variables: -```bash -# Test Evironment - TESTME="$(basename "$0")" - DIRME="$(pwd)" - TESTDIR="$1" - LOGFILE="${TESTDIR}/${TESTME}.log" - REFDIR="${TESTME%.sh}" - TESTNAME="${REFDIR//-/ }" - -# common filenames - TOKENFILE="token" - ACLFILE="botacl" - COUNTFILE="count" - ADMINFILE="botadmin" - DATADIR="data-bot-bash" - -# SUCCESS NOSUCCES -> echo "${SUCCESS}" or echo "${NOSUCCESS}" - SUCCESS=" OK" - NOSUCCESS=" FAILED!" - -# default input, reference and output files - INPUTFILE="${DIRME}/${REFDIR}/${REFDIR}.input" - REFFILE="${DIRME}/${REFDIR}/${REFDIR}.result" - OUTPUTFILE="${TESTDIR}/${REFDIR}.out" -``` - -Example test -```bash -#!/usr/bin/env bash -# file: b-example-test.sh - -# include common functions and definitions -# shellcheck source=test/ALL-tests.inc.sh -source "./ALL-tests.inc.sh" - -if [ -f "${TESTDIR}/bashbot.sh" ]; then - echo "${SUCCESS} bashbot.sh exist!" - exit 0 -else - echo "${NOSUCCESS} ${TESTDIR}/bashbot.sh missing!" - exit 1 -fi -``` - -#### [Prev Function Reference](6_reference.md) -#### [Next Expert Use](8_custom.md) - -#### $$VERSION$$ v0.90-dev2-19-g5779acc - diff --git a/DIST/telegram-bot-bash/examples/README.html b/DIST/telegram-bot-bash/examples/README.html deleted file mode 100644 index 59cc392..0000000 --- a/DIST/telegram-bot-bash/examples/README.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - Bashobot Documentation - examples/README.html - - - - -

Home

-

Bashbot examples

-

bashbot multi

-

An example wrapper to run multiple instances of bashbot, use ./bashbot-multi.sh botname command

-

bashbot.cron

-

An example crontab is provided in examples/bashbot.cron, see Expert use

-

Interactive chats

-

Two examples for interactive scripts are provided as calc.sh and question.sh, see Advanced use

-

Background scripts

-

Background jobs are an easy way to provide sceduled messages or alerts if something happens. notify.sh is a simple example on how to send a message every x seonds, e.g. current time.

-

background-scripts contains a more useful example on how to start and stop different scripts plus some example backgound scripts.

-
    mycommands.sh - /run_xxx and /kill-xxx wil start any script named run_xxx.sh
-
-    run_diskusage.sh - shows disk usage every 100 seconds
-    run_filename.sh - shown the name of new files in a named dir
-    run_filecontent.sh  - shown the content of new files in a named dir
-    run_notify.sh - same as notify.sh
-

Note: Output of system commands often contains newlines, each newline results in a telegram message, the function ‘send_telegram’ in mycommands.sh avoids this by converting each newline to ‘mynewlinestartshere’ before output the string.

-

System Status

-

send-system-status contains an example for commands showing status of different subsystems. This example is adapted from https://github.com/RG72/telegram-bot-bash to current bashbot commands, but not fully tested. This will show how easy you can convert existing bots.

-
    mycommands.sh - commands to show system status
-    botacl - controls who can show system status
-
-*Availiable commands*:
-    /se *sensors*
-    /smb *smbstatus*
-    /free *memory status*
-    /md *raid status*
-    /lvm *lvm status*
-    /lvsd *Datailed lvm status*
-    /df *disk space*
-    /ifconfig *ifconfig output*
-    /smart *sda* _smart status for sda drive_
-

External scripts

-

external-use will contain some examples on how to send messages from external scripts to Telegram chats or users.

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/examples/README.md b/DIST/telegram-bot-bash/examples/README.md deleted file mode 100644 index 9b8ebc0..0000000 --- a/DIST/telegram-bot-bash/examples/README.md +++ /dev/null @@ -1,60 +0,0 @@ -#### [Home](../README.md) - -## Bashbot examples - -### bashbot multi -An example wrapper to run multiple instances of bashbot, use ```./bashbot-multi.sh botname command``` - -### bashbot.cron -An example crontab is provided in ```examples/bashbot.cron```, see [Expert use](../doc/4_expert.md#Scedule-bashbot-from-Cron) - - -### Interactive chats -Two examples for interactive scripts are provided as **calc.sh** and **question.sh**, see [Advanced use](../doc/3_advanced.md#Interactive-Chats) - -### Background scripts - -Background jobs are an easy way to provide sceduled messages or alerts if something happens. -**notify.sh** is a simple example on how to send a message every x seonds, e.g. current time. - -**background-scripts** contains a more useful example on how to start and stop different scripts plus some example backgound scripts. - -``` - mycommands.sh - /run_xxx and /kill-xxx wil start any script named run_xxx.sh - - run_diskusage.sh - shows disk usage every 100 seconds - run_filename.sh - shown the name of new files in a named dir - run_filecontent.sh - shown the content of new files in a named dir - run_notify.sh - same as notify.sh -``` -**Note:** Output of system commands often contains newlines, each newline results in a telegram message, the function 'send_telegram' in -mycommands.sh avoids this by converting each newline to ' mynewlinestartshere ' before output the string. - -### System Status - -**send-system-status** contains an example for commands showing status of different subsystems. This example is adapted from - https://github.com/RG72/telegram-bot-bash to current bashbot commands, but not fully tested. This will show how easy you can -convert existing bots. - -``` - mycommands.sh - commands to show system status - botacl - controls who can show system status - -*Availiable commands*: - /se *sensors* - /smb *smbstatus* - /free *memory status* - /md *raid status* - /lvm *lvm status* - /lvsd *Datailed lvm status* - /df *disk space* - /ifconfig *ifconfig output* - /smart *sda* _smart status for sda drive_ -``` -### External scripts - -**external-use** will contain some examples on how to send messages from external scripts to Telegram chats or users. - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - - diff --git a/DIST/telegram-bot-bash/examples/background-scripts/mycommands.sh b/DIST/telegram-bot-bash/examples/background-scripts/mycommands.sh deleted file mode 100644 index c9f62ae..0000000 --- a/DIST/telegram-bot-bash/examples/background-scripts/mycommands.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash -# files: mycommands.sh.dist -# copy to mycommands.sh and add all your commands an functions here ... -export res - -# your additional bahsbot commands ... -mycommands() { - - case "$MESSAGE" in - '/run_'*) - myback="run_${MESSAGE#*_}" - if [ -x "./${myback}.sh" ]; then - checkback "${myback}" - if [ "$res" -gt 0 ] ; then - send_normal_message "${CHAT[ID]}" "Start ${myback}, use /kill${myback} to stop it." - background "./${myback}.sh" "${myback}" - else - send_normal_message "${CHAT[ID]}" "Background job ${myback} already running." - fi - fi - ;; - '/kill_'*) - myback="run_${MESSAGE#*_}" - if [ -x "./${myback}.sh" ]; then - checkback "${myback}" - if [ "$res" -eq 0 ] ; then - killback "${myback}" - send_normal_message "${CHAT[ID]}" "Stopping ${myback}, use /run_${myback} to start again." - else - send_normal_message "${CHAT[ID]}" "No background job ${myback}." - fi - fi - ;; - esac -} - -# place your additional processing functions here ... - -# returns true if function exist -_is_function() -{ - [ "$(LC_ALL=C type -t "$1")" = "function" ] -} - -# inifnite loop for waching a given dir for new files -# $1 dir to wtach for new files -watch_dir_loop() { - local newfile old - [ ! -d "$1" ] && echo "ERROR: no directory $1 found!" >&2 && exit 1 - # wait for new files in WATCHDIR - inotifywait -q -m "$1" -e create --format "%f" \ - | while true - do - # read in newfile - read -r newfile - - #skip if not match or same name as last time - [ "${newfile}" = "${old}" ] && continue - sleep 0.2 - - # process content and output message - echo "$(date): new file: ${newfile}" >>"$0.log" - # note: loop callback must a function in the calling script! - if _is_function loop_callback ; then - loop_callback "$1/$newfile" - else - echo "ERROR: loop_callback not found!" >&2 - exit 1 - fi - done -} # 2>>"$0.log" - - -output_telegram() { - # output to telegram - sed <<< "${1}" -e ':a;N;$!ba;s/\n/ mynewlinestartshere /g' -} # 2>>"$0.log" - -# name and localtion of the tml file - -# $1 string to output -# $2 file to add file to -output_html_file() { - local date - date="$(date)" - output_file "$(sed <<< "
$1
${date}
" ' - s/ my[a-z]\{3,15}\(start\|ends\)here.*
/
/g - s/ *mynewlinestartshere */
/ - s/\n/
/ - ')" -} # >>"$0.log" 2>&1 - -# $1 string to output -# $2 file to add file to -output_file() { - local publish="${2}" - [ ! -w "${publish}" ] && echo "ERROR: file ${publish} is not writeable or does not exist!" && exit - - # output at beginnung of file, add date to message - sed <<< "${1}" ' - s/ *mynewlinestartshere */\n/ - s/ my[a-z]\{3,15}\(start\|ends\)here.*//g - ' >"$publish$$" - cat "$publish" >>"$publish$$" - mv "${publish}$$" "${publish}" -} # >>"$0.log" 2>&1 - diff --git a/DIST/telegram-bot-bash/examples/background-scripts/run_diskusage.sh b/DIST/telegram-bot-bash/examples/background-scripts/run_diskusage.sh deleted file mode 100755 index 7b6cc50..0000000 --- a/DIST/telegram-bot-bash/examples/background-scripts/run_diskusage.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# file: run_diskcusage.sh -# example for an background job display a system value - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# discard STDIN for background jobs! -cat >/dev/null & - -source "./mycommands.sh" - -# check if $1 is a number -re='^[0-9]+$' -if [[ $1 =~ $re ]] ; then - SLEEP="$1" -else - SLEEP=100 # time between time notifications -fi - -NEWLINE=$'\n' - -# output disk usgae every $1 seconds -WAIT=0 -while sleep $WAIT -do - output_telegram "Current Disk usage ${NEWLINE} $(df -h / /tmp /usr /var /home)" - # only for testing, delete echo line for production ... - echo "Current Disk usage ${NEWLINE} $(df -h / /tmp /usr /var /home)" - WAIT="$SLEEP" -done - diff --git a/DIST/telegram-bot-bash/examples/background-scripts/run_filecontent.sh b/DIST/telegram-bot-bash/examples/background-scripts/run_filecontent.sh deleted file mode 100755 index d7afcfd..0000000 --- a/DIST/telegram-bot-bash/examples/background-scripts/run_filecontent.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# file: run_filename -# background job to display content of all new files in WATCHDIR -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# discard STDIN for background jobs! -cat >/dev/null & - -# watch for new files created by a trusted programm -WATCHDIR="/my_trusted/dir_to_watch" -source "./mycommands.sh" - -# test your script and the remove ... -WATCHDIR="/tmp/bottest" - -NEWLINE='mynewlinestartshere' - -# this is called by watch dir loop -# $1 is name of the new file -loop_callback() { - # output content of file, you must trust creator because content is sent as message! - output_telegram "Contents of ${1}: ${NEWLINE} $(cat "${1}")" -} - -watch_dir_loop "$WATCHDIR" diff --git a/DIST/telegram-bot-bash/examples/background-scripts/run_filename.sh b/DIST/telegram-bot-bash/examples/background-scripts/run_filename.sh deleted file mode 100755 index b45dd88..0000000 --- a/DIST/telegram-bot-bash/examples/background-scripts/run_filename.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# file: run_filename -# background job to display all new files in WATCHDIR -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# discard STDIN for background jobs! -cat >/dev/null & - -# watch for new logfiles -WATCHDIR="/var/log" -source "./mycommands.sh" - -# test your script and the remove ... -WATCHDIR="/tmp/bottest" - -# this is called by watch dir loop -# $1 is name of the new file -loop_callback() { - # output one simple line ... - echo "New file ${1} created in ${WATCHDIR}!" -} - -watch_dir_loop "$WATCHDIR" diff --git a/DIST/telegram-bot-bash/examples/background-scripts/run_notify.sh b/DIST/telegram-bot-bash/examples/background-scripts/run_notify.sh deleted file mode 100755 index 6326335..0000000 --- a/DIST/telegram-bot-bash/examples/background-scripts/run_notify.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# file: notify.sh -# example for an background job, run with startback notify.sh - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# discard STDIN for background jobs! -cat >/dev/null & - -# check if $1 is a number -re='^[0-9]+$' -if [[ $1 =~ $re ]] ; then - SLEEP="$1" -else - SLEEP=10 # time between time notifications -fi - -# output current time every $1 seconds -while sleep $SLEEP -do - date "+* It's %k:%M:%S o' clock ..." -done - diff --git a/DIST/telegram-bot-bash/examples/bashbot-multi.sh b/DIST/telegram-bot-bash/examples/bashbot-multi.sh deleted file mode 100755 index d461c28..0000000 --- a/DIST/telegram-bot-bash/examples/bashbot-multi.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# file. multibot.sh -# description: run multiple telegram bots from one installation -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -if [ "${2}" = "" ] || [ "${2}" = "-h" ]; then - echo "Usage: $0 botname command" - exit 1 -fi - -BOT="${1}" -[ "${#BOT}" -lt 5 ] && echo "Botname must have a minumum lenth of 5 characters" && exit 1 - -# where should the bots live? -# true in one dir, false in seperate dirs -if true; then - # example for all in one bashbot dir - BINDIR="/usr/local/telegram-bot-bash" - ETC="${BINDIR}" - VAR="${BINDIR}" - -else - # alternative linux like localtions - BINDIR="/usr/local/bin" - ETC="/etc/bahsbot" - VAR="/var/bahsbot" - export BASHBOT_JSONSH="/usr/local/bin/JSON.sh" - -fi - -# set final ENV -export BASHBOT_ETC="${ETC}/${BOT}" -export BASHBOT_VAR="${VAR}/${BOT}" - -# some checks -[ ! -d "${BINDIR}" ] && echo "Dir ${BINDIR} does not exist" && exit 1 -[ ! -d "${BASHBOT_ETC}" ] && echo "Dir ${BASHBOT_ETC} does not exist" && exit 1 -[ ! -d "${BASHBOT_VAR}" ] && echo "Dir ${BASHBOT_VAR} does not exist" && exit 1 -[ ! -x "${BINDIR}/bashbot.sh" ] && echo "${BINDIR}/bashbot.sh not executeable or does not exist" && exit 1 -[ ! -r "${BASHBOT_ETC}/commands.sh" ] && echo "${BASHBOT_ETC}/commands.sh not readable or does not exist" && exit 1 -[ ! -r "${BASHBOT_ETC}/mycommands.sh" ] && echo "${BASHBOT_ETC}/mycommands.sh not readable or does not exist" && exit 1 - -"${BINDIR}/bashbot.sh" "$2" diff --git a/DIST/telegram-bot-bash/examples/bashbot.cron b/DIST/telegram-bot-bash/examples/bashbot.cron deleted file mode 100644 index b29389f..0000000 --- a/DIST/telegram-bot-bash/examples/bashbot.cron +++ /dev/null @@ -1,36 +0,0 @@ -# -# this is an exmaple crontab file for telegram-bot-bash -# copy it to /etc/cron.d/bashbot -# -# (c) https://github.com/gnadelwartz -# -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 - - -SHELL=/bin/sh -PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin: -# place your mailadress here -MAILTO=root - - -# ┌───────────── minute (0 - 59) -# │ ┌───────────── hour (0 - 23) -# │ │ ┌───────────── day of the month (1 - 31) -# │ │ │ ┌───────────── month (1 - 12) -# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday; -# │ │ │ │ │ 7 is also Sunday on some systems) -# │ │ │ │ │ -# │ │ │ │ │ -# * * * * * USER command to execute -# * * * * * root echo "run every minute!" - -# run as www every day at 0:00 - 0 0 * * * nobody /usr/local/telegram-bot-bash/bashbot.sh start # (re)start bot - 0 0 * * * nobody /usr/local/telegram-bot-bash/bashbot.sh resumeback # (re)start background jobs - -# run as www on 24 of Dec, 12:00 - 0 12 24 12 * nobody /usr/local/telegram-bot-bash/bashbot.sh broadcast "X-Mas shopping is over!" # broadcast a message - diff --git a/DIST/telegram-bot-bash/examples/calc.sh b/DIST/telegram-bot-bash/examples/calc.sh deleted file mode 100755 index 617d137..0000000 --- a/DIST/telegram-bot-bash/examples/calc.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# file: calc.sh -# example for an interactive chat, run with startproc calc.sh - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -echo 'Starting Calculator ...' -echo 'Enter first number.' -read -r A -echo 'Enter second number.' -read -r B -echo 'Select Operation: mykeyboardstartshere [ "Addition" , "Subtraction" , "Multiplication" , "Division" , "Cancel" ]' -read -r opt -echo -n 'Result: ' -case $opt in - 'add'* | 'Add'* ) res="$(( A + B ))" ;; - 'sub'* | 'Sub'* ) res="$(( A - B ))" ;; - 'mul'* | 'Mul'* ) res="$(( A * B ))" ;; - 'div'* | 'Div'* ) res="$(( A / B ))" ;; - 'can'* | 'Can'* ) res="abort!" ;; - * ) echo "unknown operator!";; -esac -echo "$res" -echo "Bye .." diff --git a/DIST/telegram-bot-bash/examples/notify.sh b/DIST/telegram-bot-bash/examples/notify.sh deleted file mode 100755 index e7c973d..0000000 --- a/DIST/telegram-bot-bash/examples/notify.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# file: notify.sh -# example for an background job, run with startback notify.sh - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -# discard STDIN for background jobs! -cat >/dev/null & - -# check if $1 is a number -re='^[0-9]+$' -if [[ $1 =~ $re ]] ; then - SLEEP="$1" -else - SLEEP=10 # time between time notifications -fi - -# output current time every $1 seconds -date "+* It's %k:%M:%S o' clock ..." -while sleep $SLEEP -do - date "+* It's %k:%M:%S o' clock ..." -done - diff --git a/DIST/telegram-bot-bash/examples/question.sh b/DIST/telegram-bot-bash/examples/question.sh deleted file mode 100755 index d232716..0000000 --- a/DIST/telegram-bot-bash/examples/question.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# file: question.sh -# example for an interactive chat, run with startproc question.sh - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) - -#### $$VERSION$$ v0.90-dev2-0-gec85636 - -# adjust your language setting here -# https://github.com/topkecleon/telegram-bot-bash#setting-up-your-environment -export 'LC_ALL=C.UTF-8' -export 'LANG=C.UTF-8' -export 'LANGUAGE=C.UTF-8' - -unset IFS -# set -f # if you are paranoid use set -f to disable globbing - -echo "Why hello there. -Would you like some tea (y/n)?" -read -r answer -[[ $answer =~ ^([yY][eE][sS]|[yY])$ ]] && echo "OK then, here you go: http://www.rivertea.com/blog/wp-content/uploads/2013/12/Green-Tea.jpg" || echo "OK then." -until [ "$SUCCESS" = "y" ] ;do - echo 'Do you like Music? mykeyboardstartshere "Yass!" , "No"' - read -r answer - case $answer in - 'Yass!') echo "Goody! mykeyboardendshere";SUCCESS=y;; - 'No') echo "Well that's weird. mykeyboardendshere";SUCCESS=y;; - '') echo "empty answer!" && exit;; - *) SUCCESS=n;; - esac -done -exit diff --git a/DIST/telegram-bot-bash/examples/send-system-status/botacl b/DIST/telegram-bot-bash/examples/send-system-status/botacl deleted file mode 100644 index 9daa4bd..0000000 --- a/DIST/telegram-bot-bash/examples/send-system-status/botacl +++ /dev/null @@ -1,18 +0,0 @@ -# file: botacl -# a user not listed here, will return false from 'user_is_allowed' -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 -# Format: -# user:ressource:chat - -# allow user 123456789 access to all resources in all chats -123456789:*:* - -# allow user 12131415 to request systemstatus in all chats -12131415:systemstatus:* - -# * are only allowed on the right hand side and not for user! -# the following exaples are NOT valid! -*:*:* -*:start:* -*:*:98979695 diff --git a/DIST/telegram-bot-bash/examples/send-system-status/mycommands.sh b/DIST/telegram-bot-bash/examples/send-system-status/mycommands.sh deleted file mode 100644 index 8fa939e..0000000 --- a/DIST/telegram-bot-bash/examples/send-system-status/mycommands.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -# files: mycommands.sh -# -# this example is rendered after https://github.com/RG72/telegram-bot-bash -# to show how you can customize bashbot by only editing mycommands.sh -# NOTE: this is not tested, simply copied from original source and reworked! -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 -# -# shellcheck disable=SC2154 -# shellcheck disable=SC2034 - - -# uncomment the following lines to overwrite info and help messages -#' - -# returned messages - -bashbot_info='This bot allows you to request status of your system. -To begin using the bot, try with the /help command. -' -bashbot_help='*Availiable commands*: -/se *sensors* -/smb *smbstatus* -/free *memory status* -/md *raid status* -/lvm *lvm status* -/lvsd *Datailed lvm status* -/df *disk space* -/ifconfig *ifconfig output* -/smart *-d sda* _smart status for sda drive_ -' - - -# your additional bahsbot commands -# NOTE: command can have @botname attached, you must add * in case tests... -mycommands() { - [[ "$MESSAGE" = '/'* ]] || return - set +f - # shellcheck disable=SC2206 - local arg=( $MESSAGE ) - set -f - local cmd="${arg[0]}" - local msg="" - - if user_is_botadmin "${USER[ID]}" || user_is_allowed "${USER[ID]}" "systemstatus"; then - case "$cmd" in - '/md'*) msg="$(cat /proc/mdstat)";; - '/smb'*) msg="$(smbstatus)" ;; - '/se'*) msg="$(sensors | sed -r 's/\s|\)+//g' | sed -r 's/\(high=|\(min=/\//' | sed -r 's/\,crit=|\,max=/\//')";; - '/free'*) msg="$(free -h)";; - '/pvs'*) msg="$(pvs)";; - '/ifc'*) msg="$(ifconfig)";; - '/vgs'*) msg="$(vgs)";; - '/lvm'*) msg="$(lvs | sed -r 's/\s+/\n/g')";; - '/lvsd'*) msg="$(lvs -a -o +devices | sed -r 's/\s+/\n/g')";; - '/smart'*) - [ "${arg[0]}" == "" ] && msg="example \`/smart sda\`" && return - drive="$(echo "${arg[0]}" | cut -c 1-3)" - echo "smartctl -a /dev/$drive" - msg="$(smartctl -a "/dev/$drive")" - ;; - '/df') msg="$(df -h | sed -r 's/^/\n/' | sed -r 's/\s+/\n/g')";; - esac - - if [ "$msg" != "" ]; then - send_normal_message "${CHAT[ID]}" "$msg" - fi - else - send_normal_message "${USER[ID]}" "Sorry, you are not allowed to use this bot!" - fi -} - -# place your processing functions here - diff --git a/DIST/telegram-bot-bash/html/0_install.html b/DIST/telegram-bot-bash/html/0_install.html deleted file mode 100644 index 74dc70b..0000000 --- a/DIST/telegram-bot-bash/html/0_install.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - Bashobot Documentation - doc/0_install.html - - - - - -

Home

-

Install bashbot

-
    -
  1. Go to the directory you want to install bashbot, e.g. -
      -
    • your $HOME directory (install and run with your user-ID)
    • -
    • /usr/local if you want to run as service
    • -
  2. -
  3. Download latest release zip from github and extract all files.
  4. -
  5. Change into the directory telegram-bot-bash
  6. -
  7. Acticate the bot example commands ``cp mycommands.sh.dist mycommands.sh```
  8. -
  9. Run ./bashbot.sh init to setup the environment and enter your Bots token given by botfather.
  10. -
-

Edit ’mycommands.sh to your needs. Now your Bot is ready to start …

-

If you are new to Bot development read Bots: An introduction for developers

-

Install from Github

-

As an alternative to download the zip files, you can clone the github repository to get the latest improvements/fixes.

-
    -
  1. Go to the directory you want to install bashbot, e.g. -
      -
    • your $HOME directory (install and run with your user-ID)
    • -
    • /usr/local if you want to run as service
    • -
  2. -
  3. Run git clone https://github.com/topkecleon/telegram-bot-bash.git
  4. -
  5. Change into the directory telegram-bot-bash
  6. -
  7. Run test/ALL-tests.sh and if everthing finish OK …
  8. -
  9. Run sudo ./bashbot.sh init to setup the environment and enter your Bots token given by botfather.
  10. -
-

Update bashbot

-

Note: all files including ‘mycommands.sh’ may overwritten, make a backup!

-
    -
  1. Go to the directory where you had installed bashbot, e.g. -
      -
    • your $HOME directory
    • -
    • /usr/local
    • -
  2. -
  3. Download latest release zip from github
  4. -
  5. Stop all running instances of bashbot
  6. -
  7. Extract all files to your existing bashbot dir
  8. -
  9. Run sudo ./bashbot.sh init to setup your environment after the update
  10. -
-

If you modified ‘commands.sh’ move your changes to ‘mycommands.sh’, this avoids overwrrite of you changes on updates.

-

Now you can restart your bashbot instances.

-

Notes on Updates

-

removal of TMUX

-

From version 0.80 on TMUX is no longer needed and the bachsbot command ‘attach’ is deleted. Old function ‘inproc’ is replaced by ‘send_interactive’. send_interactive does checks if an interactive job is running internaly. Pls check if you make use of inproc and remove it including the old checks, e.g.

-
if tmux ls | grep -v send | grep -q "$copname"; then inproc; fi
-# or
-[ checkprog ] && inproc
-

must be replaced by send_interactive "${CHATD[ID]}" "${MESSAGE}"

-

Do not edit commands.sh

-

From version 0.60 on your commands must be placed in ‘mycommands.sh’. If you update from a version with your commands in ‘commands.sh’ move all your commands and functions to ‘mycommands.sh’.

-

From version 0.80 on ‘commands.sh’ will be overwritten on update!

-

Location of var / tmp / data dirs

-

From version 0.70 on the tmp dir is renamed to ‘data-bot-bash’ to reflect the fact that not only temporary files are stored. an existing ‘tmp-bot-bash’ will be automatically renamed after update.

-

From version 0.50 on the temporary files are no more placed in ‘/tmp’. instead a dedicated tmp dir is used.

-

Changes to send_keyboard in v0.6

-

From Version 0.60 on keybord format for send_keyboard and send_message "mykeyboardstartshere ..." was changed. Keybords are now defined in JSON Array notation e.g. “[ \“yes\” , \“no\” ]”. This has the advantage that you can create any type of keyboard supported by Telegram. The old format is supported for backward compatibility, but may fail for corner cases.

-

Example Keyboards:

-
    -
  • yes no in two rows: -
      -
    • OLD format: ‘yes’ ‘no’ (two strings)
    • -
    • NEW format: ‘[ “yes” ] , [ “no” ]’ (two arrays with a string)
    • -
  • -
  • new layouts made easy with NEW format: -
      -
    • Yes No in one row: ‘[ “yes” , “no” ]’
    • -
    • Yes No plus Maybe in 2.row: ‘[ “yes” , “no” ] , [ “maybe” ]’
    • -
    • numpad style keyboard: ‘[ “1” , “2” , “3” ] , [ “4” , “5” , “6” ] , [ “7” , “8” , “9” ] , [ “0” ]’
    • -
  • -
-

Next Create Bot

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/1_firstbot.html b/DIST/telegram-bot-bash/html/1_firstbot.html deleted file mode 100644 index e7fc5d0..0000000 --- a/DIST/telegram-bot-bash/html/1_firstbot.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - Bashobot Documentation - doc/1_firstbot.html - - - - -

Home

-

Create a Telegram Bot with botfather

-

BotFather is the one bot to rule them all. It will help you create new bots and change settings for existing ones. Commands known by Botfather

-

Creating a new Bot

-
    -
  1. Message @botfather https://telegram.me/botfather with the following text: /newbot If you don’t know how to message by username, click the search field on your Telegram app and type @botfather, you should be able to initiate a conversation. Be careful not to send it to the wrong contact, because some users has similar usernames to botfather.
  2. -
-
-botfather initial conversation -

botfather initial conversation

-
-
    -
  1. @botfather replies with Alright, a new bot. How are we going to call it? Please choose a name for your bot.

  2. -
  3. Type whatever name you want for your bot.

  4. -
  5. @botfather replies with Good. Now let's choose a username for your bot. It must end in bot. Like this, for example: TetrisBot or tetris_bot.

  6. -
  7. Type whatever username you want for your bot, minimum 5 characters, and must end with bot. For example: telesample_bot

  8. -
  9. @botfather replies with:

    -

    Done! Congratulations on your new bot. You will find it at telegram.me/telesample_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands.

    -

    Use this token to access the HTTP API: 123456789:AAG90e14-0f8-40183D-18491dDE

    -

    For a description of the Bot API, see this page: https://core.telegram.org/bots/api

  10. -
  11. Note down the ‘token’ mentioned above.

  12. -
  13. Type /setprivacy to @botfather.

  14. -
-
-botfather later conversation -

botfather later conversation

-
-
    -
  1. @botfather replies with Choose a bot to change group messages settings.

  2. -
  3. Type @telesample_bot (change to the username you set at step 5 above, but start it with @)

  4. -
  5. @botfather replies with

    -

    ‘Enable’ - your bot will only receive messages that either start with the ‘/’ symbol or mention the bot by username. ‘Disable’ - your bot will receive all messages that people send to groups. Current status is: ENABLED

  6. -
  7. Type Disable to let your bot receive all messages sent to a group. This step is up to you actually.

  8. -
  9. @botfather replies with Success! The new status is: DISABLED. /help

  10. -
-

Prev Installation

-

Next Getting started

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/2_usage.html b/DIST/telegram-bot-bash/html/2_usage.html deleted file mode 100644 index 83499c3..0000000 --- a/DIST/telegram-bot-bash/html/2_usage.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - Bashobot Documentation - doc/2_usage.html - - - - - -

Home

-

Gettting Started

-

The Bots standard commands are in the commands dispatcher commands.sh, Do not edit this file! Add your commands and functions to mycommands.sh. In ‘mycommands.sh.dist’ you find examples how to add own commands and overwrite existing ones. See Best practices for more information.

-

Once you’re done with editing start the Bot with ./bashbot.sh start. To stop the Bot run ./bashbot.sh kill

-

If something doesn’t work as expected, debug with ./bashbot.sh startbot DEBUG, where DEBUG can be ‘debug’, ‘xdebug’ or ‘xdebugx’. See Bashbot Development for more information.

-

To use the functions provided in this script in other scripts simply source bashbot: source bashbot.sh source. see Expert Use

-

Have FUN!

-
-

Files

-
.
-├── bashbot.sh      # main bashbot script - do not edit
-├── commands.sh     # command dispatcher - do not edit
-├── mycommands.sh   # place your functions and commands here!
-├── JSON.sh     # bashbots JSON parser, see https://github.com/dominictarr/JSON.sh
-│
-├── modules     # optional functions, sourced by commands.sh
-│   ├── aliases.sh      # to disable modules rename them xxx.sh.off
-│   ├── answerInline.sh
-│   ├── jsonDB.sh
-│   ├── background.sh
-│   ├── chatMember.sh
-│   └── sendMessage.sh      # main send message functions, do not disable
-│
-├── addons      # optional addons, disbaled by default
-│   ├── example.sh      # to enable addons change their XXX_ENABLE to true
-│   └── xxxxxage.sh
-│
-├── bashbot.rc          # start/stop script if you run basbot as service
-│
-├── examples            # example scripts and configs for bashbot
-│   └── bashbot.cron        # example crontab
-│
-├── doc         # Documentation and License
-├── html
-├── LICENSE
-├── README.html
-├── README.md
-└── README.txt
-
-
-

Managing your Bot

-

Note: running bashbot as root is highly danger and not recommended. See Expert use.

-

Start / Stop

-

Start or Stop your Bot use the following commands:

-
./bashbot.sh start
-
./bashbot.sh kill
-

User count

-

To count the total number of users that ever used the bot run the following command:

-
./bashbot.sh count
-

Sending broadcasts to all users

-

To send a broadcast to all of users that ever used the bot run the following command:

-
./bashbot.sh broadcast "Hey! I just wanted to let you know that the bot's been updated!"
-
-

Recieve data

-

Evertime a Message is recieved, you can read incoming data using the following variables:

-

Regular Messages

-
    -
  • ${MESSAGE}: Current message
  • -
  • ${MESSAGE[ID]}: ID of current message
  • -
  • $CAPTION: Captions
  • -
  • $REPLYTO: Original message wich was replied to
  • -
  • $USER: This array contains the First name, last name, username and user id of the sender of the current message. -
      -
    • ${USER[ID]}: User id
    • -
    • ${USER[FIRST_NAME]}: User’s first name
    • -
    • ${USER[LAST_NAME]}: User’s last name
    • -
    • ${USER[USERNAME]}: Username
    • -
  • -
  • $CHAT: This array contains the First name, last name, username, title and user id of the chat of the current message. -
      -
    • ${CHAT[ID]}: Chat id
    • -
    • ${CHAT[FIRST_NAME]}: Chat’s first name
    • -
    • ${CHAT[LAST_NAME]}: Chat’s last name
    • -
    • ${CHAT[USERNAME]}: Username
    • -
    • ${CHAT[TITLE]}: Title
    • -
    • ${CHAT[TYPE]}: Type
    • -
    • ${CHAT[ALL_MEMBERS_ARE_ADMINISTRATORS]}: All members are administrators (true if true)
    • -
  • -
  • $REPLYTO: This array contains the First name, last name, username and user id of the ORIGINAL sender of the message REPLIED to. -
      -
    • ${REPLYTO[ID]}: ID of message wich was replied to
    • -
    • ${REPLYTO[UID]}: Original user’s id
    • -
    • ${REPLYTO[FIRST_NAME]}: Original user’s first name
    • -
    • ${REPLYTO[LAST_NAME]}: Original user’s’ last name
    • -
    • ${REPLYTO[USERNAME]}: Original user’s username
    • -
  • -
  • $FORWARD: This array contains the First name, last name, username and user id of the ORIGINAL sender of the FORWARDED message. -
      -
    • ${FORWARD[ID]}: Same as MESSAGE[ID] if message is forwarded
    • -
    • ${FORWARD[UID]}: Original user’s id
    • -
    • ${FORWARD[FIRST_NAME]}: Original user’s first name
    • -
    • ${FORWARD[LAST_NAME]}: Original user’s’ last name
    • -
    • ${FORWARD[USERNAME]}: Original user’s username
    • -
  • -
  • $URLS: This array contains documents, audio files, voice recordings and stickers as URL. -
      -
    • ${URLS[AUDIO]}: Audio files
    • -
    • ${URLS[VIDEO]}: Videos
    • -
    • ${URLS[PHOTO]}: Photos (maximum quality)
    • -
    • ${URLS[VOICE]}: Voice recordings
    • -
    • ${URLS[STICKER]}: Stickers
    • -
    • ${URLS[DOCUMENT]}: Any other file
    • -
  • -
  • $CONTACT: This array contains info about contacts sent in a chat. -
      -
    • ${CONTACT[ID]}: User id
    • -
    • ${CONTACT[NUMBER]}: Phone number
    • -
    • ${CONTACT[FIRST_NAME]}: First name
    • -
    • ${CONTACT[LAST_NAME]}: Last name
    • -
    • ${CONTACT[VCARD]}: User’s complete Vcard
    • -
  • -
  • $LOCATION: This array contains info about locations sent in a chat. -
      -
    • ${LOCATION[LONGITUDE]}: Longitude
    • -
    • ${LOCATION[LATITUDE]}: Latitude
    • -
  • -
  • $VENUE: This array contains info about venue (a place) sent in a chat. -
      -
    • ${VENUE[TITLE]}: Name of the place
    • -
    • ${VENUE[ADDRESS]}: Address of the place
    • -
    • ${VENUE[LONGITUDE]}: Longitude
    • -
    • ${VENUE[LATITUDE]}: Latitude
    • -
    • ${VENUE[FOURSQUARE]}: Fouresquare ID
    • -
  • -
-

Inline queries

-

Evertime a Message is recieved, you can read incoming data using the following variables:

-
    -
  • ${iQUERY}: Current inline query
  • -
  • $iQUERY: This array contains the ID, First name, last name, username and user id of the sender of the current inline query. -
      -
    • ${iQUERY[ID]}: Inline query ID
    • -
    • ${iQUERY[USER_ID]}: User’s id
    • -
    • ${iQUERY[FIRST_NAME]}: User’s first name
    • -
    • ${iQUERY[LAST_NAME]}: User’s last name
    • -
  • -
-

Usage of bashbot functions

-

sending messages

-

To send messages use the send_xxx_message functions.

-

To send regular text without any markdown use:

-
send_text_message "${CHAT[ID]}" "lol"
-

To send text with markdown:

-
send_markdown_message "${CHAT[ID]}" "lol *bold*"
-

To send text with html:

-
send_html_message "${CHAT[ID]}" "lol <b>bold</b>"
-

To forward messages use the forward function:

-
forward "${CHAT[ID]}" "from_chat_id" "message_id"
-

If your Bot is Admin in a Chat you can delete every message, if not you can delete only your messages. To delete a message with a known ${MESSAGE[ID]} you can simple use: ```bash delete_message "${CHAT[ID]}" “${MESSAGE[ID]}” ```

-

send_message

-

In addition there is a universal send_massage function which can output any type of message. This function is used to process output from external scrips like interactive chats or background jobs.
-For safety and performance reasons I recommend to use send_xxxx_message functions above for sending messages

-
send_message "${CHAT[ID]}" "lol"
-

To send html or markdown put the following strings before the text, depending on the parsing mode you want to enable:

-
send_message "${CHAT[ID]}" "markdown_parse_mode lol *bold*"
-
send_message "${CHAT[ID]}" "html_parse_mode lol <b>bold</b>"
-

This function also allows a third parameter that disables additional function parsing (for safety use this when reprinting user input):

-
send_message "${CHAT[ID]}" "lol" "safe"
-

See also Interactive chats

-

Send files, locations, keyboards.

-

To send images, videos, voice files, photos etc. use the send_photo function (remember to change the safety Regex @ line 14 of command.sh to allow sending files only from certain directories):

-
send_file "${CHAT[ID]}" "/home/user/doge.jpg" "Lool"
-

To send custom keyboards use the send_keyboard function:

-
send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" '[ "Yep" , "No" ]' # note the simgle quotes!
-send_keyboard "${CHAT[ID]}" "Text that will appear in chat?" "[ \\"Yep\\" , \\"No\\" ]" # within double quotes you must excape the inside double quots
-

To send locations use the send_location function:

-
send_location "${CHAT[ID]}" "Latitude" "Longitude"
-

To send venues use the send_venue function:

-
send_venue "${CHAT[ID]}" "Latitude" "Longitude" "Title" "Address" "optional foursquare id"
-

To send a chat action use the send_action function. Allowed values: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for locations.

-
send_action "${CHAT[ID]}" "action"
-

See also Bashbot function reference

-

Prev Create Bot

-

Next Advanced Usage

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/3_advanced.html b/DIST/telegram-bot-bash/html/3_advanced.html deleted file mode 100644 index 2d0f25b..0000000 --- a/DIST/telegram-bot-bash/html/3_advanced.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - Bashobot Documentation - doc/3_advanced.html - - - - - -

Home

-

Advanced Features

-

Access control

-

Bashbot offers functions to check what Telegram capabilities like ‘chat admin’ or ‘chat creator’ the given user has:

-
# return true if user is admin/owner of the bot
-# -> botadmin is stored in file './botadmin'
-user_is_botadmin "user"  
-
-# return true if user is creator or admin of a chat
-user_is_admin "chat" "user"
-
-# return true if user is creator of a chat or it's a one to one chat
-user_is_creator "chat" "user"
-
-# examples:
-user_is_botadmin "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*."
-
-user_is_admin "${CHAT[ID]}" "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *CHATADMIN*."
-

In addition you can check individual capabilities of users as you must define in the file ./botacl:

-
# file: botacl
-# a user not listed here, will return false from 'user_is_allowed'
-#
-# Format:
-# user:ressource:chat
-
-# allow user 123456789 access to all resources in all chats
-123456789:*:*
-
-# allow user 12131415 to start bot in all chats
-12131415:start:*
-
-# allow user 987654321 only to start bot in chat 98979695
-987654321:start:98979695
-
-# * are only allowed on the right hand side and not for user!
-# the following exaples are NOT valid!
-*:*:*
-*:start:*
-*:*:98979695
-

You must use the function user_is_allowed to check if a user has the capability to do something. Example: Check if user has capability to start bot.

-
    case "$MESSAGE" in
-        ################################################
-        # GLOBAL commands start here, only edit messages
-        '/start'*)
-            user_is_botadmin "${USER[ID]}" && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*."
-            if user_is_allowed "${USER[ID]}" "start" "${CHAT[ID]}" ; then
-                bot_help "${CHAT[ID]}"
-            else
-                send_normal_message "${CHAT[ID]}" "You are not allowed to start Bot."
-            ;;
-    esac
-

See also Bashbot User Access Control functions

-

Interactive Chats

-

To create interactive chats, write (or edit the ‘exmaples/question.sh’ script) a bash (or C or python) script, make it executable and then use the ‘startproc’ function to start the script. The output of the script will be sent to the user and user input will be sent to the script. To stop the script use the function ‘killprog’

-

The output of the script will be processed by ‘send_messages’ to enable you to not only send text, but also keyboards, files, locations and more. Each newline in the output will start an new message to the user, to have line breaks in your message you can use ‘mynewlinestartshere’.

-

To open up a keyboard in an interactive script, print out the keyboard layout in the following way:

-
echo "Text that will appear in chat? mykeyboardstartshere [ \"Yep, sure\" , \"No, highly unlikely\" ]"
-

Same goes for files:

-
echo "Text that will appear in chat? myfilelocationstartshere /home/user/doge.jpg"
-

And buttons:

-
echo "Text that will appear in chat. mybtextstartshere Klick me myburlstartshere https://dealz.rrr.de"
-

And locations:

-
echo "Text that will appear in chat. mylatstartshere 45 mylongstartshere 45"
-

And venues:

-
echo "Text that will appear in chat. mylatstartshere 45 mylongstartshere 45 mytitlestartshere my home myaddressstartshere Diagon Alley N. 37"
-

You can combine them:

-
echo "Text that will appear in chat? mykeyboardstartshere [ \"Yep, sure\" , \"No, highly unlikely\" ] myfilelocationstartshere /home/user/doge.jpg mylatstartshere 45 mylongstartshere 45"
-

Please note that you can either send a location or a venue, not both. To send a venue add the mytitlestartshere and the myaddressstartshere keywords.

-

New in v0.6: To insert a linebreak in your message you can insert mynewlinestartshere in your echo command:

-
echo "Text that will appear in one message  mynewlinestartshere  with this text on a new line"
-

New in v0.7: In case you must extend a message already containing a location, a file, a keyboard etc., with additionial text simply add mytextstartshere additional text at the end of the string:

-
out="Text that will appear mylatstartshere 45 mylongstartshere 45"
-[[ "$out" != *'in chat'* ]] &&  out="$out mytextstartshere in chat."
-echo "$out"
-

Note: Interactive Chats run independent from main bot and continue running until your script exits or you /cancel if from your Bot.

-

Background Jobs

-

A background job is similar to an interactive chat, but runs in the background and does only output massages and does not get user input. In contrast to interactive chats it’s possible to run multiple background jobs. To create a background job write a script or edit ‘examples/notify.sh’ script and use the funtion background to start it:

-
background "examples/notify.sh" "jobname"
-

All output of the script will be sent to the user, to stop a background job use:

-
killback "jobname"
-

You can also suspend and resume the last running background jobs from outside bashbot, e.g. in your startup schripts:

-
./bashbot.sh suspendback
-./bashbot.sh resumeback
-

If you want to kill all background jobs permantly run:

-
./bashbot.sh killback
-

Note: Background Jobs run independent from main bot and continue running until your script exits or you stop if from your Bot. Backgound Jobs will continue running if your Bot is stopeda and must be terminated, e.g. by bashbot.sh killback

-

Inline queries

-

Inline queries allow users to send commands to your bot from every chat without going to a private chat. An inline query is started if the user type the bots name, e.g. @myBot. Everything after @myBot is immediatly send to the bot.

-

In order to enable inline mode, send /setinline command to [@BotFather](https://telegram.me/botfather) and provide the placeholder text that the user will see in the input field after typing your bot’s name.

-

The following commands allows you to send ansers to inline queries. To enable bashbot to process inline queries set INLINE="1" in ‘mycommands.sh’.

-

To send messsages or links through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "article" "Title of the result" "Content of the message to be sent"
-

To send photos in jpeg format and less than 5MB, from a website through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "photo" "A valid URL of the photo" "URL of the thumbnail"
-

To send standard gifs from a website (less than 1MB) through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "gif" "gif url"
-

To send mpeg4 gifs from a website (less than 1MB) through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "mpeg4_gif" "mpeg4 gif url"
-

To send videos from a website through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "video" "valid video url" "Select one mime type: text/html or video/mp4" "URL of the thumbnail" "Title for the result"
-

To send photos stored in Telegram servers through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "cached_photo" "identifier for the photo"
-

To send gifs stored in Telegram servers through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "cached_gif" "identifier for the gif"
-

To send mpeg4 gifs stored in Telegram servers through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "cached_mpeg4_gif" "identifier for the gif"
-

To send stickers through an inline query:

-
answer_inline_query "${iQUERY[ID]}" "cached_sticker" "identifier for the sticker"
-

See also mycommands.sh for more information.

-

Prev Getting started

-

Next Expert Use

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/4_expert.html b/DIST/telegram-bot-bash/html/4_expert.html deleted file mode 100644 index 888c28b..0000000 --- a/DIST/telegram-bot-bash/html/4_expert.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - Bashobot Documentation - doc/4_expert.html - - - - - -

Home

-

Expert Use

-

Handling UTF-8 character sets

-

UTF-8 is a variable length encoding of Unicode. UTF-8 is recommended as the default encoding in JSON, XML and HTML, also Telegram make use of it.

-

The first 128 characters are regular ASCII, so it’s a superset of and compatible with ASCII environments. The next 1,920 characters need two bytes for encoding and covers almost all Latin alphabets, also Greek, Cyrillic, Hebrew, Arabic and more. See Wikipedia for more details.

-

Setting up your Environment

-

In general bash and GNU utitities are UTF-8 aware if you to setup your environment and your scripts accordingly:

-
    -
  1. Your Terminal and Editor must support UTF-8: Set Terminal and Editor locale to UTF-8, eg. in Settings/Configuration select UTF-8 (Unicode) as Charset.

  2. -
  3. Set Shell environment to UTF-8 in your .profile and your scripts. The usual settings are:

  4. -
-
export 'LC_ALL=C.UTF-8'
-export 'LANG=C.UTF-8'
-export 'LANGUAGE=C.UTF-8'
-

If you use other languages, eg. german or US english, change the shell settings to:

-
export 'LC_ALL=de_DE.UTF-8'
-export 'LANG=de_DE.UTF-8'
-export 'LANGUAGE=de_DE.UTF-8'
-
export 'LC_ALL=en_US.UTF-8'
-export 'LANG=de_en_US.UTF-8'
-export 'LANGUAGE=den_US.UTF-8'
-
    -
  1. make shure your bot scripts use the correct settings, eg. include the lines above at the beginning of your scripts
  2. -
-

To display all availible locales on your system run locale -a | more. Gentoo Wiki

-

Bashbot UTF-8 Support

-

Bashbot handles all messages transparently, regardless of the charset in use. The only exception is when converting from JSON data to strings.

-

Telegram use JSON to send / recieve data. JSON encodes strings as follow: Characters not ASCII (>127) are escaped as sequences of \uxxxx to be regular ASCII. In addition multibyte characters, e.g. Emoticons or Arabic characters, are send in double byte UTF-16 notation. The Emoticons 😁 😘 ❤️ 😊 👍 are encoded as: \uD83D\uDE01 \uD83D\uDE18 \u2764\uFE0F \uD83D\uDE0A \uD83D\uDC4D

-

This “mixed” JSON encoding needs special handling and can not decoded from echo -e or printf '%s\\n'

-

Most complete support for decoding of multibyte characters can only be provided if python is installed on your system. Without phyton bashbot falls back to an internal, pure bash implementation which may not work for some corner cases.

-

Run as other user or system service

-

Bashbot is desingned to run manually by the user who installed it. Nevertheless it’s possible to run it by an other user-ID, as a system service or sceduled from cron. This is onyl recommended for experiend linux users.

-

Setup the environment for the user you want to run bashbot and enter desired username, e.g. nobody :

-
sudo ./bashbot.sh init
-

Edit the file bashbot.rc and edit the following lines to fit your configuration:

-
#######################
-# Configuration Section
-
-# edit the next line to fit the user you want to run bashbot, e.g. nobody:
-runas="nobody" 
-
-# uncomment one of the following lines 
-# runcmd="su $runas -s /bin/bash -c "      # runasuser with su
-# runcmd="runuser $runas -s /bin/bash -c " # runasuser with runuser
-
-# edit the values of the following lines to fit your config:
-start="/usr/local/telegram-bot-bash/bashbot.sh" # location of your bashbot.sh script
-name=''   # your bot name as given to botfather, e.g. mysomething_bot
-
-# END Configuration
-#######################
-

From now on use ‘bashbot.rc’ to manage your bot:

-
sudo ./bashbot.rc start
-

Type ps -ef | grep bashbot to verify your Bot is running as the desired user.

-

If your Bot is started by ‘bashbot.rc’, you must use ‘bashbot.rc’ also to manage your Bot! The following commands are availible:

-
sudo ./bashbot.rc start
-sudo ./bashbot.rc stop
-sudo ./bashbot.rc status
-sudo ./bashbot.rc suspendback
-sudo ./bashbot.rc resumeback
-sudo ./bashbot.rc killback
-

To change back the environment to your user-ID run sudo ./bashbot.sh init again and enter your user name.

-

To use bashbot as a system servive include a working bashbot.rc in your init system (systemd, /etc/init.d).

-

Scedule bashbot from Cron

-

An example crontab is provided in examples/bashbot.cron.

-
    -
  • If you are running bashbot with your user-ID, copy the examples lines to your crontab and remove username nobody.
  • -
  • if you run bashbot as an other user or a system service edit examples/bashbot.cron to fit your needs and replace usernamenobody with the username you want to run bashbot. copy the modified file to /etc/cron.d/bashbot
  • -
-

Use bashbot from CLI and scripts

-

You can use bashbot to send messages, locations, venues, pictures etc. from command line and scripts by sourcing it:

-

usage: . bashbot.sh source

-

Before sourcing ‘bahsbot.sh’ for interactive and script use, you should export and set BASHBOT_HOME to bashbots installation dir, e.g. ‘/usr/local/telegram-bot-bash’. see Bashbot Environemt

-

Note: If you don’t set BASHBOT_HOME bashbot will use the actual directory as NEW home directory which means it will create all needed files and ask for bot token and botadmin if you are not in the real bot home!

-

Examples:

-
# if you are in the bashbot directory
-.  bashbot.sh source
-
-# same, but more readable in scripts
-source ./bashbot.sh source
-
-# use bashbot config in BASHBOT_HOME from any directory
-export BASHBOT_HOME=/usr/local/telegram-bot-bash
-source ${BASHBOT_HOME}/bashbot.sh source
-
-# use / create new config in current directory
-unset  BASHBOT_HOME
-source /path/to/bashbot.sh source
-

Environment variable exported from bashbot

-

If you have sourced ‘bashbot.sh’ you have the following bashot internal variables availible:

-
COMMANDS    # default: ./commands.sh"
-MODULEDIR   # default: ./modules"
-TOKENFILE   # default: ./token"
-BOTADMIN    # default: ./botadmin"
-BOTACL      # default: ./botacl"
-TMPDIR      # default: ./data-bot-bash"
-COUNTFILE   # default: ./count"
-
-BOTTOKEN    # default: content of ${TOKENFILE}
-URL     # telegram api URL - default: https://api.telegram.org/bot${BOTTOKEN}"
-

Interacctive use

-

For testing your setup or sending messages yoursel you can use bashbot functions from bash command line:

-
# are we running bash?
-echo $SHELL
-/bin/bash
-
-# source bashbot.sh WITHOUT BASHBOT_HOME set
-./bashbot.sh source
-
-# output bashbot internal variables
-echo $COMMANDS $MODULEDIR $TOKENFILE $BOTADMIN $BOTACL $TMPDIR $COUNTFILE
-./commands.sh ./modules ./token ./botadmin ./botacl ./data-bot-bash ./count
-
-
-# source bashbot.sh WITH BASHBOT_HOME set
-export BASHBOT_HOME=/usr/local/telegram-bot-bash
-source ./bashbot.sh source
-
-# output bashbot internal variables
-echo $COMMANDS $MODULEDIR $TOKENFILE $BOTADMIN $BOTACL $TMPDIR $COUNTFILE
-/usr/local/telegram-bot-bash/commands.sh /usr/local/telegram-bot-bash/modules /usr/local/telegram-bot-bash/token
-/usr/local/telegram-bot-bash/botadmin /usr/local/telegram-bot-bash/botacl /usr/local/telegram-bot-bash/data-bot-bash
-/usr/local/telegram-bot-bash/count
-

After sourcing you can use bashbot functions to send Messages, Locations, Pictures etc. to any Telegram User or Chat you are in. See Send Messages.

-

Examples: You can test this by sending messages to yourself:

-
# fist Hello World
-send_normal_message "$(< $BOTADMIN)"  "Hello World! This is my first message"
-
-# now with some markdown and  HTML
-send_markdown_message   "$(< $BOTADMIN)"  '*Hello World!* _This is my first markdown message_'
-send_html_message   "$(< $BOTADMIN)"  '<b>Hello World!</b> <em>This is my first HTML message</em>'
-send_keyboard "$(< $BOTADMIN)"  'Do you like it?' '[ "Yep" , "No" ]'
-

Now something more useful …

-
# sending output from system commands:
-send_normal_message "$(< $BOTADMIN)"  "$(date)"
-
-send_normal_message "$(< $BOTADMIN)"  "$(uptime)"
-
-send_normal_message       "$(< $BOTADMIN)"  '`'$(free)'`'
-
-# same but markdown style 'code' (monospaced)
-send_markdown_message   "$(< $BOTADMIN)"  "\`$(free)\`"
-

Bashbot environment

-

This section describe how you can customize bashbot to your needs by setting environment variables.

-

Change file locations

-

In standard setup bashbot is self containing, this means you can place ‘telegram-bot-bash’ any location and run it from there. All files - programm, config, data etc - will reside in ‘telegram-bot-bash’.

-

If you want to have other locations for config, data etc, define and export the following environment variables. Note: all specified directories and files must exist or running ‘bashbot.sh’ will fail.

-
BASHBOT_ETC
-

Location of the files commands.sh, mycommands.sh, token, botadmin, botacl

-
  unset  BASHBOT_ETC     # keep in telegram-bot-bash (default)
-  export BASHBOT_ETC ""  # keep in telegram-bot-bash
-
-  export BASHBOT_ETC "/etc/bashbot"  # unix like config location
-
-  export BASHBOT_ETC "/etc/bashbot/bot1"  # multibot configuration bot 1
-  export BASHBOT_ETC "/etc/bashbot/bot2"  # multibot configuration bot 2
-

e.g. /etc/bashbot

-
BASHBOT_VAR
-

Location of runtime data data-bot-bash, count

-
  unset  BASHBOT_VAR     # keep in telegram-bot-bash (default)
-  export BASHBOT_VAR ""  # keep in telegram-bot-bash
-
-  export BASHBOT_VAR "/var/spool/bashbot"  # unix like config location
-
-  export BASHBOT_VAR "/var/spool/bashbot/bot1"  # multibot configuration bot 1
-  export BASHBOT_VAR "/var/spool/bashbot/bot2"  # multibot configuration bot 2
-
BASHBOT_JSONSH
-

Full path to JSON.sh script, default: ‘./JSON.sh/JSON.sh’, must end with ‘/JSON.sh’.

-
  unset  BASHBOT_JSONSH     # telegram-bot-bash/JSON.sh/JSON.sh (default)
-  export BASHBOT_JSONSH ""  # telegram-bot-bash/JSON.sh/JSON.sh
-
-  export BASHBOT_JSONSH "/usr/local/bin/JSON.sh"  # installed in /usr/local/bin
-
BASHBOT_HOME
-

Set bashbot home directory, where bashot will look for additional files. If BASHBOT_ETC, BASHBOT_VAR or BASHBOT_JSONSH are set the have precedence over BASHBOT_HOME.

-

This is also usefull if you want to force bashbot to always use full pathnames instead of relative ones.

-
  unset  BASHBOT_HOME     # autodetection (default)
-  export BASHBOT_HOME ""  # autodetection
-
-  export BASHBOT_HOME "/usr/local/telegram-bot-bash"    # unix like location
-  export BASHBOT_HOME "/usr/local/bin"  # Note: you MUST set ETC, VAR and JSONSH to other locations to make this work!
-
-

Change config values

-
BASHBOT_URL
-

Uses given URL instead of offical telegram API URL, useful if you have your own telegram server or for testing.

-
  unset  BASHBOT_URL       # use Telegram URL https://api.telegram.org/bot<token> (default)
-
-  export BASHBOT_URL ""    # use use Telegram https://api.telegram.org/bot<token>
-
-  export BASHBOT_URL "https://my.url.com/bot" # use your URL https://my.url.com/bot<token>
-
BASHBOT_TOKEN
-
BASHBOT_WGET
-

Bashbot uses curl to communicate with telegram server. if curl is not availible wget is used. If ‘BASHBOT_WGET’ is set to any value (not undefined or not empty) wget is used even is curl is availible.

-
  unset  BASHBOT_WGET       # use curl (default)
-  export BASHBOT_WGET ""    # use curl 
-
-  export BASHBOT_WGET "yes" # use wget
-  export BASHBOT_WGET "no"  # use wget!
-
BASHBOT_SLEEP
-

Instead of polling permanently or with a fixed delay, bashbot offers a simple adaptive polling. If messages are recieved bashbot polls with no dealy. If no messages are availible bashbot add 100ms delay for every poll until the maximum of BASHBOT_SLEEP ms.

-
  unset  BASHBOT_SLEEP       # 5000ms (default)
-  export BASHBOT_SLEEP ""    # 5000ms 
-
-  export BASHBOT_SLEEP "1000"     # 1s maximum sleep 
-  export BASHBOT_SLEEP "10000"    # 10s maximum sleep
-  export BASHBOT_SLEEP "1"        # values < 1000 disables sleep (not recommended) 
-  
-

Testet configs as of v0.90 release

-

Note: Environment variables are not stored, you must setup them before every call to bashbot.sh, e.g. from a script.

-
simple Unix like config, for one bot. bashbot is installed in ‘/usr/local/telegram-bot-bash’
-
  # Note: all dirs and files must exist!
-  export BASHBOT_ETC "/etc/bashbot"
-  export BASHBOT_VAR "/var/spool/bashbot"
-
-  /usr/local/telegram-bot-bash/bashbot.sh start
-
Unix like config for one bot. bashbot.sh is installed in ‘/usr/bin’
-
  # Note: all dirs and files must exist!
-  export BASHBOT_ETC "/etc/bashbot"
-  export BASHBOT_VAR "/var/spool/bashbot"
-  export BASHBOT_JSONSH "/var/spool/bashbot"
-
-  /usr/local/bin/bashbot.sh start
-
simple multibot config, everything is keept inside ‘telegram-bot-bash’ dir
-
  # config for running Bot 1
-  # Note: all dirs and files must exist!
-  export BASHBOT_ETC "./mybot1"
-  export BASHBOT_VAR "./mybot1"
-
-  /usr/local/telegram-bot-bash/bashbot.sh start
-
  # config for running Bot 2
-  # Note: all dirs and files must exist!
-  export BASHBOT_ETC "./mybot2"
-  export BASHBOT_VAR "./mybot2"
-
-  /usr/local/telegram-bot-bash/bashbot.sh start
-

Prev Advanced Use

-

Next Best Practice

-


VERSION
v0.90-dev2-19-g5779acc

- - diff --git a/DIST/telegram-bot-bash/html/5_practice.html b/DIST/telegram-bot-bash/html/5_practice.html deleted file mode 100644 index 0938440..0000000 --- a/DIST/telegram-bot-bash/html/5_practice.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - - Bashobot Documentation - doc/5_practice.html - - - - - -

Home

-

Best Practices

-

New to bot development?

-

If you are new to Bot development read Bots: An introduction for developers and consult Telegram Bot API Documentation.

-

In addition you should know about BotFather, the one bot to rule them all. It will help you create new bots and change settings for existing ones. Commands known by Botfather

-

If you dont’t have a github account, it may time to sepup a free account now

-

Add commands to mycommands.sh only

-

To ease updates never change bashbot.sh, instead your commands and functions must go to mycommands.sh . Insert your Bot commands in the case ... esac block of the ‘mycommands()’ function:

-
# file: mycommands.sh
-# your additional bahsbot commands
-
-# uncomment the following lines to overwrite info and help messages
- bashbot_info='This is *MY* variant of _bashbot_, the Telegram bot written entirely in bash.
-'
-
- bashbot_help='*Available commands*:
-/echo message - _echo the given messsage_
-'
-
-# NOTE: command can have @botname attached, you must add * in case tests... 
-mycommands() {
-
-    case "$MESSAGE" in
-        '/echo'*) # example echo command
-            send_normal_message "${CHAT[ID]}" "$MESSAGE"
-            ;;
-        # .....
-    esac
-}
-

Overwrite, extend and disable global commands

-

You can overwrite a global bashbot command by placing the same commands in mycommands.sh and add return 1 ad the end of your command, see ‘/kickme’ below.

-

To disable a global bashbot command place create a command simply containing ‘return 1’, see ‘/leave’ below.

-

In case you want to add some processing to the global bashbot command add return 0, then both command will be executed.

-

Learn more about Bot commands.

-
# file: commands.sh
-
-    case "$MESSAGE" in
-        ##########
-        # command overwrite examples
-        'info'*) # output date in front of regular info
-            send_normal_message "${CHAT[ID]}" "$(date)"
-            return 0
-            ;;
-        '/kickme'*) # this will replace the /kickme command
-            send_markdown_mesage "${CHAT[ID]}" "*This bot will not kick you!*"
-            return 1
-            ;;
-        '/leave'*) # disable all commands starting with leave
-            return 1
-            ;;
-    esac
-

Seperate logic from commands

-

If a command need more than 2-3 lines of code, you should use a function to seperate logic from command. Place your functions in mycommands.sh and call the from your command. Example:

-
# file: mycommands.sh
-# your additional bahsbot commands
-
-mycommands() {
-
-    case "$MESSAGE" in
-        '/process'*) # logic for /process is done in process_message 
-            result="$(process_message "$MESSAGE")"
-            send_normal_message "${CHAT[ID]}" "$result" 
-            ;;
-    esac
-
-}
-
-# place your functions here
-
-process_message() {
-   local ARGS="${1#/* }"    # remove command 
-   local TEXT OUTPUT=""
-
-   # process every word in MESSAGE, avoid globbing
-   set -f
-   for WORD in $ARGS
-   do
-    # process links 
-    if [[ "$WORD" == "https://"* ]]; then
-        REPORT="$(dosomething_with_link "$WORD")"
-    # no link, add as text
-    else
-        TEXT="$(echo "${TEXT} $WORD")"
-        continue
-    fi
-    # compose result
-    OUTPUT="* ${REPORT} ${WORD} ${TEXT}"
-    TEXT=""
-   done
-
-   # return result, reset globbing in case we had no ARGS
-   echo "${OUTPUT}${TEXT}"
-}
-

Test your Bot with shellcheck

-

Shellcheck is a static linter for shell scripts providing excellent tips and hints for shell coding pittfalls. You can use it online or install it on your system. All bashbot scripts are linted by shellcheck.

-

Shellcheck examples:

-
$ shellcheck -x mybotcommands.inc.sh
- 
-Line 17:
-                TEXT="$(echo "${TEXT} $WORD")"
-                      ^-- SC2116: Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'.
- 
-

As you can see my mybotcommands.inc.sh contains an useless echo command in ‘TEXT=’ assigment and can be replaced by TEXT="${TEXT}${WORD}"

-
$ shellcheck -x examples/notify
-OK
-$ shellcheck -x examples/question
-OK
-$ shellcheck -x commands.sh
-OK
-$ shellcheck -x bashbot.sh
-
-In bashbot.sh line 123:
-                text="$(echo "$text" | sed 's/ mynewlinestartshere /\r\n/g')" # hack for linebreaks in startproc scripts
-                        ^-- SC2001: See if you can use ${variable//search/replace} instead.
-
-
-In bashbot.sh line 490:
-        CONTACT[USER_ID]="$(sed -n -e '/\["result",'$PROCESS_NUMBER',"message","contact","user_id"\]/  s/.*\][ \t]"\(.*\)"$/\1/p' <"$TMP")"
-        ^-- SC2034: CONTACT appears unused. Verify it or export it.
-

The example show two warnings in bashbots scripts. The first is a hint you may use shell substitions instead of sed, this is fixed and much faster as the “echo | sed” solution. The second warning is about an unused variable, this is true because in our examples CONTACT is not used but assigned in case you want to use it :-)

-

Prev Best Practice

-

Next Functions Reference

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/6_reference.html b/DIST/telegram-bot-bash/html/6_reference.html deleted file mode 100644 index 2fd3fcf..0000000 --- a/DIST/telegram-bot-bash/html/6_reference.html +++ /dev/null @@ -1,453 +0,0 @@ - - - - - - - Bashobot Documentation - doc/6_reference.html - - - - - -

Home

-

Bashbot function reference

-

Send, forward, delete messages

-
send_action
-

send_action shows users what your bot is currently doing.

-

usage: send_action “${CHAT[ID]}” “action”

-

“action”: typing, upload_photo, record_video, upload_video, record_audio, upload_audio, upload_document, find_location.

-

alias: _action “action”

-

example:

-
send_action "${CHAT[ID]}" "typing"
-send_action "${CHAT[ID]}" "record_audio"
-
send_normal_message
-

send_normal_message sends text only messages to the given chat.

-

usage: send_normal_message “${CHAT[ID]}” “message”

-

alias: _normal_message “message”

-

example:

-
send_normal_message "${CHAT[ID]}" "this is a text message"
-
send_markdown_message
-

send_markdown_message sends markdown style messages to the given chat. Telegram supports a reduced set of Markdown only

-

usage: send_markdown_message “${CHAT[ID]}” “markdown message”

-

alias: _markdown “message”

-

example:

-
send_markdown_message "${CHAT[ID]}" "this is a markdown  message, next word is *bold*"
-send_markdown_message "${CHAT[ID]}" "*bold* _italic_ [text](link)"
-
send_html_message
-

send_html_message sends HTML style messages to the given chat. Telegram supports a reduced set of HTML only

-

usage: send_html_message “${CHAT[ID]}” “html message”

-

alias: _html_message “message”

-

example:

-
send_normal_message "${CHAT[ID]}" "this is a markdown  message, next word is <b>bold</b>"
-send_normal_message "${CHAT[ID]}" "<b>bold</b> <i>italic><i> <em>italic>/em> <a href="link">Text</a>"
-
forward_message
-

forward_mesage forwards a messsage to the given chat.

-

usage: forward_message “chat_to” “chat_from” “${MESSAGE[ID]}”

-

old call: forward “${CHAT[ID]}" "$FROMCHAT” “${MESSAGE[ID]}”

-

alias: _forward “$FROMCHAT" "${MESSAGE[ID]}”

-

See also Text formating options

-
-
delete_message
-

If your Bot is admin of a Chat he can delete every message, if not he can delete only his messages.

-

usage: delete_message “${CHAT[ID]}" "${MESSAGE[ID]}”

-

alias: _del_message “${MESSAGE[ID]}”

-

See also deleteMessage limitations

-
-
send_message
-

send_message sends any type of message to the given chat. Type of output is steered by keywords within the message.

-

The main use case for send_message is to process the output of interactive chats and background jobs. For regular Bot commands I recommend using of the dedicated send_xxx_message() functions from above.

-

usage: send_message “${CHAT[ID]}” “message”

-

example: - see Usage and Advanced Usage

-
-

File, Location, Venue, Keyboard

-
send_file
-

send_file allows you to send different type’s of files, e.g. photos, stickers, audio, media, etc. see more

-

Starting with version 0.80 send_file implements the following rules:

-
    -
  • file names must not contain “..”
  • -
  • file names must not start with “.”
  • -
  • file names not starting wit “/” are realtive to $TMPDIR, e.g. ./data-bot-bash
  • -
  • abolute filenames must match $FILE_REGEX
  • -
-

usage: send_file “${CHAT[ID]}” “file” “caption”

-

example:

-
send_file "${CHAT[ID]}" "/home/user/doge.jpg" "Lool"
-send_file "${CHAT[ID]}" "https://www.domain,com/something.gif" "Something"
-
send_location
-

usage: send_location “${CHAT[ID]}” “Latitude” “Longitude”

-
send_venue
-

usage: send_venue “${CHAT[ID]}” “Latitude” “Longitude” “Title” “Address” “foursquare id (optional)”

-
-
send_keyboard
-

Note: since version 0.6 send_keyboard was changed to use native “JSON Array” notation as used from Telegram. Example Keybord Array definitions:

-
    -
  • yes no in two rows: -
      -
    • OLD format: ‘yes’ ‘no’ (two strings)
    • -
    • NEW format: ‘[ “yes” ] , [ “no” ]’ (two arrays with a string)
    • -
  • -
  • new layouts made easy with NEW format: -
      -
    • Yes No in one row: ‘[ “yes” , “no” ]’
    • -
    • Yes No plus Maybe in 2.row: ‘[ “yes” , “no” ] , [ “maybe” ]’
    • -
    • numpad style keyboard: ‘[ “1” , “2” , “3” ] , [ “4” , “5” , “6” ] , [ “7” , “8” , “9” ] , [ “0” ]’
    • -
  • -
-

usage: send_keyboard “chat-id” “message” “keyboard”

-

alias: _keyboard “message” “keyboard”

-

example:

-
send_keyboard "${CHAT[ID]}" "Say yes or no" "[ \\"yes\" , \\"no\" ]""
-send_keyboard "${CHAT[ID]}" "Say yes or no" "[ \\"yes\\" ] , [ \\"no\\" ]"
-send_keyboard "${CHAT[ID]}" "Enter digit" "[ \\"1\\" , \\"2\\" , \\"3\\" ] , [ \\"4\\" , \\"5\\" , \\"6\\" ] , [ \\"7\\" , \\"8\\" , \\"9\\" ] , [ \\"0\\" ]"
-
remove_keyboard
-

usage: remove_keybord “$CHAT[ID]” “message”

-

alias: _del_keyboard “message”

-

See also: Keyboard Markup

-
-
send_button
-

usage: send_button “chat-id” “message” “text” “URL”

-

alias: _button “text” “URL”

-

example:

-
send_button "${CHAT[ID]}" "MAKE MONEY FAST!!!" "Visit my Shop" "https://dealz.rrr.de"
-
send_inline_keyboard
-

This allows to place multiple inline buttons in a row. The inline buttons must specified as a JSON array in the following format:

-

[ {"text":"text1", "url":"url1"}, ... {"text":"textN", "url":"urlN"} ]

-

Each button consists of a pair of text and URL values, sourrounded by ‘{ }’, multiple buttons are seperated by ‘,’ and everthing is wrapped in ‘[ ]’.

-

usage: send_inline_keyboard “chat-id” “message” “[ {“text”:“text”, “url”:“url”} …]”

-

alias: _inline_keyboard “[{“text”:“text”, “url”:“url”} …]”

-

example:

-
send_inline_keyboard "${CHAT[ID]}" "MAKE MONEY FAST!!!" '[{"text":"Visit my Shop", url"":"https://dealz.rrr.de"}]'
-send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"button 1", url"":"url 1"}, {"text":"button 2", url"":"url 2"} ]'
-send_inline_keyboard "${CHAT[ID]}" "" '[{"text":"b 1", url"":"u 1"}, {"text":"b 2", url"":"u 2"}, {"text":"b 2", url"":"u 2"} ]'
-

See also Inline keyboard markup

-
-

User Access Control

-
kick_chat_member
-

If your Bot is a chat admin he can kick and ban a user.

-

usage: kick_chat_member “${CHAT[ID]}" "${USER[ID]}”

-

alias: _kick_user “${USER[ID]}”

-
unban_chat_member
-

If your Bot is a chat admine can unban a kicked user.

-

usage: unban_chat_member “${CHAT[ID]}" "${USER[ID]}”

-

alias: _unban “${USER[ID]}”

-
leave_chat
-

Your Bot will leave the chat.

-

usage: leave_chat “${CHAT[ID]}”

-

alias: _leave

-
if _is_admin ; then 
- send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*"
- leave_chat "${CHAT[ID]}"
-fi
-

’See also kick Chat Member*

-
-
user_is_botadmin
-

Return true (0) if user is admin of bot, user id if botadmin is read from file ‘./botadmin’.

-

usage: user_is_botadmin “${USER[ID]}”

-

alias: _is_botadmin

-

example:

-
 _is_botadmin && send_markdown_message "${CHAT[ID]}" "You are *BOTADMIN*."
-
user_is_creator
-

Return true (0) if user is creator of given chat or chat is a private chat.

-

usage: user_is_creator “${CHAT[ID]}" "${USER[ID]}”

-

alias: _is_creator

-
user_is_admin
-

Return true (0) if user is admin or creator of given chat.

-

usage: user_is_admin “${CHAT[ID]}" "${USER[ID]}”

-

alias: _is_admin

-

example:

-
if _is_admin ; then 
-  send_markdown_message "${CHAT[ID]}" "*LEAVING CHAT...*"
-  leave_chat "${CHAT[ID]}"
-fi
-

See also Chat Member

-
user_is_allowed
-

Bahsbot supports User Access Control, see Advanced Usage

-

usage: user_is_allowed “${USER[ID]}" "what" "${CHAT[ID]}”

-

example:

-
if ! user_is_allowed "${USER[ID]}" "start" "${CHAT[ID]}" ; then
-  send_normal_message "${CHAT[ID]}" "You are not allowed to start Bot."
-fi
-
-

Inline Queries - answer direct queries to bot

-

You must include source modules/inline.sh in ‘commands.sh’ to have the following functions availible.

-

Inline Queries allows users to interact with your bot directly without sending extra commands. As an answer to an inline query you can send back one or more results to the Telegram client. The Telegram client will then show the results to the user and let him select one.

-
answer_inline_query
-

answer_inline_query is provided for backward compatibility with older versions of bashbot. It send back only one response to an inline query.

-

usage: answer_inline_query “$i{QUERY[ID]}” “type” “type arg 1” … “type arg n”

-

example: - see Advanced Usage

-
answer_inline_multi
-

anser_inline_multi allows you to send back a list of responses. responses must be seperated by ‘,’.

-

usage: answer_inline_multi “${iQUERY[ID]}” “res, res, … res”

-

example:

-
# note the starting " and ending " !!
-answer_inline_multi "${iQUERY[ID]}" "
-    $(inline_query_compose "1" "photo" "https://avatars0.githubusercontent.com/u/13046303") ,
-    ...
-    $(inline_query_compose "n" "photo" "https://avatars1.githubusercontent.com/u/4593242")
-    "
-

inline_query_compose

-

inline_query_compose composes one response element to to send back.

-

usage: inline_query_compose ID type args ….

-
    ID = unique ID for this response, 1-64 byte long
-    type = type of answer, e.g. article, photo, video, location ...
-    args = mandatory arguments in the order they are described in telegram documentation
-

Currently the following types and arguments are implemented (optional arguments in parenthesis)

-
    "article"|"message" title message (markup description)
-
-    "photo"         photo_URL (thumb_URL title description caption)
-    "gif"           photo_URL (thumb_URL title caption)
-    "mpeg4_gif"     mpeg_URL (thumb_URL title caption)
-    "video"         video_URL mime_type thumb_URL title (caption)
-    "audio"         audio_URL title (caption)
-    "voice"         voice_URL title (caption)
-    "document"      title document_URL mime_type (caption description)
-
-    "location"      latitude longitude title
-    "venue"         latitude longitude title (adress foursquare)
-    "contact"       phone first (last thumb)
-
-    "cached_photo"      file (title description caption)
-    "cached_gif"        file (title caption)
-    "cached_mpeg4_gif"  file (title caption)
-    "cached_sticker"    file 
-    "cached_document"   title file (description caption)
-    "cached_video"      file title (description caption)
-    "cached_voice"      file title (caption)
-    "cached_audio"      file title (caption)
-

see InlineQueryResult for more information about response types and their arguments.

-
-

Background and Interactive jobs

-

You must include source modules/background.sh in ‘commands.sh’ to have the following functions availible.

-
start_proc
-

startproc starts a script, the output of the script is sent to the user or chat, user input will be sent back to the script. see Advanced Usage

-

usage: start_proc “${CHAT[ID]}” “script”

-

alias: startproc “script”

-

example:

-
startproc 'examples/calc.sh'
-
check_proc
-

Return true (0) if an interactive script is running in the chat.

-

usage: check_prog “${CHAT[ID]}”

-

alias: checkprog

-

example:

-
if ! check_proc "${CHAT[ID]}" ; then
-  startproc "examples/calc.sh"
-else
-   send_normal_message "${CHAT[ID]}" "Calc already running ..."
-fi
-
kill_proc
-

Kill the interactive script running in the chat

-

usage: kill_proc “${CHAT[ID]}”

-

alias: killproc

-

example:

-
if check_proc "${CHAT[ID]}" ; then
-  killproc && send_message "${CHAT[ID]}" "Command canceled."
-else
-  send_message "${CHAT[ID]}" "Command is not running."
-fi
-
-
start_back
-

Starts a script as a background job and attaches a jobname to it. All output from a background job is sent to the associated chat.

-

In contrast to interactive chats, background jobs do not recieve user input and can run forever. In addition you can suspend and restart running jobs, e.g. after reboot.

-

usage: start_back “${CHAT[ID]}” “script” “jobname”

-

alias: background “script” “jobname”

-

example:

-
background "examples/notify.sh" "notify"
-
check_back
-

Return true (0) if an background job is active in the given chat.

-

usage: check_back “${CHAT[ID]}” “jobname”

-

alias: checkback “jobname”

-

example:

-
if ! checkback "notify" ; then
-  send_normal_message "${CHAT[ID]}" "Start notify"
-  background "examples/notify.sh" "notify"
-else
- send_normal_message "${CHAT[ID]}" "Process notify already running."
-fi
-
kill_back
-

usage: kill_back “${CHAT[ID]}” “jobname”

-

alias: killback “jobname”

-

example:

-
checkback "notify"
-if [ "$res" -eq 0 ] ; then
-  send_normal_message "${CHAT[ID]}" "Kill notify"
-  killback "notify"
-else
-  send_normal_message "${CHAT[ID]}" "Process notify not run."
-fi
-
-
send_interactive
-

Form version 0.80 on forward_message is used to forward messages to interactive job. It replaces the old ‘inproc’ commands used for TMUX. Usually message is automatically forwarded in ‘commands.sh’, but you can forward messages wihle processing also or send your own messages.

-

usage: send_interactive “${CHAT[ID]}” “message”

-

replaces:’ incproc

-

Aliases - shortcuts for often used funtions

-

You must include source modules/aliases.sh in ‘commands.sh’ to have the following functions availible.

-
_is_botadmin
-

usage: _is_botadmin

-

alias for: user_is_botadmin “${USER[ID]}”

-
_is_admin
-

usage: _is_admin

-

alias for: user_is_admin “${CHAT[ID]}" "${USER[ID]}”

-
_is_allowed
-

usage: _is_allowed “what”

-

alias for: user_is_allowed “${USER[ID]}" "what" "${CHAT[ID]}”

-
-
_kick_user
-

usage: _kick_user “${USER[ID]}”

-

alias for: kick_chat_member “${CHAT[ID]}" "${USER[ID]}”

-
_unban
-

usage: _unban “${USER[ID]}”

-

alias for: unban_chat_member “${CHAT[ID]}" "${USER[ID]}”

-
_leave
-

usage: _leave

-

alias for: leave_chat “${CHAT[ID]}”

-
-
_message
-

usage: _message “message”

-

alias for: send_normal_message “${CHAT[ID]}” “message”

-
_normal_message
-

usage: _normal_message “message”

-

alias for: send_normal_message “${CHAT[ID]}” “message”

-
_html_message
-

usage: _html_message “message”

-

alias for: send_html_message “${CHAT[ID]}” “message”

-
_markdown_message
-

usage: _markdown_message “message”

-

alias for: send_markdown_message “${CHAT[ID]}” “message”

-
-

_inline_button usage: _inline_button “${1}" "${2}”

-

alias for: send_inline_button “${CHAT[ID]}" "" "${1}” “${2}”

-

_inline_keyboard usage: _inline_keyboard “${1}”

-

alias for: _inline_keyboard “${CHAT[ID]}" "" "${1}”

-

_keyboard_numpad usage: _keyboard_numpad

-

alias for: send_keyboard “${CHAT[ID]}” “” ‘[“1”,“2”,“3”],[“4”,“5”,“6”],[“7”,“8”,“9”],[“-”,“0”,“.”]’ “yes”

-

_keyboard_yesno usage: _keyboard_yesno

-

alias for: send_keyboard ‘[“yes”,“no”]’

-

_del_keyboard usage: _del_keyboard

-

alias for: remove_keyboard “${CHAT[ID]}” “”

-

Helper functions

-
download
-

Download the fiven URL ans returns the final filename in TMPDIR. If the given filename exists,the filename is prefixed with a random number. filename is not allowed to contain ‘/’ or ‘..’.

-

usage: download URL filename

-

example:

-
file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")"
-echo "$file" -> ./data-bot-bash/avatar.jpg
-file="$(download "https://avatars.githubusercontent.com/u/13046303" "avatar.jpg")"
-echo "$file" -> ./data-bot-bash/12345-avatar.jpg
-
_exists Returns true if the given function exist, can be used to check if a module is loaded.
-

usage _exists command

-

example:

-
_exists "curl" && _message "Command curl is not installed!"
-
_is_function Returns true if the given function exist, can be used to check if a module is loaded.
-

usage _is_function function

-

example:

-
_is_function "background" && _message "you can run background jobs!"
-
-

Bashbot internal functions

-

These functions are for internal use only and must not used in your bot commands.

-
procname
-

Returns PrefixBotname_Postfix

-

usage: procname postfix prefix

-

example:

-
# returns botname, if already set
-procname 
-# returns unique identifier for everthing related to chat
-procname "${CHAT[ID]}"
-# returns unique identifier for job, regardless of chat
-procname "" "back-jobname-"
-# returns unique identifier for a job related to a chat
-# e.g. fifo, cmd and logfile name
-procname "${CHAT[ID]}" "back-jobname-"
-
proclist
-

Returns process IDs of current bot processes containing string ‘pattern’ in name or argument.

-

usage: proclist pattern

-

example:

-
# list PIDs of all background processes
-proclist "back-"
-# list PIDs of all processes of a job
-proclist "back-jobname-"
-# list PIDs of all processes for a chat
-proclist "_${CHAT[ID]}"
-# list PIDs of all bot processes
-proclist 
-
killallproc
-

kill all current bot processes containing string ‘pattern’ in name or argument

-

usage: killallproc pattern

-

example:

-
# kill all background processes
-killallproc "back-"
-# kill all processes for a chat
-killallproc "_${CHAT[ID]}"
-# kill all bot processes, including YOURSELF!
-killallproc 
-
get_file
-

usage: url=“$(get_file "${CHAT[ID]}” “message”)"

-
-
send_text
-

usage: send_text “${CHAT[ID]}” “message”

-
-
JsonDecode
-

Outputs decoded string to STDOUT

-

usage: JsonDecode “string”

-
JsonGetString
-

Reads JSON fro STDIN and Outputs found String to STDOUT

-

usage: JsonGetString "path","to","string"

-
JsonGetValue
-

Reads JSON fro STDIN and Outputs found Value to STDOUT

-

usage: JsonGetValue "path","to","value"

-
-
get_chat_member_status
-

usage: get_chat_member_status “${CHAT[ID]}" "${USER[ID]}”

-

this may get an official function …

-
-
process_client
-

Every Message sent to your Bot is processd by this function. It parse the send JSON and assign the found Values to bash variables.

-
process_updates
-

If new updates are availible, this functions gets the JSON from Telegram and dispatch it.

-
-
getBotName
-

The name of your bot is availible as bash variable “$ME”, there is no need to call this function if Bot is running.

-

usage: ME=“$(getBotNiname)”

-

Prev Best Practice

-

Next Notes for Developers

-


VERSION
v0.90-dev2-0-gec85636

- - diff --git a/DIST/telegram-bot-bash/html/7_develop.html b/DIST/telegram-bot-bash/html/7_develop.html deleted file mode 100644 index 51a3579..0000000 --- a/DIST/telegram-bot-bash/html/7_develop.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - Bashobot Documentation - doc/7_develop.html - - - - - -

Home

-

Notes for bashbot developers

-

This section is about help and best practices for new bashbot developers. The main focus on is creating new versions of bashbot, not on develop your individual bot. Nevertheless the rules and tools described here can also help you with your bot development.

-

bashbot development is done on github. If you want to provide fixes or new features fork bashbot on githup and provide changes as pull request on github.

-

Debugging Bashbot

-

In normal mode of operation all bashbot output is discarded. To get these messages (and more) you can start bashbot in the current shell ./bashbot.sh startbot. Now you can see all output or erros from bashbot. In addition you can change the change the level of verbosity by adding a third argument after startbot.

-
    "debug"     redirects all output to "DEBUG.log", in addtion every update is logged in "MESSAGE.LOG" and "INLINE.log"
-    "debugterm" same as debug but output and errors are sent to terminal
-
-    "xdebug"    same as debug plus set bash option '-x' to log any executed command
-    "xdebugterm"    same as xdebug but output and errors are sent to terminal
-

Modules and Addons

-

Modules live in modules/*.sh and are bashbot functions factored out in seperate files, gouped by functionality. Main reason for creating modules was to keep ‘bashbot.sh’ small, while extending functionality. In addition not every functionality is needed by a bot, so you can disable modules by removing them, e.g. rename the respective module files to ‘module.sh.off’.

-

Modules must use onyl functions provided by ‘bahsbot.sh’ or the module itself, no depedencies to other modules or addons must exist. If a module function is called from ‘bashbot.sh’, bashbot must work if the module is disabled, so it’s madatory to use ’_is_function’ or ’_execute_if_function’ if a module function is called.

-

Addons live in addons/*.sh.dist and are disabled by default. To activate an addon remove the ‘.dist’ from filename, e.g. cp addons/example.sh.dist addons/example.sh. Addons must register themself to BASHBOT_EVENTS at startup, e.g. to call a function everytime a message is recieved. Registering to EVENTS is similar on how ‘commands.sh’ is ececuted, but more flexible and one major difference: Addons are executed in the context of the main script, while ‘commands.sh’ is executed as a seperate process.

-

This is why event functions are time critical and must return as fast as possible. Spawn actions as a seperate process or function with ‘&’, e.g. send a message as respone from an addon: send_message "${CHAT[ID]}" "Message to send ..." &.

-

Bashbot Events

-

Addons can register functions to bashbot events at startup by providing their name and a callback function. If an event occours each registered function for the event is called.

-

Events run in the same context as the main bashbot loop, so variables set here are persistent as long bashbot is running.

-

Note: For the same reason event function MUST return imideatly! Time consuming tasks must be run in background or as a subshell, e.g. “long running &”

-

Availible events:

-
    -
  • BASHBOT_EVENT_INLINE an inline query is received
  • -
  • BASHBOT_EVENT_MESSAGE any type of message is received
  • -
  • BASHBOT_EVENT_TEXT a message containing text is received
  • -
  • BASHBOT_EVENT_CMD a command is recieved (fist word starts with /)
  • -
  • BASHBOT_EVENT_REPLYTO a reply to a message is received
  • -
  • BASHBOT_EVENT_FORWARD a forwarded message is received
  • -
  • BASHBOT_EVENT_CONTACT a contact is received
  • -
  • BASHBOT_EVENT_LOCATION a location or a venue is received
  • -
  • BASHBOT_EVENT_FILE a file is received
  • -
-

usage: BASHBOT_EVENT_xxx[“uniqe-name”]=“callback”

-

“unique-name” can be every alphanumeric string incl. ‘-’ and ’_’. Per convention it should be name of the addon followed by an internal identyfier.

-

Example: Register a function to echo to any Text send to the bot

-
# register callback:
-BASHBOT_EVENT_TEXT["example_1"]="example_echo"
-
-# function called if a text is recieved
-example_echo() {
-    # all availible bashbot functions and variables can be used
-    send_normal_message "${CHAT[ID]}" "${MESSAGE[0]}" & # note the &!
-}
-
    -
  • BAHSBOT_EVENT_TIMER is executed every minute and can be uses in variants: oneshot, every minute, every X minutes.
  • -
-

Registering to BASHBOT_EVENT_TIMER works a little different, you have to add a timing argument to the index name.

-

usage: BAHSBOT_EVENT_TIMER[“name”,“time”], where time is:

-
    -
  • -x execute ONCE in x minutes
  • -
  • 0 ignored
  • -
  • 1 execute every minute
  • -
  • x execute every x minutes
  • -
-

Examples:

-
# register callback:
-BAHSBOT_EVENT_TIMER["example_every","1"]="example_everymin"
-
-# function called every minute
-example_everymin() {
-    # timer events has no chat id, so send to yourself
-    send_normal_message "$(< "${BOTADMIN})" "$(date)" & # note the &!
-}
-
-# register other callback:
-BAHSBOT_EVENT_TIMER["example_10min","-10"]="example_in10min"
-
-BAHSBOT_EVENT_TIMER["example_every5","5"]="example_every5min"
-
-
-

Create a stripped down Version of your Bot

-

Currently bashbot is more a bot development environment than a bot, containing examples, developer scripts, modules, documentation and more. You don’t need all these files after you’re finished with your cool new bot.

-

Let’s create a stripped down version:

-
    -
  • delete all modules you do not need from ‘modules’, e.g. ‘modules/inline.sh’ if you don’t use inline queries
  • -
  • delete not needed standard commands and messages from ‘commands.sh’
  • -
  • delete not needed commands and functions from ‘mycommands.sh’
  • -
  • run dev/make-standalone.sh to create a a stripped down version of your bo
  • -
-

Now have a look at the directory ‘standalone’, here you find the files ‘bashbot.sh’ and ‘commands.sh’ containing everything to run your bot. Download make-standalone.sh from github.

-

Setup your develop environment

-
    -
  1. install git, install shellcheck
  2. -
  3. setup your environment for UTF-8
  4. -
  5. clone your bashbot fork to a new directory git clone https://github.com/<YOURNAME>/telegram-bot-bash.git, replace <YOURNAME> with your username on github
  6. -
  7. create and change to your develop branch git checkout -b <YOURBRANCH>, replace <YOURBRANCH> with the name you want to name it, e.g. ‘develop’
  8. -
  9. give your (dev) fork a new version tag: git tag vx.xx(optional)
  10. -
  11. setup github hooks by running dev/install-hooks.sh (optional)
  12. -
-

Test, Add, Push changes

-

A typical bashbot develop loop looks as follow:

-
    -
  1. start developing - change, copy, edit bashbot files …
  2. -
  3. after changing a bash sript: shellcheck -x scipt.sh
  4. -
  5. dev/all-tests.sh - in case if errors back to 2.
  6. -
  7. dev/git-add.sh - check for changed files, update version string, run git add
  8. -
  9. git commit -m "COMMIT MESSAGE"; git push
  10. -
-

If you setup your dev environment with hooks and use the scripts above, versioning, addding and testing is done automatically.

-

common commands

-

We state bashbot is a bash only bot, but this is not true. bashbot is a bash script using bash features PLUS external commands. Usually bash is used in a unix/linux environment where many (GNU) commands are availible, but if commands are missing, bashbot may not work.

-

To avoid this and make bashbot working on as many platforms as possible - from embedded linux to mainframe - I recommed to restrict ourself to the common commands provided by bash and coreutils/busybox/toybox. See Bash Builtins, coreutils, busybox and toybox

-

Availible commands in bash, coreutils, busybox and toybox. Do you find curl on the list?

-
    .*, [*, [[*, basename, break, builtin*, bzcat, caller*, cat, cd*, chattr,
-    chgrp, chmod, chown, clear, command*, continue *, cp, cut, date, declare*,
-    dc, dd, df, diff, dirname, du, echo*, eval*, exec*, exit *, expr*, find,
-    fuser, getopt*, grep, hash*, head, hexdump, id, kill, killall, last, length,
-    less, let*, ln, local*, logname, ls, lsattr, lsmod, man, mapfile*, md5sum, mkdir,
-    mkfifo, mknod, more, mv, nice, nohup, passwd, patch, printf*, ps, pwd*, read*,
-    readarray*, readonly* return*, rm, rmdir, sed, seq, sha1sum, shift*, sleep,
-    source*, sort, split, stat, strings, su, sync, tail, tar, tee, test,
-    time, times*, timeout, touch, tr, trap*, true, umask*, usleep, uudecode,
-    uuencode, wc, wget, which, who, whoami, xargs, yes
-

commands marked with * are bash builtins, all others are external programms. Calling an external programm is more expensive then using bulitins or using an internal replacement. Here are some examples of internal replacement for external commands:

-
HOST="$(hostname)" -> HOST="$HOSTNAME"
-
-seq 1 100 -> {0..100}
-
-data="$(cat file)" -> data="$(<"file")"
-
-DIR="$(dirname $0) -> DIR=""${0%/*}/""
-
-IAM="($basename $0)" -> IAM="${0##*/}*
-
-VAR="$(( 1 + 2 ))" -> (( var=1+2 ))
-
-INDEX="$(( ${INDEX} + 1 ))" -> (( INDEX++ ))
-

For more examples see Pure bash bible

-

Prepare a new version

-

After some development it may time to create a new version for the users. a new version can be in sub version upgrade, e.g. for fixes and smaller additions or a new release version for new features. To mark a new version use git tag NEWVERSION and run dev/version.sh to update all version strings.

-

Usually I start with pre versions and when everything looks good I push out a release candidate (rc) and finally the new version.

-
 v0.x-devx -> v0.x-prex -> v0.x-rc -> v0.x  ... 0.x+1-dev ...
-

If you release a new Version run dev/make-distribution.sh to create the zip and tar.gz archives in the dist directory and attach them to the github release. Do not forget to delete directory dist afterwards.

-

Versioning

-

Bashbot is tagged with version numbers. If you start a new development cycle you can tag your fork with a version higher than the current version. E.g. if you fork ‘v0.60’ the next develop version should tagged as git tag "v0.61-dev" for fixes or git tag "v0.70-dev" for new features.

-

To get the current version name of your develepment fork run git describe --tags. The output looks like v0.70-dev-6-g3fb7796 where your version tag is followed by the number of commits since you tag your branch and followed by the latest commit hash. see also comments in version.sh

-

To update the Version Number in files run dev/version.sh files, it will update the line ‘####
VERSION
###’ in all files to the current version name. To update version in all files run ‘dev/version.sh’ without parameter.

-

Shellcheck

-

For a shell script running as a service it’s important to be paranoid about quoting, globbing and other common problems. So it’s a must to run shellchek on all shell scripts before you commit a change. this is automated by a git hook activated in Setup step 6.

-

To run shellcheck for a single script run shellcheck -x script.sh, to check all schripts run dev/hooks/pre-commit.sh.

-

bashbot tests

-

Starting with version 0.70 bashbot has a test suite. To start testsuite run dev/all-tests.sh. all-tests.sh will return ‘SUCCESS’ only if all tests pass.

-

enabling / disabling tests

-

All tests are placed in the directory test. To disable a test remove the execute flag from the ’*-test.sh’ script, to (re)enable a test make the script executable again.

-

creating new tests

-

To create a new test run test/ADD-test-new.sh and answer the questions, it will create the usually needed files and dirs:

-

Each test consists of a script script named after p-name-test.sh (where p is test pass ‘a-z’ and name the name of your test) and an optional dir p-name-test/ (script name minus ‘.sh’) for additional files.

-

Tests with no dependency to other tests will run in pass ‘a’, tests which need an initialized bahsbot environment must run in pass ‘d’ or later. A temporary test environment is created when ‘ALL-tests.sh’ starts and deleted after all tests are finished.

-

The file ALL-tests.inc.sh must be included from all tests and provide the test environment as shell variables:

-
# Test Evironment
- TESTME="$(basename "$0")"
- DIRME="$(pwd)"
- TESTDIR="$1"
- LOGFILE="${TESTDIR}/${TESTME}.log"
- REFDIR="${TESTME%.sh}"
- TESTNAME="${REFDIR//-/ }"
-
-# common filenames
- TOKENFILE="token"
- ACLFILE="botacl"
- COUNTFILE="count"
- ADMINFILE="botadmin"
- DATADIR="data-bot-bash"
-
-# SUCCESS NOSUCCES -> echo "${SUCCESS}" or echo "${NOSUCCESS}" 
- SUCCESS="   OK"
- NOSUCCESS="   FAILED!"
-
-# default input, reference and output files
- INPUTFILE="${DIRME}/${REFDIR}/${REFDIR}.input"
- REFFILE="${DIRME}/${REFDIR}/${REFDIR}.result"
- OUTPUTFILE="${TESTDIR}/${REFDIR}.out"
-

Example test

-
#!/usr/bin/env bash
-# file: b-example-test.sh
-
-# include common functions and definitions
-# shellcheck source=test/ALL-tests.inc.sh
-source "./ALL-tests.inc.sh"
-
-if [ -f "${TESTDIR}/bashbot.sh" ]; then
-    echo "${SUCCESS} bashbot.sh exist!"
-    exit 0
-else
-    echo "${NOSUCCESS} ${TESTDIR}/bashbot.sh missing!"
-    exit 1
-fi
-

Prev Function Reference

-

Next Expert Use

-


VERSION
v0.90-dev2-19-g5779acc

- - diff --git a/DIST/telegram-bot-bash/html/index.html b/DIST/telegram-bot-bash/html/index.html deleted file mode 100644 index 103b9b2..0000000 --- a/DIST/telegram-bot-bash/html/index.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - Bashbot README - - - - - -

- Bashbot - A Telegram bot written in bash. -

-

Written by Drew (@topkecleon), Daniil Gentili (@danogentili), and Kay M (@gnadelwartz).

-

Contributions by JuanPotato, BigNerd95, TiagoDanin, and iicc1.

-

Released to the public domain wherever applicable. Elsewhere, consider it released under the WTFPLv2.

-

Prerequisites

-

Uses JSON.sh, but no more TMUX.

-

Even bashbot is written in bash, it depends on commands typically availible in a Unix/Linux Environment. More concret on the common commands provided by coreutils, busybox or toybox, see Developer Notes

-

Bashbot Documentation and Downloads are availible on www.github.com

-

Documentation

- -

Your really first bashbot in a nutshell

-

To install and run bashbot you need acess to a linux/unix/bsd command line. If you don’t know how to get accces to a linux/unix/bsd like command line you should stop reading here :-(

-

In addition you need a Telegram client and a mobile phone to register an account. If you don’t want to register for Telegram you should stop reading here ;-)

-

After you’re registered to Telegram send a message to [@botfather](https://telegram.me/botfather), create a new Telegram Bot token and write it down. You need the token to install the bot.

-

Now open a linux/unix/bsd terminal and check if bash is installed: which bash && echo "bash installed!". If you get an error message bash is not installed.

-

Create a new directory and change to it: mkdir tbb; cd tbb and download the latest ’*.tar.gz’ file from https://github.com/topkecleon/telegram-bot-bash/releases. This can be done with the commands:

-
wget -q https://github.com/$(wget -q https://github.com/topkecleon/telegram-bot-bash/releases/latest -O - | egrep '/.*/.*/.*tar.gz' -o)
-

Extract the ’*.tar.gz’ file and change to bashbot directory: tar -xzf *.tar.gz; cd telegram-bot-bash, install bashbot: ./bashbot.sh init and enter your bot token when asked. All other questions can be answered by hitting the <Return> key.

-

Thats all, now you can start your bot with ./bashbot.sh start and send him messages:

-
/start
-
-You are Botadmin
-*Available commands*:
-*• /start*: _Start bot and get this message_.
-*• /help*: _Get this message_.
-*• /info*: _Get shorter info message about this bot_....
-
-/info
-
-his is bashbot, the Telegram bot written entirely in bash.
-It features background tasks and interactive chats, and can serve as an interface for CLI programs.
-

For more Information on how to install, customize and use your new bot, read the Documentation

-
-

Security Considerations

-

Running a Telegram Bot means it is connected to the public and you never know whats send to your Bot.

-

Bash scripts in general are not designed to be bullet proof, so consider this Bot as a proof of concept. Bash programmers often struggle with ‘quoting hell’ and globbing, see Implications of wrong quoting

-

Whenever you are processing input from from untrusted sources (messages, files, network) you must be as carefull as possible, e.g. set IFS appropriate, disable globbing (set -f) and quote everthing. In addition delete unused scripts and examples from your Bot, e.g. scripts ‘notify’, ‘calc’, ‘question’, and disable all not used commands.

-

A powerful tool to improve your scripts is shellcheck. You can use it online or install shellcheck locally. Shellcheck is used extensive in bashbot development to enshure a high code quality, e.g. it’s not allowed to push changes without passing all shellcheck tests. In addition bashbot has a test suite to check if important functionality is working as expected.

-

Run your Bot as a restricted user

-

I recommend to run your bot as a user, with almost no access rights. All files your Bot have write access to are in danger to be overwritten/deleted if your bot is hacked. For the same reason ervery file your Bot can read is in danger to be disclosed. Restict your Bots access rigths to the absolute minimum.

-

Never run your Bot as root, this is the most dangerous you can do! Usually the user ‘nobody’ has almost no rights on Unix/Linux systems. See Expert use on how to run your Bot as an other user.

-

Secure your Bot installation

-

Your Bot configuration must no be readable from other users. Everyone who can read your Bots token can act as your Bot and has access to all chats your Bot is in!

-

Everyone with read access to your Bot files can extract your Bots data. Especially your Bot Token in token must be protected against other users. No one exept you must have write access to the Bot files. The Bot must be restricted to have write access to count and tmp-bot-bash only, all other files must be write protected.

-

To set access rights for your bashbot installation to a reasonable default run sudo ./bashbot.sh init after every update or change to your installation directory.

-

FAQ

-

Is this Bot insecure?

-

Bashbot is not more (in)secure as any other Bot written in any other language, we have done our best to make it as secure as possible. But YOU are responsible for the bot commands you wrote and you should know about the risks …

-

Why Bash and not the much better xyz?

-

Well, thats a damn good question … may be because I’m an Unix/Linux admin from stone age. Nevertheless there are more reasons from my side:

-
    -
  • bashbot will run everywhere where bash is availible, from ebedded linux to mainframe
  • -
  • easy to integrate with other shell script, e.g. for sending system message / health status
  • -
  • no need to install or learn a new programming language, library or framework
  • -
  • no database, not event driven, not OO …
  • -
-

Can I have the single bashbot.sh file back?

-

At the beginning bashbot was simply the file bashbot.sh you can copy everywhere and run the bot. Now we have ‘commands.sh’, ‘mycommands.sh’, ’modules/*.sh’ and much more.

-

Hey no Problem, if you are finished with your cool bot run dev/make-standalone.sh to create a stripped down Version of your bot containing only ‘bashbot.sh’ and ‘commands.sh’! For more information see Create a stripped down Version of your Bot

-

Can I send messages from CLI and scripts?

-

Of course, you can send messages from CLI and scripts, simply install bashbot as described here, send the messsage ‘/start’ to set yourself as botadmin and stop the bot with ./bashbot.sh kill.

-

Run the following commands in your bash shell or script while you are in the installation directory:

-
# prepare bash / script to send commands
-export BASHBOT_HOME="$(pwd)"
-source ./bashbot.sh source
-
-# send me a test message
-send_message "$(cat "$BOTADMIN")" "test"
-
-# send me output of a system command
-send_message "$(<"$BOTADMIN")" "$(df -h)"
-

For more information see Expert Use

-

Why do I get “EXPECTED value GOT EOF” on start?

-

May be your IP is blocked by telegram. You can test this by running curl or wget manually:

-
curl -m 10  https://api.telegram.org/bot
-#curl: (28) Connection timed out after 10001 milliseconds
-
-wget -t 1 -T 10 https://api.telegram.org/bot
-#Connecting to api.telegram.org (api.telegram.org)|46.38.243.234|:443... failed: Connection timed out.
-

This may happen if to many wrong requests are sent to api.telegram.org, e.g. using a wrong token or not existing API calls. If you have a fixed IP you can ask telegram service to unblock your ip or change your IP. If you are running a tor proxy on your server you may uncomment the BASHBOT_CURL_ARGS line in ‘mycommands.sh’

-

@Gnadelwartz

-

That’s it!

-

If you feel that there’s something missing or if you found a bug, feel free to submit a pull request!

-


VERSION
v0.90-dev2-19-g5779acc

- - diff --git a/DIST/telegram-bot-bash/modules/aliases.sh b/DIST/telegram-bot-bash/modules/aliases.sh deleted file mode 100644 index 544bd89..0000000 --- a/DIST/telegram-bot-bash/modules/aliases.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -# file: modules/alaises.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 -# -# source from commands.sh to use the aliases - -# easy handling of users: -_is_botadmin() { - user_is_botadmin "${USER[ID]}" -} -_is_admin() { - user_is_admin "${CHAT[ID]}" "${USER[ID]}" -} -_is_creator() { - user_is_creator "${CHAT[ID]}" "${USER[ID]}" -} -_is_allowed() { - user_is_allowed "${USER[ID]}" "${1}" "${CHAT[ID]}" -} -_leave() { - leave_chat "${CHAT[ID]}" -} -_kick_user() { - kick_chat_member "${CHAT[ID]}" "${1}" -} -_unban_user() { - unban_chat_member "${CHAT[ID]}" "${1}" -} -# easy sending of messages of messages -_message() { - send_normal_message "${CHAT[ID]}" "${1}" -} -_normal_message() { - send_normal_message "${CHAT[ID]}" "${1}" -} -_html_message() { - send_html_message "${CHAT[ID]}" "${1}" -} -_markdown_message() { - send_markdown_message "${CHAT[ID]}" "${1}" -} -# easy handling of keyboards -_inline_button() { - send_inline_button "${CHAT[ID]}" "" "${1}" "${2}" -} -_inline_keyboard() { - send_inline_keyboard "${CHAT[ID]}" "" "${1}" -} -_keyboard_numpad() { - send_keyboard "${CHAT[ID]}" "" '["1","2","3"],["4","5","6"],["7","8","9"],["-","0","."]' "yes" -} -_keyboard_yesno() { - send_keyboard "${CHAT[ID]}" "" '["yes","no"]' -} -_del_keyboard() { - remove_keyboard "${CHAT[ID]}" "" -} diff --git a/DIST/telegram-bot-bash/modules/answerInline.sh b/DIST/telegram-bot-bash/modules/answerInline.sh deleted file mode 100644 index 626bae1..0000000 --- a/DIST/telegram-bot-bash/modules/answerInline.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# file: modules/inline.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-13-gcb3f3e3 - -# source from commands.sh to use the inline functions - -INLINE_QUERY=$URL'/answerInlineQuery' - -answer_inline_query() { - answer_inline_multi "${1}" "$(shift; inline_query_compose "$RANDOM" "$@")" -} -answer_inline_multi() { - sendJson "" '"inline_query_id": '"${1}"', "results": ['"${2}"']' "${INLINE_QUERY}" -} - -# $1 unique ID for answer -# $2 type of answer -# remaining arguments are the "must have" arguments in the order as in telegram doc -# followed by the optional arguments: https://core.telegram.org/bots/api#inlinequeryresult -inline_query_compose(){ - local JSON="{}" - local ID="${1}" - local fours last - # title2Json title caption description markup inlinekeyboard - case "${2}" in - # user provided media - "article"|"message") # article ID title message (markup decription) - JSON='{"type":"article","id":"'$ID'","input_message_content": {"message_text":"'$4'"} '$(title2Json "$3" "" "$5" "$6")'}' - ;; - "photo") # photo ID photoURL (thumbURL title description caption) - [ "$4" = "" ] && tumb="$3" - JSON='{"type":"photo","id":"'$ID'","photo_url":"'$3'","thumb_url":"'$4${tumb}'"'$(title2Json "$5" "$7" "$6")'}' - ;; - "gif") # gif ID photoURL (thumbURL title caption) - [ "$4" = "" ] && tumb="$3" - JSON='{"type":"gif","id":"'$ID'","gif_url":"'$3'", "thumb_url":"'$4${tumb}'"'$(title2Json "$5" "$6")'}' - ;; - "mpeg4_gif") # mpeg4_gif ID mpegURL (thumbURL title caption) - [ "$4" != "" ] && tumb='","thumb_url":"'$4'"' - JSON='{"type":"mpeg4_gif","id":"'$ID'","mpeg4_url":"'$3'"'${tumb}$(title2Json "$5" "$6")'}' - ;; - "video") # video ID videoURL mime thumbURL title (caption) - JSON='{"type":"video","id":"'$ID'","video_url":"'$3'","mime_type":"'$4'","thumb_url":"'$5'"'$(title2Json "$6" "$7")'}' - ;; - "audio") # audio ID audioURL title (caption) - JSON='{"type":"audio","id":"'$ID'","audio_url":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - "voice") # voice ID voiceURL title (caption) - JSON='{"type":"voice","id":"'$ID'","voice_url":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - "document") # document ID title documentURL mimetype (caption description) - JSON='{"type":"document","id":"'$ID'","document_url":"'$4'","mime_type":"'$5'"'$(title2Json "$3" "$6" "$7")'}' - ;; - "location") # location ID lat long title - JSON='{"type":"location","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'"}' - ;; - "venue") # venue ID lat long title (adress forsquare) - [ "$6" = "" ] && addr="$5" - [ "$7" != "" ] && fours=',"foursquare_id":"'$7'"' - JSON='{"type":"venue","id":"'$ID'","latitude":"'$3'","longitude":"'$4'","title":"'$5'","address":"'$6${addr}'"'${fours}'}' - ;; - "contact") # contact ID phone first (last thumb) - [ "$5" != "" ] && last=',"last_name":"'$5'"' - [ "$6" != "" ] && tumb='","thumb_url":"'$6'"' - JSON='{"type":"contact","id":"'$ID'","phone_number":"'$3'","first_name":"'$4'"'${last}'"}' - ;; - # title2Json title caption description markup inlinekeyboard - # Cached media stored in Telegram server - "cached_photo") # photo ID file (title description caption) - JSON='{"type":"photo","id":"'$ID'","photo_file_id":"'$3'"'$(title2Json "$4" "$6" "$5")'}' - ;; - "cached_gif") # gif ID file (title caption) - JSON='{"type":"gif","id":"'$ID'","gif_file_id":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - "cached_mpeg4_gif") # mpeg ID file (title caption) - JSON='{"type":"mpeg4_gif","id":"'$ID'","mpeg4_file_id":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - "cached_sticker") # sticker ID file - JSON='{"type":"sticker","id":"'$ID'","sticker_file_id":"'$3'"}' - ;; - "cached_document") # document ID title file (description caption) - JSON='{"type":"document","id":"'$ID'","document_file_id":"'$4'"'$(title2Json "$3" "$6" "$5")'}' - ;; - "cached_video") # video ID file title (description caption) - JSON='{"type":"video","id":"'$ID'","video_file_id":"'$3'"'$(title2Json "$4" "$6" "$5")'}' - ;; - "cached_voice") # voice ID file title (caption) - JSON='{"type":"voice","id":"'$ID'","voice_file_id":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - "cached_audio") # audio ID file title (caption) - JSON='{"type":"audio","id":"'$ID'","audio_file_id":"'$3'"'$(title2Json "$4" "$5")'}' - ;; - esac - - printf '%s\n' "${JSON}" -} - diff --git a/DIST/telegram-bot-bash/modules/background.sh b/DIST/telegram-bot-bash/modules/background.sh deleted file mode 100644 index cdd5347..0000000 --- a/DIST/telegram-bot-bash/modules/background.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/bash -# file: modules/background.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-11-g59aa9fc - -# source from commands.sh if you want ro use interactive or background jobs - -###### -# interactive and background functions - -# old syntax as aliases -background() { - start_back "${CHAT[ID]}" "$1" "$2" -} -startproc() { - start_proc "${CHAT[ID]}" "$1" "$2" -} -checkback() { - check_back "${CHAT[ID]}" "$1" -} -checkproc() { - check_proc "${CHAT[ID]}" "$1" -} -killback() { - kill_back "${CHAT[ID]}" "$1" -} -killproc() { - kill_proc "${CHAT[ID]}" "$1" -} - -# inline and backgound functions -# $1 chatid -# $2 program -# $3 jobname -start_back() { - local fifo; fifo="${DATADIR:-.}/$(procname "$1")" - printf '%s\n' "$1:$3:$2" >"${fifo}$3-back.cmd" - start_proc "$1" "$2" "back-$3-" -} - - -# $1 chatid -# $2 program -# $3 prefix -start_proc() { - [ "$2" = "" ] && return - [ -x "${2%% *}" ] || return 1 - local fifo; fifo="${DATADIR:-.}/$(procname "$1" "$3")" - kill_proc "$1" "$3" - mkfifo "${fifo}" - nohup bash -c "{ tail -f < \"${fifo}\" | $2 \"\" \"\" \"$fifo\" | \"${SCRIPT}\" outproc \"${1}\" \"${fifo}\" - rm \"${fifo}\"; [ -s \"${fifo}.log\" ] || rm -f \"${fifo}.log\"; }" &>>"${fifo}.log" & -} - - -# $1 chatid -# $2 jobname -check_back() { - check_proc "$1" "back-$2-" -} - -# $1 chatid -# $2 prefix -check_proc() { - [ "$(proclist "$(procname "$1" "$2")")" != "" ] - # shellcheck disable=SC2034 - res=$?; return $? -} - -# $1 chatid -# $2 jobname -kill_back() { - kill_proc "$1" "back-$2-" - rm -f "${DATADIR:-.}/$(procname "$1")$2-back.cmd" -} - - -# $1 chatid -# $2 prefix -kill_proc() { - local fifo prid - fifo="$(procname "$1" "$2")" - prid="$(proclist "${fifo}")" - fifo="${DATADIR:-.}/${fifo}" - # shellcheck disable=SC2086 - [ "${prid}" != "" ] && kill ${prid} - [ -s "${fifo}.log" ] || rm -f "${fifo}.log" - [ -p "${fifo}" ] && rm -f "${fifo}"; -} - -# $1 chat -# $2 message -send_interactive() { - local fifo; fifo="${DATADIR:-.}/$(procname "$1")" - [ -p "${fifo}" ] && printf '%s\n' "$2" >"${fifo}" & # not blocking! -} - -# old style but may not work because of local checks -inproc() { - send_interactive "${CHAT[ID]}" "${MESSAGE}" -} - -# start stopp all jobs -# $1 command -# killb* -# suspendb* -# resumeb* -job_control() { - local content proc CHAT job fifo killall="" - for FILE in "${DATADIR:-.}/"*-back.cmd; do - [ "${FILE}" = "${DATADIR:-.}/*-back.cmd" ] && echo -e "${RED}No background processes.${NC}" && break - content="$(< "${FILE}")" - CHAT="${content%%:*}" - job="${content#*:}" - proc="${job#*:}" - job="back-${job%:*}-" - fifo="$(procname "${CHAT}" "${job}")" - case "$1" in - "resumeb"*|"backgr"*) - echo "Restart Job: ${proc} ${fifo}" - start_proc "${CHAT}" "${proc}" "${job}" - ;; - "suspendb"*) - echo "Suspend Job: ${proc} ${fifo}" - kill_proc "${CHAT}" "${job}" - killall="y" - ;; - "killb"*) - echo "Kill Job: ${proc} ${fifo}" - kill_proc "${CHAT}" "${job}" - rm -f "${FILE}" # remove job - killall="y" - ;; - esac - done - # kill all requestet. kill ALL background jobs, even not listed in data-bot-bash - [ "${killall}" = "y" ] && killallproc "back-" -} diff --git a/DIST/telegram-bot-bash/modules/chatMember.sh b/DIST/telegram-bot-bash/modules/chatMember.sh deleted file mode 100644 index 73a5f12..0000000 --- a/DIST/telegram-bot-bash/modules/chatMember.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash -# file: modules/chatMember.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-13-gcb3f3e3 - -# source from commands.sh to use the member functions - -LEAVE_URL=$URL'/leaveChat' -KICK_URL=$URL'/kickChatMember' -UNBAN_URL=$URL'/unbanChatMember' -GETMEMBER_URL=$URL'/getChatMember' - -# usage: status="$(get_chat_member_status "chat" "user")" -get_chat_member_status() { - sendJson "$1" 'user_id: '"$2"'' "$GETMEMBER_URL" - # shellcheck disable=SC2154 - JsonGetString '"result","status"' <<< "$res" -} - -kick_chat_member() { - sendJson "$1" 'user_id: '"$2"'' "$KICK_URL" -} - -unban_chat_member() { - sendJson "$1" 'user_id: '"$2"'' "$UNBAN_URL" -} - -leave_chat() { - sendJson "$1" "" "$LEAVE_URL" -} - -user_is_creator() { - if [ "${1:--}" = "${2:-+}" ] || [ "$(get_chat_member_status "$1" "$2")" = "creator" ]; then return 0; fi - return 1 -} - -user_is_admin() { - local me; me="$(get_chat_member_status "$1" "$2")" - if [ "${me}" = "creator" ] || [ "${me}" = "administrator" ]; then return 0; fi - return 1 -} - -user_is_botadmin() { - local admin; admin="$(head -n 1 "${BOTADMIN}")" - [ "${admin}" = "${1}" ] && return 0 - [[ "${admin}" = "@*" ]] && [[ "${admin}" = "${2}" ]] && return 0 - if [ "${admin}" = "?" ]; then printf '%s\n' "${1:-?}" >"${BOTADMIN}"; return 0; fi - return 1 -} - -user_is_allowed() { - local acl="$1" - [ "$1" = "" ] && return 1 - grep -F -xq "${acl}:*:*" <"${BOTACL}" && return 0 - [ "$2" != "" ] && acl="${acl}:$2" - grep -F -xq "${acl}:*" <"${BOTACL}" && return 0 - [ "$3" != "" ] && acl="${acl}:$3" - grep -F -xq "${acl}" <"${BOTACL}" -} diff --git a/DIST/telegram-bot-bash/modules/jsonDB.sh b/DIST/telegram-bot-bash/modules/jsonDB.sh deleted file mode 100644 index 8cffbca..0000000 --- a/DIST/telegram-bot-bash/modules/jsonDB.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# file: modules/jsshDB.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-9-gbbbc8ae -# -# source from commands.sh to use jsonDB functions -# -# jsonDB rovides simple functions to read and store bash Arrays -# from to file in JSON.sh output format - -# read content of a file in JSON.sh format into given ARRAY -# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront -# $2 filename, must be relative to BASHBOT_ETC, and not contain '..' -jssh_readDB() { - local DB="${BASHBOT_ETC:-.}/$2.jssh" - [ "$2" = "" ] && return 1 - [[ "$2" = *'..'* ]] && return 1 - [ ! -f "${DB}" ] && return 1 - Json2Array "$1" <"${DB}" -} - -# write ARRAY content to a file in JSON.sh format -# $1 ARRAY name, must be delared with "declare -A ARRAY" upfront -# $2 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' -jssh_writeDB() { - local DB="${BASHBOT_ETC:-.}/$2.jssh" - [ "$2" = "" ] && return 1 - [[ "$2" = *'..'* ]] && return 1 - [ ! -f "${DB}" ] && return 1 - Array2Json "$1" >"${DB}" -} - -# $1 filename (must exist!), must be relative to BASHBOT_ETC, and not contain '..' -jssh_newDB() { - local DB="${BASHBOT_ETC:-.}/$1.jssh" - [ "$1" = "" ] && return 1 - [[ "$2" = *'..'* ]] && return 1 - [ -f "${DB}" ] && return 1 # already exist, do not zero out - printf '\n' >"${DB}" -} diff --git a/DIST/telegram-bot-bash/modules/sendMessage.sh b/DIST/telegram-bot-bash/modules/sendMessage.sh deleted file mode 100644 index fef5d4b..0000000 --- a/DIST/telegram-bot-bash/modules/sendMessage.sh +++ /dev/null @@ -1,237 +0,0 @@ -#!/bin/bash -# file: modules/message.sh -# do not edit, this file will be overwritten on update - -# This file is public domain in the USA and all free countries. -# Elsewhere, consider it to be WTFPLv2. (wtfpl.net/txt/copying) -# -#### $$VERSION$$ v0.90-dev2-13-gcb3f3e3 - -# source from commands.sh to use the sendMessage functions - -MSG_URL=$URL'/sendMessage' -PHO_URL=$URL'/sendPhoto' -AUDIO_URL=$URL'/sendAudio' -DOCUMENT_URL=$URL'/sendDocument' -STICKER_URL=$URL'/sendSticker' -VIDEO_URL=$URL'/sendVideo' -VOICE_URL=$URL'/sendVoice' -LOCATION_URL=$URL'/sendLocation' -VENUE_URL=$URL'/sendVenue' -ACTION_URL=$URL'/sendChatAction' -FORWARD_URL=$URL'/forwardMessage' - -send_normal_message() { - local text="${2}" - until [ -z "${text}" ]; do - sendJson "${1}" '"text":"'"${text:0:4096}"'"' "${MSG_URL}" - text="${text:4096}" - done -} - -send_markdown_message() { - local text="${2}" - until [ -z "${text}" ]; do - sendJson "${1}" '"text":"'"${text:0:4096}"'","parse_mode":"markdown"' "${MSG_URL}" - text="${text:4096}" - done -} - -send_html_message() { - local text="${2}" - until [ -z "${text}" ]; do - sendJson "${1}" '"text":"'"${text:0:4096}"'","parse_mode":"html"' "${MSG_URL}" - text="${text:4096}" - done -} - -old_send_keyboard() { - local text='"text":"'"${2}"'"' - shift 2 - local keyboard="init" - OLDIFS="$IFS" - IFS="\"" - for f in "$@" ;do [ "$f" != " " ] && keyboard="$keyboard, [\"$f\"]";done - IFS="$OLDIFS" - keyboard="${keyboard/init, /}" - sendJson "${1}" "${text}"', "reply_markup": {"keyboard": [ '"${keyboard}"' ],"one_time_keyboard": true}' "$MSG_URL" -} - -ISEMPTY="ThisTextIsEmptyAndWillBeDeleted" -sendEmpty() { - sendJson "${@}" - [[ "${2}" = *"${ISEMPTY}"* ]] && delete_message "${1}" "${BOTSENT[ID]}" -} -send_keyboard() { - if [[ "$3" != *'['* ]]; then old_send_keyboard "${@}"; return; fi - local text='"text":"'"${2}"'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"' - local one_time=', "one_time_keyboard":true' && [ "$4" != "" ] && one_time="" - sendEmpty "${1}" "${text}"', "reply_markup": {"keyboard": [ '"${3}"' ] '"${one_time}"'}' "$MSG_URL" - # '"text":"$2", "reply_markup": {"keyboard": [ ${3} ], "one_time_keyboard": true}' -} - -remove_keyboard() { - local text='"text":"'"${2}"'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"' - sendEmpty "${1}" "${text}"', "reply_markup": {"remove_keyboard":true}' "$MSG_URL" - #JSON='"text":"$2", "reply_markup": {"remove_keyboard":true}' -} -send_inline_keyboard() { - local text='"text":"'"${2}"'"'; [ "${2}" = "" ] && text='"text":"'"${ISEMPTY}"'"' - sendEmpty "${1}" "${text}"', "reply_markup": {"inline_keyboard": [ '"${3}"' ]}' "$MSG_URL" - # JSON='"text":"$2", "reply_markup": {"inline_keyboard": [ $3->[{"text":"text", "url":"url"}]<- ]}' -} -send_button() { - send_inline_keyboard "${1}" "${2}" '[ {"text":"'"${3}"'", "url":"'"${4}"'"}]' -} - - -UPLOADDIR="${BASHBOT_UPLOAD:-${DATADIR}/upload}" - -# for now this can only send local files with curl! -# extend to allow send files by URL or telegram ID -send_file() { - [ "$2" = "" ] && return - [[ "$2" = "http"* ]] && return # currently we do not support URL - upload_file "${@}" -} - -upload_file(){ - local CUR_URL WHAT STATUS file="$2" - # file access checks ... - [[ "$file" = *'..'* ]] && return # no directory traversal - [[ "$file" = '.'* ]] && return # no hidden or relative files - if [[ "$file" = '/'* ]] ; then - [[ ! "$file" =~ $FILE_REGEX ]] && return # absulute must match REGEX - else - file="${UPLOADDIR:-NOUPLOADDIR}/${file}" # othiers must be in UPLOADDIR - fi - [ ! -r "$file" ] && return # and file must exits of course - - local ext="${file##*.}" - case $ext in - mp3|flac) - CUR_URL="$AUDIO_URL" - WHAT="audio" - STATUS="upload_audio" - ;; - png|jpg|jpeg|gif) - CUR_URL="$PHO_URL" - WHAT="photo" - STATUS="upload_photo" - ;; - webp) - CUR_URL="$STICKER_URL" - WHAT="sticker" - STATUS="upload_photo" - ;; - mp4) - CUR_URL="$VIDEO_URL" - WHAT="video" - STATUS="upload_video" - ;; - - ogg) - CUR_URL="$VOICE_URL" - WHAT="voice" - STATUS="upload_audio" - ;; - *) - CUR_URL="$DOCUMENT_URL" - WHAT="document" - STATUS="upload_document" - ;; - esac - send_action "${1}" "$STATUS" - sendUpload "$1" "${WHAT}" "${file}" "${CUR_URL}" "$3" -} - -# typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location -send_action() { - [ "$2" = "" ] && return - sendJson "${1}" '"action": "'"${2}"'"' "$ACTION_URL" -} - -send_location() { - [ "$3" = "" ] && return - sendJson "${1}" '"latitude": '"${2}"', "longitude": '"${3}"'' "$LOCATION_URL" -} - -send_venue() { - local add="" - [ "$5" = "" ] && return - [ "$6" != "" ] && add=', "foursquare_id": '"$6"'' - sendJson "${1}" '"latitude": '"${2}"', "longitude": '"${3}"', "address": "'"${5}"'", "title": "'"${4}"'"'"${add}" "$VENUE_URL" -} - - -forward_message() { - [ "$3" = "" ] && return - sendJson "${1}" '"from_chat_id": '"${2}"', "message_id": '"${3}"'' "$FORWARD_URL" -} -forward() { # backward compatibility - forward_message "$@" || return -} - -send_message() { - [ "$2" = "" ] && return - local text keyboard btext burl no_keyboard file lat long title address sent - text="$(sed <<< "${2}" 's/ mykeyboardend.*//;s/ *my[kfltab][a-z]\{2,13\}startshere.*//')$(sed <<< "${2}" -n '/mytextstartshere/ s/.*mytextstartshere//p')" - text="$(sed <<< "${text}" 's/ *mynewlinestartshere */\r\n/g')" - [ "$3" != "safe" ] && { - no_keyboard="$(sed <<< "${2}" '/mykeyboardendshere/!d;s/.*mykeyboardendshere.*/mykeyboardendshere/')" - keyboard="$(sed <<< "${2}" '/mykeyboardstartshere /!d;s/.*mykeyboardstartshere *//;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - btext="$(sed <<< "${2}" '/mybtextstartshere /!d;s/.*mybtextstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - burl="$(sed <<< "${2}" '/myburlstartshere /!d;s/.*myburlstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//g;s/ *mykeyboardendshere.*//g')" - file="$(sed <<< "${2}" '/myfilelocationstartshere /!d;s/.*myfilelocationstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - lat="$(sed <<< "${2}" '/mylatstartshere /!d;s/.*mylatstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - long="$(sed <<< "${2}" '/mylongstartshere /!d;s/.*mylongstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - title="$(sed <<< "${2}" '/mytitlestartshere /!d;s/.*mytitlestartshere //;s/ *my[kfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - address="$(sed <<< "${2}" '/myaddressstartshere /!d;s/.*myaddressstartshere //;s/ *my[nkfltab][a-z]\{2,13\}startshere.*//;s/ *mykeyboardendshere.*//')" - } - if [ "$no_keyboard" != "" ]; then - remove_keyboard "$1" "$text" - sent=y - fi - if [ "$keyboard" != "" ]; then - if [[ "$keyboard" != *"["* ]]; then # pre 0.60 style - keyboard="[ ${keyboard//\" \"/\" \] , \[ \"} ]" - fi - send_keyboard "$1" "$text" "$keyboard" - sent=y - fi - if [ "$btext" != "" ] && [ "$burl" != "" ]; then - send_button "$1" "$text" "$btext" "$burl" - sent=y - fi - if [ "$file" != "" ]; then - send_file "$1" "$file" "$text" - sent=y - fi - if [ "$lat" != "" ] && [ "$long" != "" ]; then - if [ "$address" != "" ] && [ "$title" != "" ]; then - send_venue "$1" "$lat" "$long" "$title" "$address" - else - send_location "$1" "$lat" "$long" - fi - sent=y - fi - if [ "$sent" != "y" ];then - send_text "$1" "$text" - fi - -} - -send_text() { - case "$2" in - html_parse_mode*) - send_html_message "$1" "${2//html_parse_mode}" - ;; - markdown_parse_mode*) - send_markdown_message "$1" "${2//markdown_parse_mode}" - ;; - *) - send_normal_message "$1" "$2" - ;; - esac -} - diff --git a/DIST/telegram-bot-bash/mycommands.sh.dist b/DIST/telegram-bot-bash/mycommands.sh.dist deleted file mode 100644 index bab96e7..0000000 --- a/DIST/telegram-bot-bash/mycommands.sh.dist +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash -# files: mycommands.sh.dist -# copy to mycommands.sh and add all your commands and functions here ... -# -#### $$VERSION$$ v0.90-dev2-0-gec85636 -# - -# uncomment the following lines to overwrite info and help messages -# bashbot_info='This is bashbot, the Telegram bot written entirely in bash. -#' -# bashbot_help='*Available commands*: -#' -res="" - -# Set INLINE to 1 in order to receive inline queries. -# To enable this option in your bot, send the /setinline command to @BotFather. -export INLINE="0" -# Set to .* to allow sending files from all locations -export FILE_REGEX='/home/user/allowed/.*' -# example: run bashbot over TOR -# export BASHBOT_CURL_ARGS="--socks5-hostname 127.0.0.1:9050" - -if [ "$1" != "source" ];then - # your additional bahsbot commands - # NOTE: command can have @botname attached, you must add * in case tests... - mycommands() { - - case "${MESSAGE}" in - ################## - # example commands, replace thm by your own - '/echo'*) # example echo command - send_normal_message "${CHAT[ID]}" "$MESSAGE" - ;; - '/question'*) # start interactive questions - checkproc - if [ "$res" -gt 0 ] ; then - startproc "examples/question.sh" || _message "Can't start question." - else - send_normal_message "${CHAT[ID]}" "$MESSAGE already running ..." - fi - ;; - - '/run_notify'*) # start notify background job - myback="notify"; checkback "$myback" - if [ "$res" -gt 0 ] ; then - background "examples/notify.sh 60" "$myback" || _message "Can't start notify." - else - send_normal_message "${CHAT[ID]}" "Background command $myback already running ..." - fi - ;; - '/stop_notify'*) # kill notify background job - myback="notify"; checkback "$myback" - if [ "$res" -eq 0 ] ; then - killback "$myback" - send_normal_message "${CHAT[ID]}" "Background command $myback canceled." - else - send_normal_message "${CHAT[ID]}" "No background command $myback is currently running.." - fi - ;; - - ########## - # command overwrite examples - 'info'*) # output date in front of regular info - send_normal_message "${CHAT[ID]}" "$(date)" - return 0 - ;; - '/kickme'*) # this will replace the /kickme command - send_markdown_mesage "${CHAT[ID]}" "*This bot will not kick you!*" - return 1 - ;; - esac - } - - myinlines() { - ####################### - # Inline query examples, do not use them in production (exept image search ;-) - # shellcheck disable=SC2128 - iQUERY="${iQUERY,,}" # all lowercase - case "${iQUERY}" in - "image "*) # search images with yahoo - local search="${iQUERY#* }" - answer_inline_multi "${iQUERY[ID]}" "$(my_image_search "${search}")" - ;; - - "0"*) # a single message with title - answer_inline_query "${iQUERY[ID]}" "message" "Title of the result" "Content of the message to be sent" - ;; - "1"*) # a single photo - answer_inline_query "${iQUERY[ID]}" "photo" "https://avatars.githubusercontent.com/u/13046303" "https://avatars.githubusercontent.com/u/13046303" - ;; - "2"*) # two photos - answer_inline_multi "${iQUERY[ID]}" " - $(inline_query_compose "$RANDOM" "photo" "https://avatars.githubusercontent.com/u/13046303"), - $(inline_query_compose "$RANDOM" "photo" "https://avatars.githubusercontent.com/u/4593242") - " - ;; - "3"*) # three photos - answer_inline_multi "${iQUERY[ID]}" " - $(inline_query_compose "$RANDOM" "photo" "https://avatars.githubusercontent.com/u/13046303"), - $(inline_query_compose "$RANDOM" "photo" "https://avatars.githubusercontent.com/u/4593242") - $(inline_query_compose "$RANDOM" "photo" "https://avatars.githubusercontent.com/u/102707") - " - ;; - - "4") # four photo from array - local sep="" - local avatar=("https://avatars.githubusercontent.com/u/13046303" "https://avatars.githubusercontent.com/u/4593242" "https://avatars.githubusercontent.com/u/102707" "https://avatars.githubusercontent.com/u/6460407") - answer_inline_multi "${iQUERY[ID]}" " - $(for photo in ${avatar[*]} ; do - echo "${sep}"; inline_query_compose "$RANDOM" "photo" "${photo}" "${photo}"; sep="," - done) - " - ;; - - "sticker") # example chaecd telegram sticker - answer_inline_query "${iQUERY[ID]}" "cached_sticker" "BQADBAAD_QEAAiSFLwABWSYyiuj-g4AC" - ;; - "gif") # exmaple chaehed gif - answer_inline_query "${iQUERY[ID]}" "cached_gif" "BQADBAADIwYAAmwsDAABlIia56QGP0YC" - ;; - esac -set +x - } - - # place your processing functions here - - # $1 search parameter - my_image_search(){ - local image result sep="" count="1" - result="$(wget --user-agent 'Mozilla/5.0' -qO - "https://images.search.yahoo.com/search/images?p=$1" | sed 's/ span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
  • Inline Queries
  • Background and Interactive Jobs
  • -
  • Deveoper Notess +
  • Deveoper Notes
    • Debug bashbot
    • Modules, addons, events
    • diff --git a/README.txt b/README.txt index e1343e8..5476176 100644 --- a/README.txt +++ b/README.txt @@ -64,7 +64,7 @@ all](https://core.telegram.org/bots#3-how-do-i-create-a-bot) * User Access Control * Inline Queries * Background and Interactive Jobs -* [Deveoper Notess](doc/7_develop.md) +* [Deveoper Notes](doc/7_develop.md) * Debug bashbot * Modules, addons, events * Setup your environment diff --git a/doc/7_develop.md b/doc/7_develop.md index bdd2ad1..a75d429 100644 --- a/doc/7_develop.md +++ b/doc/7_develop.md @@ -272,5 +272,5 @@ fi #### [Prev Function Reference](6_reference.md) #### [Next Expert Use](8_custom.md) -#### $$VERSION$$ v0.90-dev2-19-g5779acc +#### $$VERSION$$ v0.90-dev2-20-g60b1a59
  • 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 - exit 1 -} - -BRIEF=0 -LEAFONLY=0 -PRUNE=0 -NO_HEAD=0 -NORMALIZE_SOLIDUS=0 - -usage() { - echo - echo "Usage: JSON.sh [-b] [-l] [-p] [-s] [-h]" - echo - echo "-p - Prune empty. Exclude fields with empty values." - echo "-l - Leaf only. Only show leaf nodes, which stops data duplication." - echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options." - echo "-n - No-head. Do not show nodes that have no path (lines that start with [])." - echo "-s - Remove escaping of the solidus symbol (straight slash)." - echo "-h - This help text." - echo -} - -parse_options() { - set -- "$@" - local ARGN=$# - while [ "$ARGN" -ne 0 ] - do - case $1 in - -h) usage - exit 0 - ;; - -b) BRIEF=1 - LEAFONLY=1 - PRUNE=1 - ;; - -l) LEAFONLY=1 - ;; - -p) PRUNE=1 - ;; - -n) NO_HEAD=1 - ;; - -s) NORMALIZE_SOLIDUS=1 - ;; - ?*) echo "ERROR: Unknown option." - usage - exit 0 - ;; - esac - shift 1 - ARGN=$((ARGN-1)) - done -} - -awk_egrep () { - local pattern_string=$1 - - gawk '{ - while ($0) { - start=match($0, pattern); - token=substr($0, start, RLENGTH); - print token; - $0=substr($0, start+RLENGTH); - } - }' pattern="$pattern_string" -} - -tokenize () { - local GREP - local ESCAPE - local CHAR - - if echo "test string" | egrep -ao --color=never "test" >/dev/null 2>&1 - then - GREP='egrep -ao --color=never' - else - GREP='egrep -ao' - fi - - if echo "test string" | egrep -o "test" >/dev/null 2>&1 - then - ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' - CHAR='[^[:cntrl:]"\\]' - else - GREP=awk_egrep - ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' - CHAR='[^[:cntrl:]"\\\\]' - fi - - local STRING="\"$CHAR*($ESCAPE$CHAR*)*\"" - local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?' - local KEYWORD='null|false|true' - local SPACE='[[:space:]]+' - - # Force zsh to expand $A into multiple words - local is_wordsplit_disabled=$(unsetopt 2>/dev/null | grep -c '^shwordsplit$') - if [ $is_wordsplit_disabled != 0 ]; then setopt shwordsplit; fi - $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$" - if [ $is_wordsplit_disabled != 0 ]; then unsetopt shwordsplit; fi -} - -parse_array () { - local index=0 - local ary='' - read -r token - case "$token" in - ']') ;; - *) - while : - do - parse_value "$1" "$index" - index=$((index+1)) - ary="$ary""$value" - read -r token - case "$token" in - ']') break ;; - ',') ary="$ary," ;; - *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;; - esac - read -r token - done - ;; - esac - [ "$BRIEF" -eq 0 ] && value=$(printf '[%s]' "$ary") || value= - : -} - -parse_object () { - local key - local obj='' - read -r token - case "$token" in - '}') ;; - *) - while : - do - case "$token" in - '"'*'"') key=$token ;; - *) throw "EXPECTED string GOT ${token:-EOF}" ;; - esac - read -r token - case "$token" in - ':') ;; - *) throw "EXPECTED : GOT ${token:-EOF}" ;; - esac - read -r token - parse_value "$1" "$key" - obj="$obj$key:$value" - read -r token - case "$token" in - '}') break ;; - ',') obj="$obj," ;; - *) throw "EXPECTED , or } GOT ${token:-EOF}" ;; - esac - read -r token - done - ;; - esac - [ "$BRIEF" -eq 0 ] && value=$(printf '{%s}' "$obj") || value= - : -} - -parse_value () { - local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0 - case "$token" in - '{') parse_object "$jpath" ;; - '[') parse_array "$jpath" ;; - # At this point, the only valid single-character tokens are digits. - ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;; - *) value=$token - # if asked, replace solidus ("\/") in json strings with normalized value: "/" - [ "$NORMALIZE_SOLIDUS" -eq 1 ] && value=$(echo "$value" | sed 's#\\/#/#g') - isleaf=1 - [ "$value" = '""' ] && isempty=1 - ;; - esac - [ "$value" = '' ] && return - [ "$NO_HEAD" -eq 1 ] && [ -z "$jpath" ] && return - - [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1 - [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1 - [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1 - [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \ - [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1 - [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value" - : -} - -parse () { - read -r token - parse_value - read -r token - case "$token" in - '') ;; - *) throw "EXPECTED EOF GOT $token" ;; - esac -} - -if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]); -then - parse_options "$@" - tokenize | parse -fi - -# vi: expandtab sw=2 ts=2 diff --git a/DIST/telegram-bot-bash/LICENSE b/DIST/telegram-bot-bash/LICENSE deleted file mode 100644 index 76d3373..0000000 --- a/DIST/telegram-bot-bash/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -This project is released to the public domain where applicable. - -Otherwise, it is released under the terms of the WTFPLv2: - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - Version 2, December 2004 - - Copyright (C) 2004 Sam Hocevar - - Everyone is permitted to copy and distribute verbatim or modified - copies of this license document, and changing it is allowed as long - as the name is changed. - - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. You just DO WHAT THE FUCK YOU WANT TO. - diff --git a/DIST/telegram-bot-bash/README.html b/DIST/telegram-bot-bash/README.html deleted file mode 100644 index 103b9b2..0000000 --- a/DIST/telegram-bot-bash/README.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - Bashbot README - - - - -