в тг канале рея была обнваПоследняя версия 04.02.2023?
в тг канале рея была обнва
function onRunCommand(cmd, params)
if cmd:find("^!afkoff") then
afk = false
return false
end
end
Теперь не onruncommand, а oninputкод:function onRunCommand(cmd, params) if cmd:find("^!afkoff") then afk = false return false end end
В обнове рей написал, что добавил второй параметр для этого события, но чет я не понимаю как это работает
До этого было onRunCommand с одним параметром и теперь не работают команды
Странная условия. 65535 это которое отсутствует листайтем в диалоге омп?омп сделал валидацию диалогов, и теперь чтобы с ним взаимодействовать надо слать листайтем 65535, из за этого на омп серверах диалоги не отправляются, есть возможность пофиксить это?
В теме подробно написано,какие прокси надо, не ipv6
Во 1 если прочитать внимательнее мое сообщение, можно увидеть, что я написал не только про ipv6, во вторых либо я в глаза ебусь, либо там внатуре нету ничего про версию айпиВ теме подробно написано,какие прокси надо, не ipv6
у тебя написано onLoUd, а не onLoAdonLoud
ХАхахаха от души:D оказывается я в глаза ебусьу тебя написано onLoUd, а не onLoAd
function onLoad()
getLastUpdate()
telegram_task = newTask(get_telegram_updates, 1000)
end
function getLastUpdate()
async_http_request('https://api.telegram.org/bot'.. token_telegram ..'/getUpdates?chat_id='..chatid_telegram ..'&offset=-1','',function(result)
if result then
local proc_table = json.decode(result)
if proc_table.ok then
if #proc_table.result > 0 then
local res_table = proc_table.result[1]
if res_table then
updateid = res_table.update_id
end
else
updateid = 1
end
end
end
end)
end
function get_telegram_updates() -- функция получения сообщений от юзера
while not updateid do wait(0) end -- ждем пока не узнаем последний ID
local runner = requestRunner()
local reject = function() end
local args = ''
while true do
url = '[URL]https://api.telegram.org/bot[/URL]'.. token_telegram ..'/getUpdates?chat_id='..chatid_telegram ..'&offset=-1' -- создаем ссылку
threadHandle(runner, url, args, processing_telegram_messages, reject)
wait(0)
end
end
function processing_telegram_messages(result) -- функция проверОчки того что отправил чел
if result then
-- тута мы проверяем все ли верно
local proc_table = json.decode(result)
if proc_table.ok then
if #proc_table.result > 0 then
local res_table = proc_table.result[1]
if res_table then
if res_table.update_id ~= updateid then
updateid = res_table.update_id
local message_from_user = res_table.message.text
if message_from_user then
-- и тут если чел отправил текст мы сверяем
local textTg = u8:decode(message_from_user) .. " " --добавляем в конец пробел дабы не произошли тех. шоколадки с командами(типо чтоб !q не считалось как !qq)
local textTg2 = u8:decode(message_from_user)
if textTg2:find("%/send (%d+) (.+)") then
local local_bot_id, sendArg = textTg2:match("^/send (%d+) (.+)")
if tonumber(local_bot_id) == getBotId() then
sendInput(sendArg)
sendTelegramNotification('Вы написали: "'..sendArg..'"')
end
end
if textTg2:find("^/stats (%d+)") then
local local_botid = textTg2:match("/stats (%d+)")
if tonumber(local_botid) == getBotId() then
sendInput("/stats")
show_stats_info = true
end
end
if textTg2:find("^/diag (%d+) (%d) (%d+) (.*)") then
local argument_id, argument_button, argument_listbox, argument_input = textTg2:match("^/diag (%d+) (%d) (%d+) (.*)")
sendDialogResponse(tonumber(argument_id), tonumber(argument_button), tonumber(argument_listbox), tostring(argument_input))
end
if textTg2:match('^/rsamp (%d+) (.+)') then
local id_rsamp_arg, rsamp_arg = textTg2:match('^/rsamp (%d+) (.+)')
if tonumber(id_rsamp_arg) == getBotId() then
runCommand(rsamp_arg)
end
end
if textTg2:match('^/log (%d+)') then
local ar_for_log = textTg2:match('^/log (%d+)')
if tonumber(ar_for_log) == getBotId() then
log_chat_serv_info = not log_chat_serv_info
sendTelegramNotification(log_chat_serv_info and "Логирование выключено"or "Логирование включено")
end
end
if textTg2:match('^/chatl (%d+)') then
local arg_for_id_bot = textTg2:match('^/chatl (%d+)')
if tonumber(arg_for_id_bot) == getBotId() then
log_chat_all = not log_chat_all
sendTelegramNotification(log_chat_all and "Логирование чата выключено" or "Логирование чата включено")
end
end
if textTg2:match('^/binfo') then
sendTelegramNotification(getBotNick().."["..getBotId().."]")
if next(staff_on_roulette) then
for k, v in ipairs(staff_on_roulette) do
print("Количество рулеток "..k.." Название рулеток "..v)
end
end
end
if textTg2:match("^/case") then
if not next(status.item_list) then
sendInput("/stats")
save_case_item = true
end
for k, v in pairs(status.item_list) do
sendTelegramNotification("[№"..v.index.. "] "..v.name.. " ".." ["..v.count.."шт]")
end
end
end
end
end
end
end
end
end[/spoiler]
для прокси тас по другому надо, буду дома, скину шаблон, сам несколько часов ебалсяДа так я уже пробовал, все равно шлет ошибку попытки подключения
Если юзать VPN или просто пинговать настоящий IP на ПК то нормально, а вот именно с прокси это не катит
Да я уже разобрался, не надо, спасибодля прокси тас по другому надо, буду дома, скину шаблон, сам несколько часов ебался
Хотел спросить,бот заходит с моего айпи ,или как? То есть меня забанят по айпи если вычеслят бота?ну тоесть я без прокси запущу бота ,то при его входе ,админам какой айпи будет показываться?Минималистичный фейк клиент с возможностью расширения функционала с помощью Lua API
Посмотреть вложение 136678
Lua:print(string text) -- вывести сообщение sleep(int ms) -- задержка ( вешает всю программу ) exit() -- выйти из программы ( с отключением от сервера и выгрузкой скриптов ) setServerAddress(string address) -- установить адрес сервера setBotNick(string nick) -- установить ник боту setBotPosition(float x, float y, float z) -- установить позицию боту setBotQuaternion(float w, float x, float y, float z) -- установить кватернион боту setBotRotation(float angle) -- установить угол боту setBotHealth(float health) -- установить хп боту setBotArmor(float armor) -- установить броню боту setNetworkAdapter(string ip) -- установить сетевой адаптер setWindowTitle(string title) -- установить заголовок окну программы lockWindowTitle(bool lock) -- заблокировать изменение заголовка окна программой setWindowText(string text) -- установить текст в строке состояния над чатом ( GUI ) lockWindowText(bool lock) -- заблокировать изменение строки состояния flashWindow() -- моргнуть окном showWindow() -- развернуть окно setRate(int rate, int value) -- установить рейты ( 0-8 ) ** runCommand(string cmd) -- обработать команду раксампа reconnect(int ms) -- переподключиться к серверу, вызов без параметров установит стандартную задержку spawn() -- заспавниться updateSync() -- обновить синхру resetConnectTimeout() -- сбросить текущий таймер переподключения setBotVehicle(int id, int seat = 0) -- установить авто боту. id 0 - нет авто, seat 0 - водительское coordStart(float x, float y, float z, int delay, float step, bool off_at_spawn) -- включить курд coordStop() -- выключить курд setLogPath(string path) -- установить файл лога setAutoPick(bool state) -- включить/выключить автовзятие пикапов proxyConnect(string address, string username, string password) -- подключиться к прокси, 2 и 3 аргумент необязательны proxyDisconnect() -- отключиться от прокси setBotMoney(int money) -- установить деньги боту registerHandler(string event, function handler) -- регистрирует обработчик события destroyHandlers() -- удаляет все обработчики событий скрипта int id = getBotId() -- получить ид бота string ip = getServerAddress() -- получить адрес сервера string name = getServerName() -- получить название сервера string nick = getBotNick() -- получить ник бота int score = getBotScore() -- получить очки ( лвл ) бота int ping = getBotPing() -- получить пинг бота bool spawned = isBotSpawned() -- проверить, заспавнен ли бот int interior = getBotInterior() -- получить интерьер бота int skin = getBotSkin() -- получить скин бота float x, float y, float z = getBotPosition() -- получить позицию бота float w, float x, float y, float z = getBotQuaternion() -- получить кватернион бота float angle = getBotRotation() -- получить угол бота float health = getBotHealth() -- получить хп бота float armor = getBotArmor() -- получить броню бота string ip = getNetworkAdapter() -- получить сетевой адаптер string args = getCommandLineArgs() -- получить аргументы командной строки bool connected = isBotConnected() -- проверить, подключен ли бот к серверу int vehicle = getBotVehicle() -- получить авто бота. 0 - нет авто bool active = isCoordActive() -- проверить, включен ли курд string path = getPath(string additional) -- получить корень программы bool connected = isProxyConnected() -- проверить, подключен ли к прокси int money = getBotMoney() -- получить деньги бота int count = getPlayerCount(bool stream = false) -- получить количество игроков bool connected = isPlayerConnected(int id) -- проверить, подключен ли игрок bool exist = doesPlayerExist(int id) -- проверить, существует ли игрок bool exist = doesVehicleExist(int id) -- проверить, существует ли авто bool exist = doesPickupExist(int id) -- проверить, существует ли пикап bool exist = doesLabelExist(int id) -- проверить, существует ли 3д текст table players = getAllPlayers() -- получить всех игроков table vehicles = getAllVehicles() -- получить все авто table pickups = getAllPickups() -- получить все пикапы table labels = getAllLabels() -- получить все 3д тексты table player = getPlayer(int id) -- получить игрока, не вернет ничего, если не подключен table vehicle = getVehicle(int id) -- получить авто * table pickup = getPickup(int id) -- получить пикап * table label = getLabel(int id) -- получить 3д текст * * не вернет ничего, если не существует -- структуры: getAllPlayers() = { [id] = { int score, int ping, string nick, bool exist, int skin, int color, table position }, ... } getAllVehicles() = { [id] = { float health, int model, string name, string number, bool engine, bool lights, bool alarm, bool locked, table position }, ... } getAllPickups() = { [id] = { int model, int type, table position }, ... } getAllLabels() = { [id] = { float distance, int color, bool test_los, int player_id, int vehicle_id, string text, table position }, ... } ** рейты: RATE_SLEEP RATE_CONNECT RATE_RECONNECT RATE_NETWORK RATE_LUA RATE_SPECTATE RATE_ONFOOT RATE_INCAR RATE_AIM
События можно регистрировать двумя способами:
Lua:-- по классике function onRunCommand(cmd) print(cmd) end -- либо так registerHandler("onRunCommand", function(cmd) print(cmd) end)
Список событий
* поддерживает выход с помощью return true/falseLua:onLoad() -- загрузка скрипта onUnload() -- выгрузка скрипта onUpdate() -- вызывается каждые N ms, указывается в конфиге onRequestConnect() -- запрос на подключение к серверу * onConnect() -- коннект к серверу onDisconnect() -- дисконнект от сервера onProxyConnect() -- подключение к прокси onProxyDisconnect() -- отключение от прокси onProxyError() -- ошибка подключения к прокси onCoordStart() -- старт курда onCoordStop() -- остановка курда onRunCommand(string cmd) -- ввод клиентской команды !cmd * onPrintLog(string text) -- вывод в лог * onSendPacket(id, bs) -- отправка пакета * onReceivePacket(id, bs) -- получение пакета * onSendRPC(id, bs) -- отправка RPC * onReceiveRPC(id, bs) -- получение RPC *
creditsLua:bitStream bs = bitStream.new() -- создать новый объект bs:writeBool(bool value) bs:writeInt8(int value) bs:writeUInt8(int value) bs:writeInt16(int value) bs:writeUInt16(int value) bs:writeInt32(int value) bs:writeUInt32(int value) bs:writeFloat(float value) bs:writeString(string value) bs:writeEncoded(string value) bs:writeBitStream(bitStream bs) bs:resetWritePointer() bs:resetReadPointer() bs:setWriteOffset(int offset) bs:setReadOffset(int offset) bs:writeBuffer(int bitStreamPtr, int size) bs:ignoreBits(int value) bs:ignoreBytes(int value) bs:reset() bool value = bs:readBool() int value = bs:readInt8() int value = bs:readUInt8() int value = bs:readInt16() int value = bs:readUInt16() int value = bs:readInt32() int value = bs:readUInt32() float value = bs:readFloat() string value = bs:readString(int length) string value = bs:readEncoded(int length) bitStream bs = bs:getBitStream() int offset = bs:getWriteOffset() int offset = bs:getReadOffset() bool result = bs:readBuffer(int data_ptr, int size) int value = bs:getNumberOfBitsUsed() int value = bs:getNumberOfBytesUsed() int value = bs:getNumberOfUnreadBits() int value = bs:getNumberOfUnreadBytes() int ptr = bs:getDataPtr() int ptr = bs:getBitStreamPtr() bool result = bs:sendRPC(int id) bool result = bs:sendPacket() bool result = bs:sendRPCEx(int id, int priority, int reliability, int channel, bool timestamp) bool result = bs:sendPacketEx(int priority, int reliability, int channel)
Добавляет новые функции и методы битстрима, идет из коробки
Lua:require("addon") -- подключить аддон bool value = isBotInAnyVehicle() -- проверить, находится ли бот в авто sendSpawnRequest() -- отправить запрос спавна sendDialogResponse(int id, int button, int list, string input) -- отправить ответ на диалог sendClickTextdraw(int id) -- кликнуть по текстдраву sendPickedUpPickup(int id) -- поднять пикап sendVehicleEnter(int id, bool passenger) -- отправить посадку в авто sendVehicleExit(int id) -- отправить выход из авто sendTargetUpdate(int object, int vehicle, int player, int actor) -- отправить таргет камеры sendInput(string text) -- отправить в чат ( текст с / отошлется командой ) bitStream:readString8() -- прочитать строку с длиной int8 bitStream:writeString8(string value) -- записать длину в int8 и строку bitStream:readBool8() -- прочитать bool в int8 bitStream:writeBool8(bool value) -- записать bool в int8 bitStream:writeArray(table value) -- записать массив байтов bitStream:writeVector3(table value ) -- записать vector3 bitStream:readVector3() -- прочитать vector3 task = newTask(function f, bool/int halted, ...) -- создать таску и получить объект -- если halted - true, то таска запустится приостановленной -- если halted - число, то запустится с указанной задержкой -- ... - аргументы, передаваемые в функцию clearTasks() -- убить все таски wait(int time) -- задержка в мс, работает только внутри тасков -- методы task:halt() -- поставить на паузу task:tick() -- принудительно выполнить task:resume() -- возобновить выполнение task:kill() -- убить таску bool alive = task:isAlive() -- проверить, существует ли таска bool halted = task:isHalted() -- проверить, на паузе ли таска
Так как основная цель этого апи - прописать реакцию бота на внешние раздражители, весь код выполняется в различных событиях. Таких как загрузка/выгрузка скрипта, подключение/отключение к серверу/прокси, сетевые события, скриптовые RPC и т.д. Полный список всех функций и событий есть в этой теме под спойлерами выше.
Lua:-- сразу можем подключить две либы, они используются почти во всех скриптах require "addon" -- стандартная библиотека с доп. функциями на lua local sampev = require "samp.events" -- библиотека FYP'а, адаптированная под раксамп -- onLoad - аналог main в MoonLoader. За исключением того, что в нем нельзя использовать задержку без потока, о ней ниже function onLoad() print("hello world") setRate(RATE_LUA, 100) -- теперь функция onUpdate будет вызываться каждые 100 ms end function onConnect() print("мы подключились к серверу!") end -- эта функция вызывается постоянно каждые n миллисекунд, в onLoad function onUpdate() -- так же end -- функция вызывается при выгрузке скрипта в двух случаях: перезагрузка всех скриптов и выход из программы -- можно в ней сделать сохранение каких-нибудь данных скрипта -- но важные данные лучше сохранять до, т.к. при аварийном выходе она не всегда будет вызвана function onUnload() print("выгружаемся") end
Часто нужно вызывать несколько функций подряд с некоторой задержкой. Самый простой вариант - функция sleep
Задержка сработала, но вся программа зависла на 3 секунды, а персонаж на сервере стоял в афк, так что она практически не используется разработчиками. Для этого в аддоне есть менеджер корутин, принцип такой же, как и в MoonLoader. С помощью него мы можем создавать скриптовые потоки, которые можно ставить на паузу и не блокировать основной поток программы. Обратите внимание, что это не обеспечивает многопоточность в чистом виде, так что ресурсозатратные функции все равно будут вешать раксамп на время выполнения.Lua:function onLoad() print("добрый день") sleep(3000) -- ждём 3 секунды print("хороший сегодня день") end
Lua:-- если текст для флуда был задан пользователем через команду, -- то текст будет отправляться в чат каждую секунду function onLoad() print("флудер загружен") newTask(function() wait(1000) if flood_text then sendInput(flood_text) end end) end -- ловим в событии ввод команды в программу function onRunCommand(cmd) if cmd:find("^!flood") then if flood_text then -- если флудер работает, то повторный ввод команды выключит его flood_text = nil else flood_text = cmd:match("^!flood (.-)$") -- с помощью регулярного выражения получаем из команды аргумент end return false -- чтобы она не шла пошла дальше в стандартный обработчик команд и не получить ошибку "неизвестная команда" от раксампа end end
И рассмотрим напоследок то, с чем больше всего придется работать - samp events. Больше информации и примеров в основной теме SAMP.Lua
Lua:math.randomseed(os.time()) -- без этого math.random будет выдавать каждый раз одинаковые результаты -- хук сообщений от сервера function sampev.onServerMessage(color, text) if text:find("^Администратор .- Вы тут?") -- если вам нужен поток всего для одной функции, а не блока кода, -- то проще создать поток таким образом newTask(sendInput, math.random(3000, 6000, "тут") end if text:find("хуй") then -- фильтруем базар text = text:gsub("хуй", "***") end return { color, text } -- возвращаем исправленное сообщение end -- фриз персонажа function sampev.onTogglePlayerControllable(controllable) if not controllable then print("нашего бота заморозили!") else print("фух, разморожен") end end -- сервер устанавливает хп function sampev.onSetPlayerHealth(health) if health < 100 then return false -- отклоняем пакет, получается антиголод end end -- пакет с информацией о выпущенной пули function sampev.onBulletSync(id, data) if data.targetId == getBotId() then -- если пуля летит в нас, то отклоняем ее - гм от пуль return false end end
Lua:require("addon") local sampev = require("samp.events") local password = "123456" local dialog_id = 2 function sampev.onShowDialog(id, style, title, btn1, btn2, text) if id == dialog_id then sendDialogResponse(id, 1, -1, password) return false end end
Искать нужный диалог можно еще, например, по названию
Lua:function sampev.onShowDialog(id, style, title, btn1, btn2, text) if title:find("Авторизация") then sendDialogResponse(id, 1, -1, password) return false end end
Используем requests
Синхронный запрос, который вешает поток программы во время выполнения
Lua:local requests = require("requests") -- получить свой IP local response = requests.get("https://api.ipify.org") if response.status_code == 200 then -- OK print(response.text) end
Асинхронный запрос с помощью effil. Для работы нужно обновить либу до последней версии отсюда: github
Lua:local effil = require 'effil' async_http_request = setmetatable({}, { __index = function(_, method) return function(url, args, resolve, reject) local runner = effil.thread(function(method, url, args) local requests = require 'requests' local result, response = pcall(requests.request, method:upper(), url, args and effil.dump(args)) if result then response.json, response.xml = nil, nil return true, response else return false, response end end)(method, url, args) if not resolve then resolve = function() end end if not reject then reject = function() end end newTask(function() while true do local status, err = runner:status() if err then return reject(err) elseif status == 'cancelled' then return reject(status) elseif status == 'completed' then local result, response = runner:get() if result then return resolve(response) end return reject(response) end wait(0) end end) end end })
Примеры использования
Lua:-- отправка сообщения в тг async_http_request.get("https://api.telegram.org/bot5678126785:AJsfj8i123j5rtAJSF91/sendMessage", { params = { chat_id = "12848142", text = "huyita" } }) -- получение своего IP async_http_request.get("https://api.ipify.org", function(response) -- эта функция вызовется при успешном запросе print(response.text) print(response.status_code) end, function(err) -- эта функция вызовется при ошибке print("ошибка:", err) end)
Кнопки отправляются в синхре игрока в поле keysData. У вас не получится сесть в авто только лишь с помощью нажатия F/G или прожать текстдрав через клик мыши, для этого есть отдельные функции!
Список айди кнопок : wiki
Клавиши: Y/N/H ( 1/2/3 соответственно ) отправляются в поле specialKey
Использование:Lua:local sampev = require("samp.events") -- обычные клавиши ( alt, f, лкм, ... ) function sendKey(id) key = id updateSync() -- принудительно отправляем пакет синхронизации с кнопками end -- Y, N, H function sendSpecialKey(id) skey = id updateSync() end function onRunCommand(cmd) if cmd:find("^!key %d+$") then sendKey(tonumber(cmd:match("%d+"))) return false elseif cmd:find("^!skey %d+$") then sendSpecialKey(tonumber(cmd:match("%d+"))) return false end end function sampev.onSendPlayerSync(data) if key then data.keysData = key -- в исходящей синхронизации подставляем свою кнопку key = nil -- чистим, чтобы клавиша не была зажата end if skey then data.specialKey = skey -- ( Y, N, H ) skey = nil end end -- то же самое, что и в функции выше, только еще и для авто function sampev.onSendVehicleSync(data) if key then data.keysData = key key = nil end if skey then data.specialKey = skey skey = nil end end -- помимо этого, keysData и specialKey есть в PassengerSyncData. А в SpectatorSyncData только keysData -- см. libs/samp/synchronization.lua
Lua:sendKey(1024) -- отправить alt sendSpecialKey(1) -- отправить Y
!key 1024
!skey 1
Lua:-- подключаемся к прокси после загрузки скрипта function onLoad() proxyConnect("123.145.167.189:58765", "user", "pass") end -- чтоб не подключался к серверу без прокси function onRequestConnect() if not isProxyConnected() then return false end end -- функция вызывается при ошибки function onProxyError() print("ошибка при подключении к прокси") end -- функция вызывается при успешном подключении function onProxyConnect() print("успешно подключились к прокси!") end
Фиксы:
WeaponFix
interpolate camera fix
ConnectFix
Connect Accepted Fix
HitFix
SendPing Fix
AimSync FIX
Slapfix
Другие
Follow ( липучка )
Route Recording SystemЕсть поддержка SOCKS5 прокси для смены IP программы. Для подключения достаточно создать скрипт и вписать в него одну строку. Более подробные примеры использования в разделе "FAQ по скриптингу"
Lua:proxyConnect("123.145.167.189:58765", "user", "pass")
В бесплатных прокси листах сложно найти что-то рабочее, так что советую платные проверенные сервисы ⬇️
proxy6.net - купон на 5% скидку: WQhFNHVGM3
proxys.io - купон на 5% скидку: raksamp ( много стран )
proxymania.ru - купон на 5% скидку: raksamp
proxyline.net - купон на 5% скидку: raksamp
⚠️ У сервисов есть возможность авторизации по IP, чтоб не вводить логин и пароль. Но не советую пользоваться этой функцией, в текущей версии есть проблема с авторизацией без пароля!exit/quit - выход из программы
!reconnect - перезаход
!reload - перезагрузить настройки
!players - вывести список игроков
!npcs - вывести список npc
!vehicles- вывести список авто
!pickups - вывести список пикапов
!labels - вывести список 3д текстов
!goto <id> - телепортироваться к игроку
!gotocp - телепортироваться к чекпоинту
!vdeath - отправить серверу уведомление о взрыве авто
!fu - отправить серверу потерю соединения
!menusel <n> - выбрать пункт в gta menu
!class <id> - отправить request class
!spawn - заспавниться
!reqspawn - отправить request spawn
!gm - переключить гм
!kill - убиться
!pickup <id> - подобрать пикап
!weapon <id> - установить оружие
!selveh <id> [seat] - установить авто. id 0 - нет авто, seat 0 - водительское
!setip <ip:port> - установить сервер
!setnick <nick> - установить ник
!pos <x> <y> <z> - установить позицию
!rot <angle> - установить угол
!coord <x> <y> <z> - курд мастер
!diagsend <button> <listbox> <input> - отправить ответ последнему диалогу
!scmevent <type> <param1> <param2> <param3> - отправить scm эвент
!fakekick - кикнуться с помощью невалидного авто
!seltd <id> - кликнуть по текстдраву
!autopick - переключить автовзятие пикапов
!reloadlua - перезагрузить Lua скрипты
beta версия от 15 мая 2024, список изменений