diff --git a/pro100_binder.lua b/pro100_binder.lua
new file mode 100755
index 0000000..9411bfa
--- /dev/null
+++ b/pro100_binder.lua
@@ -0,0 +1,725 @@
+script_name('pro100_binder')
+script_author("RuBin")
+vkeys = require "lib.vkeys"
+sampev = require 'lib.samp.events'
+
+DIALOG_ID = 7761
+DIALOG_ID_NO_LIST = 7762
+
+local ffi = require "ffi"
+ffi.cdef[[
+     void keybd_event(int keycode, int scancode, int flags, int extra);
+]]
+
+function EmulateKey(key, isDown)
+    if not isDown then
+        ffi.C.keybd_event(key, 0, 2, 0)
+    else
+        ffi.C.keybd_event(key, 0, 0, 0)
+    end
+end
+
+function sampev.onServerMessage(color, message)
+    if message:find('SMS: .+�����������: .+%[(%d+)%]') then
+        replace["{id_in_sms}"] = message:match('SMS: .+�����������: .+%[(%d+)%]')
+    end
+    if message:find('SMS: .+����������: .+%[(%d+)%]') then
+        replace["{id_out_sms}"] = message:match('SMS: .+����������: .+%[(%d+)%]')
+    end
+end
+
+function main()
+	if not isSampLoaded() or not isSampfuncsLoaded() then return end
+	while not isSampAvailable() do wait(100) end
+	
+	settings_load()
+	command_show_dialog()
+
+	replace = {
+		["{target_id}"] = "0",
+		["{target_rp_name}"] = " ",
+		["{target_name}"] = " ",
+		["{id_in_sms}"] = "0",
+		["{id_out_sms}"] = "0"
+	}
+
+	key_press = {}
+	key_press_counter = 0
+	is_key_press = false
+	is_block_gamekeys = false
+
+	while true do
+		wait(0)
+		while_target_check()
+		while_key_check()
+		while_show_dialog()
+	end
+end
+
+function settings_load()
+	AdressConfig = string.format("%s\\moonloader\\config ", getGameDirectory())
+	AdressJson = string.format("%s\\moonloader\\config\\pro100_binder.json", getGameDirectory())
+
+    local table_std = {
+		{ cmd = {'/mm'}, enter = true, key = {'VK_M'} }
+	}
+    if not doesFileExist(AdressJson) then
+        local file, error = io.open(AdressJson, "w")
+        if file ~= nil then
+            file:write(encodeJson(table_std))
+            file:flush()
+            io.close(file)
+        else
+            sampAddChatMessage(error, -1)
+        end
+    end
+
+    try(
+        function()
+            local file, error = io.open(AdressJson, "r")
+            if file then
+                settings = decodeJson(file:read("*a"))
+                io.close(file)
+            end
+        end, 
+        function(e)
+            sampAddChatMessage("[pro100_binder] ������ ������ �������! ��������� ������!", 0xff0000)
+            local file, error = io.open(AdressJson, "w")
+            if file ~= nil then
+                file:write(encodeJson(table_std))
+                file:flush()
+                io.close(file)
+            end
+        end
+    )
+
+
+    if settings ~= nil then
+        if error_ini ~= nil then
+            sampAddChatMessage("[pro100_binder] ������ ��� ������� ��������!", 0xff0000)
+            error_ini = nil
+        end
+        for k,v in pairs(table_std) do
+            if settings[k] == nil then
+                settings[k] = v
+            end
+            for i, s in pairs(v) do
+                if settings[k][i] == nil then
+                    settings[k][i] = s
+                end
+            end
+        end
+        settings_save()
+    else
+        error_ini = true
+        sampAddChatMessage("[pro100_binder] ������ ������ �������! ������ ��� ��� ��������", 0xff0000)
+        settings_load()
+    end
+end
+
+function settings_save()
+    local file, error = io.open(AdressJson, "w")
+    if file ~= nil then
+        file:write(encodeJson(settings))
+        file:flush()
+        io.close(file)
+    else
+        sampAddChatMessage(error, -1)
+    end
+end
+
+function try(f, catch_f)
+	local status, exception = pcall(f)
+	if not status then
+	  catch_f(exception)
+	end
+end
+
+function while_target_check()
+	local result, handle = getCharPlayerIsTargeting(PLAYER_HANDLE)
+	if result then
+		local result2, id = sampGetPlayerIdByCharHandle(handle)
+		if result2 then
+			local name = sampGetPlayerNickname(id)
+			replace["{target_id}"] = id
+			replace["{target_rp_name}"] = name:gsub("_", " ")
+			replace["{target_name}"] = name
+		end
+	end
+end
+
+function while_key_check()
+	local editDialogActive = false
+	if sampIsDialogActive() and sampGetCurrentDialogType() == 1 then
+		editDialogActive = true 
+	end
+
+	if not sampIsScoreboardOpen() and not sampIsChatInputActive() and not isSampfuncsConsoleActive() and not editDialogActive then
+		if isKeyDown(vkeys["VK_MENU"]) then
+			if not is_block_gamekeys then
+				is_block_gamekeys = true
+				setPlayerDuckButton(PLAYER_HANDLE, false)
+				setPlayerEnterCarButton(PLAYER_HANDLE, false)
+			end
+		else
+			if is_block_gamekeys then
+				is_block_gamekeys = false
+				setPlayerDuckButton(PLAYER_HANDLE, true)
+				setPlayerEnterCarButton(PLAYER_HANDLE, true)
+			end
+		end
+
+
+		local isPress = false
+		local isMenu = false
+		for k, v in pairs(vkeys) do
+			if isKeyDown(v) and k ~= "VK_ESCAPE" and k ~= "VK_RETURN" and k ~= "VK_LBUTTON" and k ~= "VK_W" and k ~= "VK_S" and k ~= "VK_A" and k ~= "VK_D" then
+				isPress = true
+				if key_press[k] == nil then
+					if k:find("MENU") then
+						local enter_press = false
+						for k,v in pairs(key_press) do
+							if k:find("MENU") then
+								enter_press = true
+							end
+						end
+						if not enter_press then
+							key_press["VK_MENU"] = "VK_MENU"
+							key_press_counter = key_press_counter + 1
+						end
+					else
+						key_press[k] = v
+						key_press_counter = key_press_counter + 1
+					end
+				end
+			end
+		end
+		
+		if isPress ~= is_key_press and not isPress then
+			for i = 1, #settings do 
+				local down_counter = 0
+				local alt = false
+				for k = 1, #settings[i].key do
+					if settings[i].key[k]:find("MENU") then
+						alt = true
+					end
+					if key_press[settings[i].key[k]] ~= nil then
+						down_counter = down_counter + 1
+					end
+				end
+				if down_counter == #settings[i].key and key_press_counter == #settings[i].key then
+					while_send_array(alt, settings[i].enter, settings[i].cmd)
+					repeat wait(0) until not isKeyDown(vkeys[settings[i].key[#settings[i].key]])
+				end
+			end
+			key_press = {}
+			key_press_counter = 0
+		end
+		is_key_press = isPress
+	end
+end
+
+function check_stop_bind()
+	if isKeyDown(vkeys["VK_N"]) then
+		return true
+	end
+end
+
+function while_send_array(alt, enter, copy)
+
+	local array = {}
+	local command = 0
+	for i = 1, #copy do
+		array[i] = copy[i]
+		if array[i]:find("wait (%d+)") then
+			local ms = array[i]:match("wait (%d+)")
+			if #("wait "..ms) ~= #array[i] then
+				command = command + 1
+			end
+		else
+			command = command + 1
+		end
+	end
+
+	local command_count = 0
+	repeat
+		wait(0)
+		local text = array[1]
+		local wait_time = -1
+
+		if text:find("wait (%d+)") then
+			local ms = text:match("wait (%d+)")
+			if #("wait "..ms) == #text then
+				wait_time = tonumber(ms)
+			end
+		end
+
+		if wait_time ~= -1 then
+			local sleep = os.clock() * 1000
+			repeat 
+				wait(0)
+			until os.clock() * 1000 - sleep > wait_time or check_stop_bind() or not enter
+			if check_stop_bind() then
+				break
+			end
+			table.remove(array, 1)
+		else
+
+			for k,v in pairs(replace) do 
+				if text:find(k) then
+					text = text:gsub(k, v)
+				end
+			end
+
+			if alt and enter then
+				sampSendChat(text)
+				command_count = command_count + 1
+			else
+				local chattext = sampGetChatInputText()
+				sampSetChatInputText(text)
+				sampSetChatInputEnabled(true)
+				if enter then
+					repeat
+						wait(0)
+					until sampIsChatInputActive()
+					EmulateKey(13, true)
+					EmulateKey(13, false)
+				end
+				command_count = command_count + 1
+			end
+			repeat
+				wait(0)
+			until not sampIsChatInputActive()
+			if command > 1 then
+				printStringNow(string.format("BIND COUNT: %s/%s~n~Press N to stop", command_count, command), 1000)
+				if check_stop_bind() then
+					break
+				end
+			end
+			table.remove(array, 1)
+		end
+	until #array <= 0
+end
+
+function command_show_dialog()
+	sampRegisterChatCommand('binder',function()
+		show_dialog("main")
+	end)
+end
+
+function show_dialog(param)
+	if param == "main" then
+		show_main_dialog()
+	end
+end
+
+function get_text_keys_array(i)
+	local key_name = ""
+	for s = 1, #settings[i].key do
+		if (settings[i].key[s]:find("MENU") and not key_name:find("ALT")) or not settings[i].key[s]:find("MENU") then
+			key_name = key_name..settings[i].key[s]:gsub("VK_",""):gsub("MENU", "ALT")..(settings[i].key[s + 1] ~= nil and " + " or "")
+		end
+	end
+	return key_name
+end
+
+function show_main_dialog()
+	dialogArray = {}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "������� ����\t \t ",
+		func = function(button, list, input)
+			if button == 1 then
+				show_create_dialog()
+			end
+		end
+	}
+
+	for i = 1, #settings do 
+		local key_name = get_text_keys_array(i)
+
+		dialogArray[#dialogArray + 1] = {
+			text = " ["..i.."] "..key_name.."\t"..(settings[i].cmd[1] ~= nil and settings[i].cmd[1] or "nil").." ["..#settings[i].cmd.."]\t"..(settings[i].enter and "ENTER" or " "),
+			func = function(button, list, input)
+				if button == 1 then
+					show_bind_dialog(i, key_name)
+					return
+				end
+			end
+		}
+	end
+
+	local text = ""
+	for k,v in pairs(dialogArray) do
+		text = text..v.text.."\n"
+	end
+
+	sampShowDialog(DIALOG_ID,"PRO100_BINDER",text,"�������","�������",4)
+end
+
+function show_bind_dialog(bind_id, key_name)
+	if settings[bind_id] == nil then return end
+
+	local reset = function(button, list, input)
+		if button == 1 then
+			show_bind_dialog(bind_id, key_name)
+		else
+			show_main_dialog()
+		end
+	end
+
+	dialogArray = {}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "���������: "..key_name,
+		func = reset
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "����-��������: "..(settings[bind_id].enter and "ON" or "OFF"),
+		func = reset
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " ",
+		func = reset
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "[1] �������",
+		func = function(button, list, input)
+			if button == 1 then
+				table.remove(settings, bind_id)
+				settings_save()
+			end
+			show_main_dialog()
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "[2] �������������",
+		func = function(button, list, input)
+			if button == 1 then
+				show_create_dialog(bind_id)
+			end
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = "\n����: \n"..get_text_array(settings[bind_id].cmd),
+		func = reset
+	}
+
+	local text = ""
+	for k,v in pairs(dialogArray) do
+		text = text..v.text.."\n"
+	end
+
+	sampShowDialog(DIALOG_ID,"PRO100_BINDER",text,"�������","�������",4)
+end
+
+function get_text_keys(keys)
+	local text_keys = ""
+	for i = 1, #keys do
+		if (keys[i]:find("MENU") and not text_keys:find("ALT")) or not keys[i]:find("MENU") then
+			text_keys = text_keys..keys[i]:gsub("VK_", ""):gsub("MENU", "ALT")..(keys[i+1] ~= nil and " + " or "")
+		end
+	end
+	return text_keys
+end
+
+function get_text_array(array)
+	local text= ""
+	for i = 1, #array do
+		text = text..array[i].."\n"
+	end
+	return text
+end
+
+function show_create_dialog(param)
+	edit_key = param
+	dialogArray = {}
+	local keys = {}
+
+	if edit_key ~= nil and settings[edit_key] ~= nil then
+		keys = settings[edit_key].key
+	end
+
+	local timer_show_dialog = os.time()
+	local result, button = false, 0
+	local refresh = true
+	local global_isPress = false
+	repeat
+		wait(0)
+
+		local isPress = false
+		for k, v in pairs(vkeys) do
+			if isKeyDown(v) and k ~= "VK_ESCAPE" and k ~= "VK_RETURN" and k ~= "VK_LBUTTON" then
+				isPress = true
+
+				if k:find("MENU") then
+					local result_search = true
+					for i = 1, #keys do
+						if keys[i]:find("MENU") then
+							result_search = false
+						end
+					end
+
+					if result_search then
+						keys[#keys + 1] = "VK_MENU"
+						refresh = true
+					end
+				else
+					local result_search = false
+					for i = 1, #keys do
+						if keys[i] == k then
+							result_search = true
+						end
+					end
+
+					if not result_search then
+						keys[#keys + 1] = k
+						refresh = true
+					end
+				end
+			end
+		end
+		
+		if isPress ~= global_isPress and isPress then
+			keys = {}
+		end
+		global_isPress = isPress
+
+		if os.time() - timer_show_dialog > 0 or refresh then
+			refresh = false
+			timer_show_dialog = os.time()
+
+			local text_keys = get_text_keys(keys)
+
+			local text = string.format("{FFFFFF}������� ������� ��� ��������� ������\n������������: %s", text_keys)
+			sampShowDialog(DIALOG_ID,"PRO100_BINDER",text,"�����","�����",0)
+		end
+		
+		local res, button, list, input = sampHasDialogRespond(DIALOG_ID)
+		if res then
+			if button == 1 then
+				if #keys > 0 then
+					local array = {}
+					if edit_key ~= nil and settings[edit_key] ~= nil then
+						array = settings[edit_key].cmd
+					end
+					show_create2_dialog(keys, array)
+					break
+				else
+					show_create_dialog(param)
+					break
+				end
+			else
+				show_main_dialog()
+				break
+			end
+		end
+	until result
+end
+
+function show_create2_dialog(keys, array)
+	dialogArray = {}
+	dialogArray[#dialogArray + 1] = {
+		text = "���������: "..get_text_keys(keys),
+		func = function(button, list, input)
+			show_create_dialog(edit_key)
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " ",
+		func = function(button, list, input)
+			show_create_dialog(edit_key)
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " [1] �������� ������",
+		func = function(button, list, input)
+			if button == 1 then
+				show_edit("�������� ������", keys, array)
+			else
+				show_create_dialog(edit_key)
+			end
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " [2] �������� �������� (ms)",
+		func = function(button, list, input)
+			if button == 1 then
+				show_edit("�������� �������� (ms)", keys, array)
+			else
+				show_create_dialog(edit_key)
+			end
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " [3] �����",
+		func = function(button, list, input)
+			if button == 1 then
+				show_create3_dialog(keys, array)
+			else
+				show_create_dialog(edit_key)
+			end
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " [4] ������",
+		func = function(button, list, input)
+			show_create_dialog(edit_key)
+		end
+	}
+
+	dialogArray[#dialogArray + 1] = {
+		text = " ",
+		func = function(button, list, input)
+			show_create_dialog(edit_key)
+		end
+	}
+
+	if #array > 0 then
+		dialogArray[#dialogArray + 1] = {
+			text = "������ ������	(click to del)",
+			func = function(button, list, input)
+				show_create_dialog(edit_key)
+			end
+		}
+	end
+
+	for i = 1, #array do
+		dialogArray[#dialogArray + 1] = {
+			text = "   "..array[i],
+			func = function(button, list, input)
+				if button == 1 then
+					table.remove(array, i)
+				end
+				show_create2_dialog(keys, array)
+			end
+		}
+	end
+
+	dialogArray[#dialogArray + 1] = {
+		text = " ",
+		func = function(button, list, input)
+			show_create_dialog(edit_key)
+		end
+	}
+
+	local text = ""
+	for i = 1, #dialogArray do
+		text = text..dialogArray[i].text.."\n"
+	end
+
+	sampShowDialog(DIALOG_ID,"PRO100_BINDER",text,"�����","������",2)
+end
+
+function show_edit(text, keys, array)
+	dialogArray = {}
+
+	if text == "�������� ������" then
+
+		text = string.format("%s\n�������������� ���������� ����������!\n\n ������ - ��� ����� �� ����������� � �������� (������� ����������� ��� �������)\n ����� ������ ���������� ������ ����� ������ � �� ������ �� ������������!\n\t{target_id} - ID ������\n\t{target_rp_name} - RP ��� ������\n\t{target_name} - ��� ������", text)
+
+		local server_name = sampGetCurrentServerName()
+		if server_name:find("SRP") or server_name:find("Samp%-Rp") then
+			text = string.format("%s\n\n�������������� ���������� ��� Samp-Rp!\n\t{id_in_sms} - ID ������ � ���������� ��������� ���\n\t{id_out_sms} - ID ������ � ���������� ������������� ���� ���", text)
+		end
+
+		dialogArray[1] = {
+			text = " [1] �������� ������",
+			func = function(button, list, input)
+				if button == 1 then
+					if #input > 0 then
+						array[#array + 1] = input
+					end
+					show_create2_dialog(keys, array)
+				end
+				show_create2_dialog(keys, array)
+			end
+		}
+	end
+
+	if text == "�������� �������� (ms)" then
+		dialogArray[1] = {
+			text = " [2] �������� �������� (ms)",
+			func = function(button, list, input)
+				if button == 1 then
+					if input:find("%d+") then
+						local ms = input:match("(%d+)")
+						if #ms == #input then
+							array[#array + 1] = "wait "..input:match("(%d+)")
+						end
+					end
+				end
+				show_create2_dialog(keys, array)
+			end
+		}
+	end
+
+	sampShowDialog(DIALOG_ID_NO_LIST,"PRO100_BINDER",text,"���������","������",1)
+end
+
+function show_create3_dialog(keys, array)
+	local text_keys = get_text_keys(keys)
+	local text_command = get_text_array(array)
+	local text = string.format("{FFFFFF}���������: %s\n����: %s\n\n  ���������� �������������?\n\n      (����� ������� � ���)", text_keys, text_command)
+
+	dialogArray = {}
+	dialogArray[1] = {
+		text = "",
+		func = function(button, list, input)
+			if button == 1 then
+				show_create4_dialog(keys, array, true)
+			else
+				show_create4_dialog(keys, array, false)
+			end
+		end
+	}
+
+	sampShowDialog(DIALOG_ID_NO_LIST,"PRO100_BINDER",text,"��","���",0)
+end
+
+function show_create4_dialog(keys, array, enter)
+	local text_keys = get_text_keys(keys)
+	local text_command = get_text_array(array)
+
+	local text = string.format("{FFFFFF}���������: %s\n����: %s\n����-��������: %s\n\n  ���������?", text_keys, text_command, (enter and "ON" or "OFF"))
+
+	dialogArray = {}
+	dialogArray[1] = {
+		text = "",
+		func = function(button, list, input)
+			if button == 1 then
+				if edit_key ~= nil and settings[edit_key] ~= nil then
+					settings[edit_key] = { cmd = array, enter = enter, key = keys }
+				else
+					settings[#settings + 1] = { cmd = array, enter = enter, key = keys }
+				end
+				settings_save()
+			end
+			show_main_dialog()
+		end
+	}
+
+	sampShowDialog(DIALOG_ID_NO_LIST,"PRO100_BINDER",text,"YES","NO",0)
+end
+
+
+function while_show_dialog()
+	local result, button, list, input = sampHasDialogRespond(DIALOG_ID)
+	if result and dialogArray ~= nil and dialogArray[list + 1] then
+		dialogArray[list + 1].func(button, list, input)
+	end
+	local result, button, list, input = sampHasDialogRespond(DIALOG_ID_NO_LIST)
+	if result and dialogArray ~= nil and dialogArray[1] then
+		dialogArray[1].func(button, list, input)
+	end
+end
\ No newline at end of file