· 8 years ago · Apr 17, 2018, 08:06 PM
1local sql = {}
2sql.Connected = false
3sql.Queries = {}
4
5file.CreateDir("autodonate")
6local function log(text, silent)
7 text = text.."\n"
8 if !silent then Msg("[autodonate] "..text) end
9 file.Append(os.date("autodonate/%d-%m-%y.txt"), os.date("[%c] ")..text)
10end
11
12function sql:Log( text )
13 print( "[autodonate MySQL] " .. text )
14end
15
16function sql:Initialize()
17 require( "mysqloo" )
18
19 sql.Database = mysqloo.connect( auth.Host, auth.Username, auth.Password, auth.Database, auth.Port )
20
21 sql.Database.onConnected = function( )
22 self.Connected = true
23
24 self:Query( "CREATE TABLE IF NOT EXISTS ad_pdata ( infoid VARCHAR( 32 ) NOT NULL, value BLOB NULL, PRIMARY KEY ( infoid ) );" )
25 --for _, v in ipairs( sql.Queries ) do
26 while ( #sql.Queries > 0 ) do
27 local query = table.remove( sql.Queries, 1 )
28 self:Query( query[ 1 ], query[ 2 ], unpack( query[ 3 ] ) )
29 end
30
31 self:Log( "Connection succesful!" )
32 end
33
34 sql.Database.onConnectionFailed = function( db, err )
35 sql:Log( "Connection failed: " .. err )
36
37 self.Connected = false
38
39 timer.Simple( 10, function()
40 db:connect()
41 end )
42 end
43
44 self:Log( "Connecting to database..." )
45
46 sql.Database:connect()
47end
48
49function sql:Query( sql, callback, ... )
50 if ( self.Database and self.Connected ) then
51 local args = {}
52 for _, val in ipairs( { ... } ) do
53 val = tostring( val )
54 val = self.Database:escape( val )
55
56 table.insert( args, val )
57 end
58
59 local formatted = string.format( sql, unpack( args ) )
60 local query = self.Database:query( formatted )
61 query.onSuccess = function( _, data )
62 if ( callback ) then
63 callback( data )
64 end
65 end
66
67 query.onError = function( _, err )
68 if ( self.Database:status() == mysqloo.DATABASE_NOT_CONNECTED ) then
69 table.insert( self.Queries, { formatted, callback, {} } )
70 self.Database:connect()
71
72 self.Connected = false
73
74 self:Log( "Lost connection to server, reconnecting..." )
75 else
76 self:Log( "Query failed: " .. err )
77 end
78 end
79
80 query:start()
81 else
82 self:Log( "Query attempted while disconnected." )
83 table.insert( self.Queries, { sql, callback, { ... } } )
84 end
85end
86
87function sql:GetPData(steamid, callback)
88 if !isstring(steamid) then steamid = steamid:SteamID64() end
89 self:Query( "SELECT value FROM ad_pdata WHERE infoid = '%s' LIMIT 1", callback, steamid )
90end
91
92local function retrySet(steamid, value)
93 sql:GetPData(steamid, function(a)
94 if !a or !a[1] or !a[1].value or a[1].value != value then
95 timer.Simple(0.5, function()
96 sql:SetPData(steamid, value)
97 retrySet(steamid, value)
98 end)
99 end
100 end)
101end
102
103function sql:SetPData( steamid, value )
104 if !isstring(steamid) then steamid = steamid:SteamID64() end
105 self:Query( "REPLACE INTO ad_pdata ( infoid, value ) VALUES ( '%s', '%s' )", nil, steamid, value )
106 timer.Simple(0.1, function() retrySet(steamid, value) end)
107end
108
109
110sql:Initialize()
111
112ad.sql = sql
113
114------------------------------------------------------------
115
116local md5 = include("ad_md5.lua")
117
118local red = Color(255, 0, 0)
119local function onError(reason, sid, code)
120 reason = "\n============AUTODONATE ERROR============\n "..reason.."\n========================================\n SteamID: "..sid.." || code: "..code.."\n========================================\n\n"
121 log(reason, true)
122 MsgC(red, reason, color_white)
123end
124
125local function processData(statuscode, body, sid, code)
126 if statuscode != 200 then return onError("код ответа равен не 200 ("..statuscode..")", sid, code) end
127 body = util.JSONToTable(body)
128 if !body then return onError("ответ пришел не в JSON формате", sid, code) end
129 if body.retval != "0" then return onError("Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ выполнен!\n код: "..body.retval.."\n опиÑание ошибки: "..body.retdesc, sid, code) end
130 if util.SteamIDFrom64(sid) == "STEAM_0:0:0" then return onError("SteamID неверен!", sid, code) end
131
132 body.inv = tostring(body.inv)
133
134 if file.Read("autodonate/invkeys.txt"):find(body.inv) then return log("Ignoring duped inv key "..body.inv) end
135
136 local val = math.floor(tonumber(body.cnt_goods:Replace(",", ".")))
137 if !val then return onError("JOPA ERROR CNT_GOODS IS NIL:\n"..util.TableToJSON(body, true), sid, code) end
138 log(sid.." byed "..val.."p")
139
140 local ply = player.GetBySteamID64(sid)
141 if ply then
142 ad.add(ply, val, true)
143 else
144 log(sid.." is offline, adding "..val.."p to MySQL")
145 sql:GetPData(sid, function(a)
146 local data = util.JSONToTable(a and a[1] and a[1].value or "{\"balance\":0}")
147 data.balance = data.balance+val
148 sql:SetPData(sid, util.TableToJSON(data))
149 end)
150 end
151
152 file.Append("autodonate/invkeys.txt", body.inv.."\n")
153 log("writed inv key "..body.inv)
154end
155
156local b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
157
158function dec(data)
159 data = string.gsub(data, '[^'..b..'=]', '')
160 return (data:gsub('.', function(x)
161 if (x == '=') then return '' end
162 local r,f='',(b:find(x)-1)
163 for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
164 return r;
165 end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
166 if (#x ~= 8) then return '' end
167 local c=0
168 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
169 return string.char(c)
170 end))
171end
172
173concommand.Add("ad_go", function(ply, _, req)
174 if IsValid(ply) then return ply:Kick("net") end
175 req = dec(req[1]):Split("&"..ad.config.param.."=")
176
177 if !req[1] or !req[2] then
178 log("wrong request: "..tostring(req[1]).." sid: "..tostring(req[2]))
179 return
180 end
181
182 local code, sid = req[1], req[2]
183 log("sending POST: code: ["..code.."] sid: ["..sid.."]")
184 HTTP{
185 method = "POST",
186 url = "http://shop.digiseller.ru/xml/check_unique_code.asp",
187 type = "application/json",
188 body = [[{"id_seller":"]]..id_seller..[[","unique_code":"]]..code..[[","sign":"]]..md5.sumhexa(id_seller..":"..code..":"..password)..[["}]],
189 failed = function(reason) onError(reason, sid, code) end,
190 success = function(status, body) processData(status, body, sid, code) end
191 }
192end)
193
194concommand.Add("ad_retry",function(ply, _, arg)
195 if IsValid(ply) then return end
196 local code, sid = arg[1], arg[2]
197 if !code or !sid then return print("ad_retry wrong arguments") end
198 log("sending POST: code: "..code.." sid: "..sid)
199 HTTP{
200 method = "POST",
201 url = "http://shop.digiseller.ru/xml/check_unique_code.asp",
202 type = "application/json",
203 body = [[{"id_seller":"]]..id_seller..[[","unique_code":"]]..code..[[","sign":"]]..md5.sumhexa(id_seller..":"..code..":"..password)..[["}]],
204 failed = function(reason) onError(reason, sid, code) end,
205 success = function(status, body) processData(status, body, sid, code) end
206 }
207end)
208
209------------------------------------------------------
210local sip = "autodonate_error_what"
211timer.Simple(1, function()
212 sip = game.GetIPAddress()
213 ad.sip = sip
214 if sip == "0.0.0.0:27015" then
215 http.Fetch("https://api.ipify.org", function(ip)
216 sip = ip..":27015"
217 ad.sip = sip
218 end)
219 end
220end)
221
222util.AddNetworkString("autodonate")
223
224local function update(ply)
225 local tbl = {balance = ply.ad.balance}
226 if ply.ad[sip] then
227 tbl.group = ply.ad[sip].group
228 tbl.group_expires = ply.ad[sip].group_expires
229 end
230 timer.Simple(5, function()
231 net.Start("autodonate")
232 net.WriteTable(tbl)
233 net.WriteString(ply:GetPData("ad_weapons", ""))
234 net.WriteBool(false)
235 net.Send(ply)
236 end)
237end
238
239function ad.save(ply, upd)
240 sql:SetPData(ply, util.TableToJSON(ply.ad))
241 if upd then update(ply) end
242end
243
244function ad.set(ply, value)
245 ply = isentity(ply) and ply or false
246 assert(ply, "игрок не на Ñервере!")
247 log("изменение Ñчёта "..ply:SteamID64()..", было "..ply.ad.balance.."p, Ñтало: "..value, true)
248 ply.ad.balance = value
249 ad.save(ply, true)
250end
251
252
253function ad.add(ply, amount, msg)
254 ply = isentity(ply) and ply or false
255 assert(ply, "игрок не на Ñервере!")
256 ad.set(ply, ply.ad.balance+amount)
257 log(ply:SteamID64().." пополнил Ñчет на "..amount.."Ñ€, Ñчет равен "..ply.ad.balance.."p", true)
258 if msg then ad.msg(ply, "Ð’Ñ‹ пополнили Ñчет на "..amount.."Ñ€") end
259end
260
261function ad.msg(ply, msg, err)
262 local tbl = {balance = ply.ad.balance}
263 if ply.ad[sip] then
264 tbl.group = ply.ad[sip].group
265 tbl.group_expires = ply.ad[sip].group_expires
266 end
267 net.Start("autodonate")
268 net.WriteTable(tbl)
269 net.WriteString(ply:GetPData("ad_weapons", ""))
270 net.WriteBool(true)
271 net.WriteString(msg)
272 net.WriteBool(err)
273 net.Send(ply)
274end
275
276function ad.removeGroup(sid)
277 if !isstring(sid) then sid = sid:SteamID64() end
278 sql:GetPData(sid, function(a)
279 local data = util.JSONToTable(a and a[1] and a[1].value or "{\"balance\":0}")
280 data[sip] = nil
281 sql:SetPData(sid, util.TableToJSON(data))
282 end)
283end
284
285local function timedGroup(ply, group)
286 RunConsoleCommand('ba', 'setgroup', ply:SteamID(), group)
287 ply.ad[sip] = {}
288 ply.ad[sip].group = group
289 ply.ad[sip].group_expires = os.time()+2591999
290end
291
292
293hook.Add("PlayerInitialSpawn", "autodonate", function(ply)
294 if !ply:IsBot() then
295 sql:GetPData(ply, function(a)
296 ply.ad = util.JSONToTable(a and a[1] and a[1].value or "{\"balance\":0}")
297 update(ply)
298 end)
299 end
300end)
301
302timer.Create("ad.checkGroups", 200, 0, function()
303 for _, ply in pairs(player.GetAll()) do
304 if !ply:IsBot() and ply.ad and ply.ad[sip] and ply.ad[sip].group:gsub("_inf", "") != ply:GetUserGroup() then
305 ply.ad[sip] = nil
306 update(ply)
307 elseif !ply:IsBot() and ply.ad and ply.ad[sip] and ply.ad[sip].group_expires and ply.ad[sip].group_expires-os.time() <= 0 then
308 if ply.ad[sip].group == ply:GetUserGroup() then
309 log(ply:SteamID64().." был ÑнÑÑ‚ Ñ Ð¿Ñ€Ð¸Ð²ÐµÐ»ÐµÐ½Ð¸Ð¸ "..ply.ad[sip].group, true)
310 RunConsoleCommand('ba', 'setgroup', ply:SteamID(), "user")
311 ad.msg(ply, "МеÑÑц прошёл - донат группа ÑнÑта")
312 end
313 ply.ad[sip] = nil
314 if ply.ad.balance == 0 then
315 sql:Query( "DELETE FROM ad_pdata WHERE infoid = '%s'", nil, ply:SteamID64())
316 else ad.save(ply) end
317 update(ply)
318 end
319 end
320end)
321
322hook.Add("PostPlayerDeath", "ad_death", function(ply)
323 ply.ad_weps = nil
324 ply.ad_jmp = nil
325 ply.ad_hp = nil
326 ply.ad_ar = nil
327end)
328
329concommand.Add("ad_get", function(ply, _, arg)
330
331 arg = arg[1]
332 if table.HasValue(ply:GetPData("ad_weapons", ""):Split(" "), arg) then
333 ply.ad_weps = ply.ad_weps or {}
334
335 if arg == "_hp" and !ply.ad_hp then
336 ply.ad_hp = true
337 ply:SetHealth(250)
338 ad.msg(ply, "Ð’Ñ‹ взÑли 250 хп")
339 elseif arg == "_ar" and !ply.ad_ar then
340 ply.ad_ar = true
341 ply:SetArmor(228)
342 ad.msg(ply, "Ð’Ñ‹ взÑли 228 брони")
343 elseif arg == "_jmp" and !ply.ad_jmp then
344 ply.ad_jmp = true
345 ply:SetJumpPower(ply:GetJumpPower()*2)
346 ad.msg(ply, "Вы активировали двойной прыжок")
347 elseif !ply.ad_weps[arg] then
348 ply:Give(arg)
349 ply.ad_weps[arg] = true
350 ad.msg(ply, "Ð’Ñ‹ взÑли "..arg)
351 end
352 end
353end)
354
355concommand.Add("ad_buywep", function(ply, _, arg)
356 arg = arg[1]
357 for _, v in pairs(ad.categories) do
358 local try = v[arg]
359 if try then
360 if ply.ad.balance < try.p then
361 log(ply:SteamID64().." попыталÑÑ Ð½Ð°ÐµÐ±Ð°Ñ‚ÑŒ донат (PLUS) => Что: "..arg..", Ð‘Ð°Ð»Ð°Ð½Ñ Ð¸Ð³Ñ€Ð¾ÐºÐ°:"..(ply.ad.balance.."p")..", Цена предмета: "..try.p)
362 return ply:Kick("siebalsa")
363 end
364 if os.time()-(ply.ad_lastByed or 0) < 5 then return ad.msg(ply, "Подождите 5 Ñекунд перед покупкой Ñледующего предмета", true) end
365
366 log(ply:SteamID64().." купил(PLUS) "..arg.." за "..try.p.."p", true)
367 local err, msg = try.func(ply)
368 ad.msg(ply, msg)
369 if err then
370 ply.ad_lastByed = os.time()
371 return
372 end
373 ad.set(ply, ply.ad.balance-try.p)
374 ply.ad_lastByed = os.time()
375 end
376 end
377end)
378
379concommand.Add("autodonate_buy", function(ply, _, arg)
380 arg = arg[1]
381 local try = ad.config.items[arg]
382 if !try or ply.ad.balance < try.p or (ply.ad[sip] and ply.ad[sip].group == try) then
383 log(ply:SteamID64().." попыталÑÑ Ð½Ð°ÐµÐ±Ð°Ñ‚ÑŒ донат => Что: "..arg..", Ð‘Ð°Ð»Ð°Ð½Ñ Ð¸Ð³Ñ€Ð¾ÐºÐ°:"..(ply.ad.balance.."p")..(try and ", Цена предмета: "..try.p or "").." ещё инфа: "..(ply.ad[sip] and ply.ad[sip].group == try and " уже имеет Ñту привилегию" or "нет") , true)
384 return ply:Kick("siebalsa")
385 end
386 if os.time()-(ply.ad_lastByed or 0) < 5 then return ad.msg(ply, "Подождите 5 Ñекунд перед покупкой Ñледующего предмета", true) end
387
388 log(ply:SteamID64().." купил "..arg.." за "..try.p.."p", true)
389 if try.func then
390 local err, msg = try.func(ply)
391 ad.msg(ply, msg)
392 if err then
393 ply.ad_lastByed = os.time()
394 return
395 end
396 else
397 timedGroup(ply, arg)
398 ad.msg(ply, "Ð’Ñ‹ уÑпешно купили привилегию "..try[1].." за "..try.p.."Ñ€")
399 end
400 ad.set(ply, ply.ad.balance-try.p)
401 ply.ad_lastByed = os.time()
402end)