ΠΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΡΠ°ΡΡΠΎ ΠΏΡΠΎΠΈΡΡ
ΠΎΠ΄ΡΡ ΡΠΈΡΡΠ°ΡΠΈΠΈ, ΠΊΠΎΠ³Π΄Π° Π΄ΠΎΡΡΡΠΏ ΠΊ ΡΠ΅ΡΠ²Π΅ΡΡ Π½ΡΠΆΠ΅Π½ Π·Π΄Π΅ΡΡ ΠΈ ΡΠ΅ΠΉΡΠ°Ρ. ΠΠ΄Π½Π°ΠΊΠΎ, Π½Π΅ Π²ΡΠ΅Π³Π΄Π° ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎ SSH ΡΠ²Π»ΡΠ΅ΡΡΡ Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΡΠ΄ΠΎΠ±Π½ΡΠΌ ΡΠΏΠΎΡΠΎΠ±ΠΎΠΌ, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ ΠΏΠΎΠ΄ ΡΡΠΊΠΎΠΉ ΠΌΠΎΠΆΠ΅Ρ Π½Π΅ ΠΎΠΊΠ°Π·Π°ΡΡΡΡ SSH ΠΊΠ»ΠΈΠ΅Π½ΡΠ°, Π°Π΄ΡΠ΅ΡΠ° ΡΠ΅ΡΠ²Π΅ΡΠ° ΠΈΠ»ΠΈ ΡΠ²ΡΠ·ΠΊΠΈ Β«ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ/ΠΏΠ°ΡΠΎΠ»ΡΒ». ΠΠΎΠ½Π΅ΡΠ½ΠΎ, Π΅ΡΡΡ
ΠΠΎΡΡΠΎΠΌΡ Ρ ΡΠ΅ΡΠΈΠ» ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ ΠΏΡΠΎΡΡΠΎΠ΅, Π½ΠΎ Π»ΡΠ±ΠΎΠΏΡΡΠ½ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅. Π ΠΈΠΌΠ΅Π½Π½ΠΎ β Π½Π°ΠΏΠΈΡΠ°ΡΡ Telegram-Π±ΠΎΡΠ°, ΠΊΠΎΡΠΎΡΡΠΉ, Π·Π°ΠΏΡΡΠΊΠ°ΡΡΡ Π½Π° ΡΠ°ΠΌΠΎΠΌ ΡΠ΅ΡΠ²Π΅ΡΠ΅, Π±ΡΠ΄Π΅Ρ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ ΠΏΡΠΈΡΡΠ»Π°Π΅ΠΌΡΠ΅ Π΅ΠΌΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°ΡΡ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ. ΠΠ·ΡΡΠΈΠ²
ΠΠ°Π½Π½ΡΠΉ ΠΏΡΠΎΠ΅ΠΊΡ Ρ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²ΡΠ²Π°Π» Π½Π° Ubuntu 16.04, Π½ΠΎ Π΄Π»Ρ Π±Π΅ΡΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΠΎΠ³ΠΎ Π·Π°ΠΏΡΡΠΊΠ° Π½Π° Π΄ΡΡΠ³ΠΈΡ Π΄ΠΈΡΡΡΠΈΠ±ΡΡΠΈΠ²Π°Ρ Ρ ΠΏΠΎΡΡΠ°ΡΠ°Π»ΡΡ ΡΠ΄Π΅Π»Π°ΡΡ Π²ΡΡ Π² ΠΎΠ±ΡΠ΅ΠΌ Π²ΠΈΠ΄Π΅.
Π Π΅Π³ΠΈΡΡΡΠ°ΡΠΈΡ Π±ΠΎΡΠ°
Π Π΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΠΌ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π±ΠΎΡΠ° Ρ @BotFather. ΠΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌ Π΅ΠΌΡ /newbot
ΠΈ Π΄Π°Π»Π΅Π΅ ΠΏΠΎ ΡΠ΅ΠΊΡΡΡ. ΠΠ°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΡΡΡΡ ΡΠΎΠΊΠ΅Π½ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π±ΠΎΡΠ° ΠΈ Π²Π°Ρ id (ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, Ρ
ΠΠΎΠ΄Π³ΠΎΡΠΎΠ²ΠΊΠ° ΠΏΠΈΡΠΎΠ½Π°
ΠΠ»Ρ Π·Π°ΠΏΡΡΠΊΠ° Π±ΠΎΡΠ° Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ telebot
(pip install pytelegrambotapi
). Π‘ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ subprocess
Π±ΡΠ΄Π΅ΠΌ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π½Π° ΡΠ΅ΡΠ²Π΅ΡΠ΅.
ΠΠ°ΠΏΡΡΠΊ Π±ΠΎΡΠ°
ΠΠ° ΡΠ΅ΡΠ²Π΅ΡΠ΅ ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΡΠ°ΠΉΠ» bot.py:
nano bot.py
Π Π²ΡΡΠ°Π²Π»ΡΠ΅ΠΌ Π² Π½Π΅Π³ΠΎ ΠΊΠΎΠ΄:
from subprocess import check_output
import telebot
import time
bot = telebot.TeleBot("XXXXXXXXX:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")#ΡΠΎΠΊΠ΅Π½ Π±ΠΎΡΠ°
user_id = 0 #id Π²Π°ΡΠ΅Π³ΠΎ Π°ΠΊΠΊΠ°ΡΠ½ΡΠ°
@bot.message_handler(content_types=["text"])
def main(message):
if (user_id == message.chat.id): #ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, ΡΡΠΎ ΠΏΠΈΡΠ΅Ρ ΠΈΠΌΠ΅Π½Π½ΠΎ Π²Π»Π°Π΄Π΅Π»Π΅Ρ
comand = message.text #ΡΠ΅ΠΊΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ
try: #Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠ°Ρ - check_output Π²ΡΠ΄Π°ΡΡ exception
bot.send_message(message.chat.id, check_output(comand, shell = True))
except:
bot.send_message(message.chat.id, "Invalid input") #Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅ΠΊΠΎΡΡΠ΅ΠΊΡΠ½Π°
if __name__ == '__main__':
while True:
try:#Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ try Π΄Π»Ρ Π±Π΅ΡΠΏΠ΅ΡΠ΅Π±ΠΎΠΉΠ½ΠΎΠΉ ΡΠ°Π±ΠΎΡΡ
bot.polling(none_stop=True)#Π·Π°ΠΏΡΡΠΊ Π±ΠΎΡΠ°
except:
time.sleep(10)#Π² ΡΠ»ΡΡΠ°Π΅ ΠΏΠ°Π΄Π΅Π½ΠΈΡ
ΠΠ°ΠΌΠ΅Π½ΡΠ΅ΠΌ Π² Π½ΡΠΌ ΡΠΎΠΊΠ΅Π½ Π±ΠΎΡΠ° Π½Π° ΡΠΎΡ, ΠΊΠΎΡΠΎΡΡΠΉ Π²ΡΠ΄Π°Π» @BotFather, ΠΈ user_id β Π½Π° Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ id Π²Π°ΡΠ΅Π³ΠΎ Π°ΠΊΠΊΠ°ΡΠ½ΡΠ°. ΠΡΠΎΠ²Π΅ΡΠΊΠ° id ΡΠ·Π΅ΡΠ° Π½ΡΠΆΠ½Π° Π΄Π»Ρ ΡΠΎΠ³ΠΎ, ΡΡΠΎΠ±Ρ Π±ΠΎΡ ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ» Π΄ΠΎΡΡΡΠΏ ΠΊ Π²Π°ΡΠ΅ΠΌΡ ΡΠ΅ΡΠ²Π΅ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π²Π°ΠΌ. Π€ΡΠ½ΠΊΡΠΈΡ check_output()
Π²ΡΠΏΠΎΠ»Π½ΡΠ΅Ρ ΠΏΠ΅ΡΠ΅Π΄Π°Π½Π½ΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ.
ΠΡΡΠ°Π»ΠΎΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π·Π°ΠΏΡΡΡΠΈΡΡ Π±ΠΎΡΠ°. ΠΠ»Ρ Π·Π°ΠΏΡΡΠΊΠ° ΠΏΡΠΎΡΠ΅ΡΡΠΎΠ² Π½Π° ΡΠ΅ΡΠ²Π΅ΡΠ΅ Ρ ΠΏΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ screen
(sudo apt-get install screen
):
screen -dmS ServerBot python3 bot.py
(Π³Π΄Π΅ Β«ServerBotΒ» β ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΠΏΡΠΎΡΠ΅ΡΡΠ°)
ΠΡΠΎΡΠ΅ΡΡ Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ Π·Π°ΠΏΡΡΡΠΈΡΡΡ Π² ΡΠΎΠ½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅. ΠΠ΅ΡΠ΅ΠΉΠ΄Π΅ΠΌ Π² Π΄ΠΈΠ°Π»ΠΎΠ³ Ρ Π±ΠΎΡΠΎΠΌ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ, ΡΡΠΎ Π²ΡΡ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ, ΠΊΠ°ΠΊ Π½Π°Π΄ΠΎ:
Π‘ongratulations! ΠΠΎΡ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅Ρ ΠΏΡΠΈΡΡΠ»Π°Π΅ΠΌΡΠ΅ Π΅ΠΌΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ. Π’Π΅ΠΏΠ΅ΡΡ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΄ΠΎΡΡΡΠΏ ΠΊ ΡΠ΅ΡΠ²Π΅ΡΡ, Π²Π°ΠΌ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΎΡΠΊΡΡΡΡ Π΄ΠΈΠ°Π»ΠΎΠ³ Ρ Π±ΠΎΡΠΎΠΌ.
ΠΠΎΠ²ΡΠΎΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄
ΠΠ°ΡΠ°ΡΡΡΡ, Π΄Π»Ρ ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³Π° ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΡΠ΅ΡΠ²Π΅ΡΠ° ΠΏΡΠΈΡ ΠΎΠ΄ΠΈΡΡΡ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ ΠΎΠ΄Π½ΠΈ ΠΈ ΡΠ΅ ΠΆΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ. ΠΠΎΡΡΠΎΠΌΡ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠ²ΡΠΎΡΠ° ΠΊΠΎΠΌΠ°Π½Π΄ Π±Π΅Π· ΠΈΡ ΠΏΠΎΠ²ΡΠΎΡΠ½ΠΎΠ³ΠΎ ΠΎΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π±ΡΠ΄Π΅Ρ ΠΎΡΠ΅Π½Ρ ΠΊ ΠΌΠ΅ΡΡΡ.
Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²ΡΠ²Π°ΡΡ Π±ΡΠ΄Π΅ΠΌ ΠΏΡΠΈ ΠΏΠΎΠΌΠΎΡΠΈ inline ΠΊΠ½ΠΎΠΏΠΎΠΊ ΠΏΠΎΠ΄ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡΠΌΠΈ:
from subprocess import check_output
import telebot
from telebot import types #ΠΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΈΠΌΠΏΠΎΡΡ ΠΊΠ½ΠΎΠΏΠΎΠΊ
import time
bot = telebot.TeleBot("XXXXXXXXX:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")#Π’ΠΎΠΊΠ΅Π½ Π±ΠΎΡΠ°
user_id = 0 #id Π²Π°ΡΠ΅Π³ΠΎ Π°ΠΊΠΊΠ°ΡΠ½ΡΠ°
@bot.message_handler(content_types=["text"])
def main(message):
if (user_id == message.chat.id): #ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ, ΡΡΠΎ ΠΏΠΈΡΠ΅Ρ ΠΈΠΌΠ΅Π½Π½ΠΎ Π²Π»Π°Π΄Π΅Π»Π΅Ρ
comand = message.text #ΡΠ΅ΠΊΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ
markup = types.InlineKeyboardMarkup() #ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ
button = types.InlineKeyboardButton(text="ΠΠΎΠ²ΡΠΎΡΠΈΡΡ", callback_data=comand) #ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΊΠ½ΠΎΠΏΠΊΡ
markup.add(button) #Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ½ΠΎΠΏΠΊΡ Π² ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ
try: #Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠ°Ρ - check_output Π²ΡΠ΄Π°ΡΡ exception
bot.send_message(user_id, check_output(comand, shell = True, reply_markup = markup)) #Π²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ ΠΎΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠΌ
except:
bot.send_message(user_id, "Invalid input") #Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅ΠΊΠΎΡΡΠ΅ΠΊΡΠ½Π°
@bot.callback_query_handler(func=lambda call: True)
def callback(call):
comand = call.data #ΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈΠ· ΠΏΠΎΠ»Ρ ΠΊΠ½ΠΎΠΏΠΊΠΈ data
try:#Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠ°Ρ - check_output Π²ΡΠ΄Π°ΡΡ exception
markup = types.InlineKeyboardMarkup() #ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ
button = types.InlineKeyboardButton(text="ΠΠΎΠ²ΡΠΎΡΠΈΡΡ", callback_data=comand) #ΡΠΎΠ·Π΄Π°Π΅ΠΌ ΠΊΠ½ΠΎΠΏΠΊΡ ΠΈ Π² data ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠΌ ΠΊΠΎΠΌΠ°Π½Π΄Ρ
markup.add(button) #Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ ΠΊΠ½ΠΎΠΏΠΊΡ Π² ΠΊΠ»Π°Π²ΠΈΠ°ΡΡΡΡ
bot.send_message(user_id, check_output(comand, shell = True), reply_markup = markup) #Π²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ ΠΎΡΠΏΡΠ°Π²Π»ΡΠ΅ΠΌ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠΌ
except:
bot.send_message(user_id, "Invalid input") #Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π° Π½Π΅ΠΊΠΎΡΡΠ΅ΠΊΡΠ½Π°
if __name__ == '__main__':
while True:
try:#Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ try Π΄Π»Ρ Π±Π΅ΡΠΏΠ΅ΡΠ΅Π±ΠΎΠΉΠ½ΠΎΠΉ ΡΠ°Π±ΠΎΡΡ
bot.polling(none_stop=True)#Π·Π°ΠΏΡΡΠΊ Π±ΠΎΡΠ°
except:
time.sleep(10)#Π² ΡΠ»ΡΡΠ°Π΅ ΠΏΠ°Π΄Π΅Π½ΠΈΡ
ΠΠ΅ΡΠ΅Π·Π°ΠΏΡΡΠΊΠ°Π΅ΠΌ Π±ΠΎΡΠ°:
killall python3
screen -dmS ServerBot python3 bot.py
Π‘Π½ΠΎΠ²Π° ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ, ΡΡΠΎ Π²ΡΡ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎ:
ΠΠΎ Π½Π°ΠΆΠ°ΡΠΈΡ ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΏΠΎΠ΄ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ΠΌ Π±ΠΎΡ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΠΎΠ²ΡΠΎΡΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ, ΠΎΡ ΠΊΠΎΡΠΎΡΠΎΠΉ Π±ΡΠ»ΠΎ ΠΏΡΠΈΡΠ»Π°Π½ΠΎ Π΄Π°Π½Π½ΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅.
ΠΠΌΠ΅ΡΡΠΎ Π·Π°ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ
ΠΠ΅Π·ΡΡΠ»ΠΎΠ²Π½ΠΎ, ΡΠ°ΠΊΠΎΠΉ ΠΌΠ΅ΡΠΎΠ΄ Π½Π΅ ΠΏΡΠ΅ΡΠ΅Π½Π΄ΡΠ΅Ρ ΡΡΠ°ΡΡ Π·Π°ΠΌΠ΅Π½ΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΈΠΌ ΡΠΏΠΎΡΠΎΠ±Π°ΠΌ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ, ΠΎΠ΄Π½Π°ΠΊΠΎ, ΠΎΠ½ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π±ΡΡΡΡΠΎ ΡΠ·Π½Π°ΡΡ ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ ΡΠ΅ΡΠ²Π΅ΡΠ° ΠΈ ΠΎΡΠΏΡΠ°Π²ΠΈΡΡ Π΅ΠΌΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ, Π½Π΅ ΡΡΠ΅Π±ΡΡΡΠΈΠ΅ ΡΠ»ΠΎΠΆΠ½ΠΎΠ³ΠΎ Π²ΡΠ²ΠΎΠ΄Π°.
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com