local ffi = require("ffi")
local bit = require("bit")
local vkeys = require("vkeys")
ffi.cdef[[
typedef uintptr_t SOCKET;
typedef struct WSADATA {
uint16_t wVersion, wHighVersion;
char szDescription[257], szSystemStatus[129];
unsigned short iMaxSockets, iMaxUdpDg;
char *lpVendorInfo;
} WSADATA;
typedef struct sockaddr_in {
short sin_family;
unsigned short sin_port;
struct { unsigned long s_addr; } sin_addr;
char sin_zero[8];
} sockaddr_in;
typedef struct timeval {
long tv_sec;
long tv_usec;
} timeval;
typedef struct fd_set {
unsigned int fd_count;
SOCKET fd_array[64];
} fd_set;
int WSAStartup(uint16_t wVer, WSADATA *lpWSAData);
SOCKET socket(int af, int type, int protocol);
int connect(SOCKET s, const struct sockaddr *name, int namelen);
int ioctlsocket(SOCKET s, long cmd, unsigned long *argp);
int send(SOCKET s, const char *buf, int len, int flags);
int recv(SOCKET s, char *buf, int len, int flags);
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const timeval *timeout);
int WSAGetLastError();
unsigned short htons(unsigned short hostshort);
unsigned long inet_addr(const char *cp);
]]
local ws2 = ffi.load("ws2_32")
local FIONBIO = 0x8004667e
local WSAEISCONN = 10056
local function build_ws_frame(payload)
local len = #payload
local head = {0x81}
if len <= 125 then
table.insert(head, bit.bor(len, 0x80))
elseif len < 65536 then
table.insert(head, bit.bor(126, 0x80))
table.insert(head, bit.rshift(len, 8))
table.insert(head, bit.band(len, 0xFF))
end
local mask = {0x61, 0x72, 0x7A, 0x21}
for i=1,4 do table.insert(head, mask[i]) end
local out = {string.char(unpack(head))}
for i=1, len do
table.insert(out, string.char(bit.bxor(payload:sub(i,i):byte(), mask[(i-1)%4+1])))
end
return table.concat(out)
end
local OBS = { sock = nil, state = "IDLE" }
function OBS:connect(ip, port)
local wsa = ffi.new("WSADATA")
ws2.WSAStartup(0x0202, wsa)
self.sock = ws2.socket(2, 1, 6)
local mode = ffi.new("unsigned long[1]", 1)
ws2.ioctlsocket(self.sock, FIONBIO, mode)
local addr = ffi.new("sockaddr_in")
addr.sin_family = 2
addr.sin_port = ws2.htons(port)
addr.sin_addr.s_addr = ws2.inet_addr(ip)
ws2.connect(self.sock, ffi.cast("struct sockaddr*", addr), ffi.sizeof(addr))
self.state = "CONNECTING"
print("[OBS] Подключение к " .. ip .. ":" .. port)
end
function OBS:SendRaw(payload)
if self.sock and self.state == "READY" then
local frame = build_ws_frame(payload)
ws2.send(self.sock, frame, #frame, 0)
else
print("[OBS] Ошибка: Соединение еще не готово")
end
end
function OBS:update()
if not self.sock then return end
if self.state == "CONNECTING" then
local fds = ffi.new("fd_set")
fds.fd_count = 1
fds.fd_array[0] = self.sock
local tv = ffi.new("timeval", {0, 0})
if ws2.select(0, nil, fds, nil, tv) > 0 then
local key = "dGhlIHNhbXBsZSBub25jZQ=="
local req = "GET / HTTP/1.1\r\nHost: localhost\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: "..key.."\r\nSec-WebSocket-Version: 13\r\n\r\n"
ws2.send(self.sock, req, #req, 0)
self.state = "HANDSHAKE_SENT"
print("[OBS] TCP OK. Handshake отправлен.")
end
return
end
local fds = ffi.new("fd_set")
fds.fd_count = 1
fds.fd_array[0] = self.sock
local tv = ffi.new("timeval", {0, 0})
if ws2.select(0, fds, nil, nil, tv) > 0 then
local buf = ffi.new("char[8192]")
local len = ws2.recv(self.sock, buf, 8192, 0)
if len > 0 then
local raw = ffi.string(buf, len)
if self.state == "HANDSHAKE_SENT" then
if raw:find("101") or raw:find("Upgrade") then
self.state = "WAIT_HELLO"
print("[OBS] Handshake принят сервером.")
if raw:find('"op":0') then
self:identify()
end
end
elseif self.state == "WAIT_HELLO" then
if raw:find('"op":0') then
self:identify()
end
end
elseif len == 0 then
self.state = "IDLE"
print("[OBS] Соединение закрыто.")
end
end
end
function OBS:identify()
local id_msg = [[{"op":1,"d":{"rpcVersion":1}}]]
local frame = build_ws_frame(id_msg)
ws2.send(self.sock, frame, #frame, 0)
self.state = "READY"
print("{00FF00}[OBS] Авторизация пройдена!")
end
function main()
while not isSampAvailable() do wait(100) end
OBS:connect("127.0.0.1", 4455)
while true do wait(0)
OBS:update()
if isKeyJustPressed(vkeys.VK_NUMPAD1) then
print("[OBS] Запрос: Старт записи")
OBS:SendRaw('{"op":6,"d":{"requestType":"StartRecord","requestId":"manual_start"}}')
end
if isKeyJustPressed(vkeys.VK_NUMPAD2) then
print("[OBS] Запрос: Стоп записи")
OBS:SendRaw('{"op":6,"d":{"requestType":"StopRecord","requestId":"manual_stop"}}')
end
end
wait(-1)
end