Browse Source

kamazai init

master
gsd 3 days ago
parent
commit
f138e52a96
  1. 2
      Dockerfile
  2. 267
      other_ext/kamaz_ai.py
  3. 9
      other_ext/webhook_helper.py

2
Dockerfile

@ -1,5 +1,5 @@
FROM python:3.10
RUN python -m pip install git+https://github.com/Rapptz/discord.py aiohttp websocket-client
RUN python -m pip install git+https://github.com/Rapptz/discord.py aiohttp websocket-client numpy
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY ./ ./

267
other_ext/kamaz_ai.py

@ -0,0 +1,267 @@
from discord.ext import tasks
import discord
import traceback
import asyncio
import aiohttp
import os
import numpy as np
from collections import Counter
#ai sloop
class KamazAI:
WEIGHTS = {
'steam_identity': 0.4,
'reason': 0.2,
'permissions': 0.1,
'numerics': 0.2,
'server': 0.1
}
K_NEIGHBORS = 5
'''
{
"id":2,
"a_nickname":"Роботяга",
"a_permition":"DONT_HAVE",
"a_kills":6,
"a_deads":5,
"a_seconds":392,
"r_nickname":"zombiskell",
"r_permition":"DONT_HAVE",
"r_kills":10,
"r_deads":10,
"r_seconds":2022,
"reasons":"Читы",
"utime":1717502378,
"srv":"srv9",
"online":18,
"type":"IN_GAME",
"actions":["inspect"],
"serverName":"Норильск 2019",
"a_steam":{
"steam3":"[U:1:1338527208]",
"steam2":"STEAM_0:0:669263604",
"steam64":"76561199298792936",
"community_url":"https://steamcommunity.com/profiles/76561199298792936",
"account_id":1338527208
},
"r_steam":{
"steam3":"[U:1:1554861952]",
"steam2":"STEAM_0:0:777430976",
"steam64":"76561199515127680",
"community_url":"https://steamcommunity.com/profiles/76561199515127680",
"account_id":1554861952
}
}
'''
def __init__(self, reports_list):
self.historical_reports = reports_list
self.report_actions = {}
for report in reports_list:
self.report_actions[report["id"]] = report["actions"]
self.numeric_stats = self.normalize_numerics(reports_list)
'''
def init_data(reports: list, actions: list):
"""Загрузка данных при старте или через специальный endpoint."""
global historical_reports, report_actions
historical_reports = reports
report_actions.clear()
for act in actions:
rid = act['report_id']
report_actions.setdefault(rid, []).append(act['action'])
'''
def normalize_numerics(self, reports):
"""Вычисляет min/max для всех числовых полей (обучающая выборка)."""
fields = ['a_kills', 'a_deads', 'a_seconds', 'r_kills', 'r_deads', 'r_seconds', 'online']
stats = {}
for f in fields:
values = [r[f] for r in reports if r[f] is not None]
stats[f] = {'min': min(values), 'max': max(values)}
return stats
async def similarity(self, report1, report2, num_stats):
"""Возвращает взвешенное сходство (0..1)."""
score = 0.0
# 1. Совпадение пары Steam-аккаунтов
if False:
identity_score = 0.0
if report1['a_steam2'] == report2['a_steam2'] and report1['r_steam2'] == report2['r_steam2']:
identity_score = 1.0
elif report1['a_steam2'] == report2['a_steam2'] or report1['r_steam2'] == report2['r_steam2']:
identity_score = 0.5
score += self.WEIGHTS['steam_identity'] * identity_score
# 2. Причина (точное совпадение)
reason_score = 1.0 if report1['reasons'] == report2['reasons'] else 0.0
score += self.WEIGHTS['reason'] * reason_score
# 3. Права
perm_score = 0.0
if report1['a_permition'] == report2['a_permition']:
perm_score += 0.5
if report1['r_permition'] == report2['r_permition']:
perm_score += 0.5
score += self.WEIGHTS['permissions'] * perm_score
# 4. Числовые поля (нормированное евклидово расстояние -> сходство)
num_fields = ['a_kills', 'a_deads', 'a_seconds', 'r_kills', 'r_deads', 'r_seconds', 'online']
dist_sq = 0.0
for f in num_fields:
min_val = num_stats[f]['min']
max_val = num_stats[f]['max']
if max_val == min_val:
norm_diff = 0.0
else:
try:
norm_diff = (report1[f] - report2[f]) / (max_val - min_val)
except:
norm_diff = 0.0
dist_sq += norm_diff ** 2
eucl_dist = np.sqrt(dist_sq / len(num_fields))
# Превращаем расстояние в сходство (1 - нормализованное расстояние)
num_similarity = 1.0 - min(eucl_dist, 1.0)
score += self.WEIGHTS['numerics'] * num_similarity
# 5. Сервер
if False:
srv_score = 1.0 if report1['srv'] == report2['srv'] else 0.0
score += self.WEIGHTS['server'] * srv_score
return score
async def predict(self, new_report):
# Вычисляем сходство со всеми историческими заявками
similarities = []
for hist in self.historical_reports:
sim = await self.similarity(new_report, hist, self.numeric_stats)
similarities.append((hist['id'], sim))
# Сортируем по убыванию сходства
similarities.sort(key=lambda x: x[1], reverse=True)
top_k = similarities[:self.K_NEIGHBORS]
#print(top_k)
# Собираем все действия, назначенные на эти заявки
action_counter = Counter()
similar_report_ids = []
for rid, sim in top_k:
#print(rid, sim)
if sim > 0: # можно задать порог, чтобы отсечь шум
similar_report_ids.append(rid)
if rid in self.report_actions:
for act in self.report_actions[rid]:
action_counter[act] += 1
total = sum(action_counter.values())
suggestions = []
if total > 0:
for action, count in action_counter.most_common():
suggestions.append({
'action': action,
'confidence': round(count / total, 4)
})
else:
# Если ни одного похожего – предложение "inspect" по умолчанию
suggestions.append({'action': 'inspect', 'confidence': 1.0})
return {
'suggestions': suggestions,
'similar_reports': similar_report_ids
}
class Extension:
core = None
def __init__(self, core):
self.core = core
self.kamazai = None
self.reports_list = []
async def task(self, timeout = 15):
if os.getenv('BACKEND_URL') and os.getenv("BACKEND_SECRETKEY"):
pass
else:
print("Cannot init kamazAI, BACKEND_URL or BACKEND_SECRETKEY is missing in env")
return
await self.core.wait_until_ready()
while True:
await self.updater()
await asyncio.sleep(timeout)
async def updater(self):
try:
if self.kamazai == None:
print("Sync report list")
async with aiohttp.ClientSession(cookies={
"secretkey":os.getenv("BACKEND_SECRETKEY")}) as session:
async with session.post(f"{os.getenv('BACKEND_URL')}/api/discord/report/s", ssl = False) as response:
self.reports_list = await response.json()
self.kamazai = KamazAI(self.reports_list)
print("KamazAI Enabled")
except:
traceback.print_exc()
async def __call__(self, message: discord.Message = None):
if self.kamazai == None:
print("call kamazAi but he is not init")
return await message.reply(content=f'KamazAI не иницилизирован')
try:
report_id = message.embeds[0].color.value
except:
return await message.reply(content=f'KamazAI не может получить индификатор репорта')
async with aiohttp.ClientSession(cookies={
"secretkey":os.getenv("BACKEND_SECRETKEY")}) as session:
async with session.post(f"{os.getenv('BACKEND_URL')}/api/discord/report/{report_id}", ssl = False) as response:
report = await response.json()
result = await self.kamazai.predict(report)
suggestions = result.get("suggestions", [])
s = []
for ss in suggestions:
s.append(f'{ss["action"]} c вероятностью {ss['confidence']}')
content = f'KamazAI решил что с этим репортом надо сделать: ' + ",".join(s) + "\nОцените решение камаза где :thumbsup: - норм или :thumbsdown: - не очень"
response = await message.reply(content=content)
try:
await response.add_reaction(':thumbsup:')
await response.add_reaction(':thumbsdown:')
except:
pass
return response
#self.reports_list.append(report)
#self.kamazai = KamazAI(self.reports_list)
if __name__ == "__main__":
async def run():
print("run")
from json import load
with open("/Users/gsd/Downloads/reports.json", "r", encoding="utf8") as report_list:
kamazAi = KamazAI(load(report_list))
test_report1 = {"id":23059,"a_nickname":"серийный чувак","a_permition":"DONT_HAVE","a_kills":29,"a_deads":38,"a_seconds":3237,"r_nickname":"Корабль Бомж 1","r_permition":"DONT_HAVE","r_kills":44,"r_deads":31,"r_seconds":3598,"reasons":"Читы","utime":1780842262,"srv":"srv9","online":18,"type":"IN_GAME","actions":[],"serverName":"Норильск 2019","a_steam":{"steam3":"[U:1:1675616295]","steam2":"STEAM_0:1:837808147","steam64":"76561199635882023","community_url":"https://steamcommunity.com/profiles/76561199635882023","account_id":1675616295},"r_steam":{"steam3":"[U:1:1546493291]","steam2":"STEAM_0:1:773246645","steam64":"76561199506759019","community_url":"https://steamcommunity.com/profiles/76561199506759019","account_id":1546493291}}
test_report2 = {"id":23048,"a_nickname":"Евгений Задротов","a_permition":"FREE","a_kills":63,"a_deads":68,"a_seconds":5031,"r_nickname":"Kapusta_KvSH","r_permition":"VIP","r_kills":11,"r_deads":2,"r_seconds":436,"reasons":"VIP абуз","utime":1780837315,"srv":"srv5","online":21,"type":"IN_GAME","actions":[],"serverName":"Завод Ultimate","a_steam":{"steam3":"[U:1:1623272121]","steam2":"STEAM_0:1:811636060","steam64":"76561199583537849","community_url":"https://steamcommunity.com/profiles/76561199583537849","account_id":1623272121},"r_steam":{"steam3":"[U:1:196806785]","steam2":"STEAM_0:1:98403392","steam64":"76561198157072513","community_url":"https://steamcommunity.com/profiles/76561198157072513","account_id":196806785}}
print(test_report1)
print(await kamazAi.predict(test_report1))
print()
print(test_report2)
print(await kamazAi.predict(test_report2))
import asyncio
asyncio.run(run())

9
other_ext/webhook_helper.py

@ -57,6 +57,15 @@ class Extension:
await message.add_reaction(emoji)
except Exception as err:
print(f"Cannot add reaction on webhook, error: {err}")
try:
#kamazai
kwargs = {'message': message}
await self.core.loaded_extensions['kamaz_ai'](**kwargs)
except:
print("Cannot call kamazAI")
traceback.print_exc()
return
@core.listen()

Loading…
Cancel
Save