Kekik 1.7.2__py3-none-any.whl → 1.7.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Kekik/Sifreleme/Packer.py +158 -21
- Kekik/cache.py +250 -65
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info}/METADATA +29 -2
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info}/RECORD +8 -8
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info}/WHEEL +1 -1
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info}/entry_points.txt +0 -0
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info/licenses}/LICENSE +0 -0
- {Kekik-1.7.2.dist-info → kekik-1.7.9.dist-info}/top_level.txt +0 -0
Kekik/Sifreleme/Packer.py
CHANGED
|
@@ -4,9 +4,49 @@ import re
|
|
|
4
4
|
|
|
5
5
|
class Packer:
|
|
6
6
|
"""
|
|
7
|
-
P.A.C.K.E.R. sıkıştırma ve çözme işlemleri için bir sınıf.
|
|
7
|
+
P.A.C.K.E.R. sıkıştırma ve çözme işlemleri için kapsamlı bir sınıf.
|
|
8
8
|
! » https://github.com/beautifier/js-beautify/blob/main/python/jsbeautifier/unpackers/packer.py
|
|
9
9
|
"""
|
|
10
|
+
|
|
11
|
+
# Regex kalıpları - daha gevşek, farklı varyasyonları yakalayabilecek şekilde
|
|
12
|
+
PACKED_PATTERN = re.compile(
|
|
13
|
+
r"\}\s*\(\s*['\"](.*?)['\"],\s*(\d+),\s*(\d+),\s*['\"](.+?)['\"]\.split\(['\"]\\?\|['\"]\)",
|
|
14
|
+
re.IGNORECASE | re.MULTILINE | re.DOTALL
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Alternatif regex pattern, farklı formatlarda paketlenmiş kodu yakalamak için
|
|
18
|
+
ALTERNATIVE_PATTERNS = [
|
|
19
|
+
# Standart pattern
|
|
20
|
+
re.compile(
|
|
21
|
+
r"\}\('(.*)',\s*(\d+),\s*(\d+),\s*'(.*?)'\.split\('\|'\)",
|
|
22
|
+
re.IGNORECASE | re.MULTILINE | re.DOTALL
|
|
23
|
+
),
|
|
24
|
+
# Daha gevşek pattern
|
|
25
|
+
re.compile(
|
|
26
|
+
r"\}\s*\(\s*['\"](.*?)['\"],\s*(\d+),\s*(\d+),\s*['\"](.+?)['\"]\.split\(['\"]\\?\|['\"]\)",
|
|
27
|
+
re.IGNORECASE | re.MULTILINE | re.DOTALL
|
|
28
|
+
),
|
|
29
|
+
# Eval formatı
|
|
30
|
+
re.compile(
|
|
31
|
+
r"eval\(function\(p,a,c,k,e,(?:r|d|)\)\{.*?return p\}(.*?\.split\('\|'\))",
|
|
32
|
+
re.IGNORECASE | re.MULTILINE | re.DOTALL
|
|
33
|
+
)
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Kelime değiştirme deseni
|
|
37
|
+
REPLACE_PATTERN = re.compile(
|
|
38
|
+
r"\b\w+\b",
|
|
39
|
+
re.IGNORECASE | re.MULTILINE
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Alfabeler
|
|
43
|
+
ALPHABET = {
|
|
44
|
+
52: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP",
|
|
45
|
+
54: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR",
|
|
46
|
+
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
47
|
+
95: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
|
|
48
|
+
}
|
|
49
|
+
|
|
10
50
|
@staticmethod
|
|
11
51
|
def clean_escape_sequences(source: str) -> str:
|
|
12
52
|
"""Kaçış dizilerini temizler."""
|
|
@@ -18,45 +58,142 @@ class Packer:
|
|
|
18
58
|
@staticmethod
|
|
19
59
|
def extract_arguments(source: str) -> tuple[str, list[str], int, int]:
|
|
20
60
|
"""P.A.C.K.E.R. formatındaki kaynak koddan argümanları çıkarır."""
|
|
21
|
-
|
|
61
|
+
# Önce standart pattern ile dene
|
|
62
|
+
match = Packer.PACKED_PATTERN.search(source)
|
|
63
|
+
|
|
64
|
+
# Eğer bulunamazsa, alternatif pattern'leri dene
|
|
65
|
+
if not match:
|
|
66
|
+
for pattern in Packer.ALTERNATIVE_PATTERNS:
|
|
67
|
+
match = pattern.search(source)
|
|
68
|
+
if match:
|
|
69
|
+
break
|
|
22
70
|
|
|
23
71
|
if not match:
|
|
24
|
-
|
|
72
|
+
# Son çare: daha serbest bir string arama
|
|
73
|
+
if "'.split('|')" in source or '".split("|")' in source:
|
|
74
|
+
# Manuel olarak parçalama işlemi yap
|
|
75
|
+
try:
|
|
76
|
+
# Basit bir yaklaşım, çoğu vakada çalışır
|
|
77
|
+
parts = re.findall(r"\((['\"](.*?)['\"],\s*(\d+),\s*(\d+),\s*['\"](.*?)['\"]\.split", source)
|
|
78
|
+
if parts:
|
|
79
|
+
payload, radix, count, symtab = parts[0][1:]
|
|
80
|
+
return payload, symtab.split("|"), int(radix), int(count)
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
raise ValueError("Invalid P.A.C.K.E.R. source format. Pattern not found.")
|
|
25
85
|
|
|
26
|
-
|
|
86
|
+
# Eval formatını işle
|
|
87
|
+
if len(match.groups()) == 1:
|
|
88
|
+
# Eval formatı yakalandı, içeriği çıkar
|
|
89
|
+
eval_content = match.group(1)
|
|
90
|
+
if inner_match := re.search(r"\('(.*)',(\d+),(\d+),'(.*)'\)", eval_content):
|
|
91
|
+
payload, radix, count, symtab = inner_match.groups()
|
|
92
|
+
else:
|
|
93
|
+
raise ValueError("Cannot extract arguments from eval pattern")
|
|
94
|
+
else:
|
|
95
|
+
# Standart format yakalandı
|
|
96
|
+
payload, radix, count, symtab = match.groups()
|
|
27
97
|
|
|
28
98
|
return payload, symtab.split("|"), int(radix), int(count)
|
|
29
99
|
|
|
30
100
|
@staticmethod
|
|
31
|
-
def
|
|
32
|
-
"""
|
|
33
|
-
|
|
101
|
+
def unbase(value: str, base: int) -> int:
|
|
102
|
+
"""
|
|
103
|
+
Verilen değeri belirtilen tabandan ondalık sayıya dönüştürür.
|
|
104
|
+
Geniş taban desteği (2-95 arası) sağlar.
|
|
105
|
+
"""
|
|
106
|
+
# Standart Python taban dönüşümü (2-36 arası)
|
|
107
|
+
if 2 <= base <= 36:
|
|
108
|
+
try:
|
|
109
|
+
return int(value, base)
|
|
110
|
+
except ValueError:
|
|
111
|
+
return 0
|
|
34
112
|
|
|
35
|
-
|
|
113
|
+
# Geniş taban desteği (37-95 arası)
|
|
114
|
+
if base > 95:
|
|
115
|
+
raise ValueError(f"Desteklenmeyen taban: {base}")
|
|
36
116
|
|
|
117
|
+
# Uygun alfabeyi seç
|
|
118
|
+
if base > 62:
|
|
119
|
+
selector = 95
|
|
120
|
+
elif base > 54:
|
|
121
|
+
selector = 62
|
|
122
|
+
elif base > 52:
|
|
123
|
+
selector = 54
|
|
124
|
+
else:
|
|
125
|
+
selector = 52
|
|
126
|
+
|
|
127
|
+
# Alfabeden karakter-indeks sözlüğü oluştur
|
|
128
|
+
char_dict = {char: idx for idx, char in enumerate(Packer.ALPHABET[selector])}
|
|
129
|
+
|
|
130
|
+
# Değeri dönüştür
|
|
131
|
+
result = 0
|
|
132
|
+
for index, char in enumerate(reversed(value)):
|
|
133
|
+
digit = char_dict.get(char, 0)
|
|
134
|
+
result += digit * (base ** index)
|
|
135
|
+
|
|
136
|
+
return result
|
|
137
|
+
|
|
37
138
|
@staticmethod
|
|
38
139
|
def lookup_symbol(match: re.Match, symtab: list[str], radix: int) -> str:
|
|
39
140
|
"""Sembolleri arar ve yerine koyar."""
|
|
40
|
-
word
|
|
141
|
+
word = match[0]
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
index = Packer.unbase(word, radix)
|
|
145
|
+
if 0 <= index < len(symtab):
|
|
146
|
+
replacement = symtab[index]
|
|
147
|
+
return replacement or word
|
|
148
|
+
except (ValueError, IndexError):
|
|
149
|
+
pass
|
|
41
150
|
|
|
42
|
-
return
|
|
151
|
+
return word
|
|
43
152
|
|
|
44
153
|
@staticmethod
|
|
45
154
|
def unpack(source: str) -> str:
|
|
46
|
-
"""
|
|
155
|
+
"""
|
|
156
|
+
P.A.C.K.E.R. formatındaki sıkıştırılmış bir JavaScript kodunu çözer.
|
|
157
|
+
Birden fazla format ve varyasyonu destekler.
|
|
158
|
+
"""
|
|
159
|
+
# Kaçış dizilerini temizle
|
|
47
160
|
source = Packer.clean_escape_sequences(source)
|
|
48
161
|
|
|
49
|
-
|
|
162
|
+
# Argümanları çıkar
|
|
163
|
+
try:
|
|
164
|
+
payload, symtab, radix, count = Packer.extract_arguments(source)
|
|
50
165
|
|
|
51
|
-
|
|
52
|
-
|
|
166
|
+
# Sembol tablosunun doğruluğunu kontrol et (ancak sıkı değil)
|
|
167
|
+
if len(symtab) != count:
|
|
168
|
+
print(f"Uyarı: Sembol tablosu sayısı ({len(symtab)}) ile belirtilen sayı ({count}) eşleşmiyor, ancak devam ediliyor.")
|
|
53
169
|
|
|
54
|
-
|
|
170
|
+
# Kelimeleri değiştir ve sonucu döndür
|
|
171
|
+
return Packer.REPLACE_PATTERN.sub(
|
|
172
|
+
lambda match: Packer.lookup_symbol(match, symtab, radix),
|
|
173
|
+
payload
|
|
174
|
+
)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
# Detaylı hata mesajı
|
|
177
|
+
raise ValueError(f"Unpacking failed: {str(e)}\nSource preview: {source[:100]}...")
|
|
55
178
|
|
|
56
179
|
@staticmethod
|
|
57
|
-
def
|
|
58
|
-
"""
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
180
|
+
def detect_packed(source: str) -> bool:
|
|
181
|
+
"""Verilen kodun P.A.C.K.E.R. formatında sıkıştırılmış olup olmadığını kontrol eder."""
|
|
182
|
+
# Standart pattern'i kontrol et
|
|
183
|
+
if Packer.PACKED_PATTERN.search(source):
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
# Alternatif pattern'leri kontrol et
|
|
187
|
+
for pattern in Packer.ALTERNATIVE_PATTERNS:
|
|
188
|
+
if pattern.search(source):
|
|
189
|
+
return True
|
|
190
|
+
|
|
191
|
+
# Yaygın belirteçleri kontrol et
|
|
192
|
+
indicators = [
|
|
193
|
+
".split('|')",
|
|
194
|
+
'.split("|")',
|
|
195
|
+
"function(p,a,c,k,e,",
|
|
196
|
+
"function(p, a, c, k, e, "
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
return any(indicator in source for indicator in indicators)
|
Kekik/cache.py
CHANGED
|
@@ -9,13 +9,23 @@ import threading
|
|
|
9
9
|
import asyncio
|
|
10
10
|
import pickle
|
|
11
11
|
|
|
12
|
-
# Redis
|
|
13
|
-
import redis.asyncio as
|
|
12
|
+
# Redis client (Redis yoksa fallback yapılacak)
|
|
13
|
+
import redis.asyncio as redisAsync
|
|
14
|
+
import redis as redis
|
|
14
15
|
|
|
15
16
|
# -----------------------------------------------------
|
|
16
17
|
# Sabitler ve Yardımcı Fonksiyonlar
|
|
17
18
|
# -----------------------------------------------------
|
|
18
|
-
UNLIMITED
|
|
19
|
+
UNLIMITED = None
|
|
20
|
+
|
|
21
|
+
REDIS_HOST = "127.0.0.1"
|
|
22
|
+
REDIS_PORT = 6379
|
|
23
|
+
REDIS_DB = 0
|
|
24
|
+
REDIS_PASS = None
|
|
25
|
+
|
|
26
|
+
# FastAPI için cache'ten hariç tutulacak parametreler ve HTTP status kodları
|
|
27
|
+
CACHE_IGNORE_PARAMS = {"kurek", "debug", "_", "t", "timestamp"}
|
|
28
|
+
CACHE_IGNORE_STATUS_CODES = {400, 401, 403, 404, 500, 501, 502, 503}
|
|
19
29
|
|
|
20
30
|
def normalize_for_key(value):
|
|
21
31
|
"""
|
|
@@ -43,6 +53,7 @@ def simple_cache_key(func, args, kwargs) -> str:
|
|
|
43
53
|
Oluşturulan stringin sonuna MD5 hash eklenebilir.
|
|
44
54
|
"""
|
|
45
55
|
base_key = f"{func.__module__}.{func.__qualname__}"
|
|
56
|
+
base_key = "|".join(base_key.split("."))
|
|
46
57
|
|
|
47
58
|
if args:
|
|
48
59
|
norm_args = [normalize_for_key(arg) for arg in args]
|
|
@@ -55,11 +66,12 @@ def simple_cache_key(func, args, kwargs) -> str:
|
|
|
55
66
|
hashed = md5(base_key.encode('utf-8')).hexdigest()
|
|
56
67
|
return f"{base_key}" # |{hashed}
|
|
57
68
|
|
|
58
|
-
async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
69
|
+
async def make_cache_key(func, args, kwargs, is_fastapi=False, include_auth=False) -> str:
|
|
59
70
|
"""
|
|
60
71
|
Cache key'ini oluşturur.
|
|
61
72
|
- is_fastapi=False ise simple_cache_key() kullanılır.
|
|
62
73
|
- True ise FastAPI Request nesnesine göre özel key oluşturulur.
|
|
74
|
+
- include_auth=True ise authorization header'ı key'e dahil edilir.
|
|
63
75
|
"""
|
|
64
76
|
if not is_fastapi:
|
|
65
77
|
return simple_cache_key(func, args, kwargs)
|
|
@@ -67,21 +79,40 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
|
67
79
|
# FastAPI: request ilk argümandan ya da kwargs'dan alınır.
|
|
68
80
|
request = args[0] if args else kwargs.get("request")
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
else:
|
|
74
|
-
try:
|
|
75
|
-
veri = await request.json()
|
|
76
|
-
except Exception:
|
|
77
|
-
form_data = await request.form()
|
|
78
|
-
veri = dict(form_data.items())
|
|
79
|
-
|
|
80
|
-
# Eğer "kurek" gibi özel parametreler varsa temizleyebilirsiniz:
|
|
81
|
-
veri.pop("kurek", None)
|
|
82
|
+
# Request bulunamamışsa fallback
|
|
83
|
+
if request is None or not hasattr(request, 'method'):
|
|
84
|
+
return simple_cache_key(func, args, kwargs)
|
|
82
85
|
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
try:
|
|
87
|
+
if request.method == "GET":
|
|
88
|
+
# Eğer query_params boşsa {} olarak ayarla
|
|
89
|
+
veri = dict(request.query_params) if request.query_params else {}
|
|
90
|
+
else:
|
|
91
|
+
try:
|
|
92
|
+
content_type = request.headers.get("content-type", "")
|
|
93
|
+
if "application/json" in content_type:
|
|
94
|
+
veri = await request.json()
|
|
95
|
+
else:
|
|
96
|
+
form_data = await request.form()
|
|
97
|
+
veri = dict(form_data.items())
|
|
98
|
+
except Exception:
|
|
99
|
+
veri = {}
|
|
100
|
+
|
|
101
|
+
# Sistem parametrelerini temizle
|
|
102
|
+
for param in CACHE_IGNORE_PARAMS:
|
|
103
|
+
veri.pop(param, None)
|
|
104
|
+
|
|
105
|
+
# Authorization header'ı dahil et (user-specific cache için)
|
|
106
|
+
if include_auth and "authorization" in request.headers:
|
|
107
|
+
auth_hash = md5(request.headers["authorization"].encode()).hexdigest()
|
|
108
|
+
veri[f"_auth_hash"] = auth_hash
|
|
109
|
+
|
|
110
|
+
args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
|
|
111
|
+
return f"{request.url.path}|{veri}" if veri else f"{request.url.path}"
|
|
112
|
+
except Exception as e:
|
|
113
|
+
# Herhangi bir hata durumunda fallback
|
|
114
|
+
konsol.log(f"[yellow]FastAPI cache key oluşturma hatası: {e}, basit key kullanılıyor")
|
|
115
|
+
return simple_cache_key(func, args, kwargs)
|
|
85
116
|
|
|
86
117
|
# -----------------------------------------------------
|
|
87
118
|
# Senkron Cache (RAM) Sınıfı
|
|
@@ -90,29 +121,35 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
|
|
|
90
121
|
class SyncCache:
|
|
91
122
|
"""
|
|
92
123
|
Senkron fonksiyonlar için basit in-memory cache.
|
|
93
|
-
TTL süresi dolan veriler periyodik olarak arka plan thread
|
|
124
|
+
TTL süresi dolan veriler periyodik olarak arka plan thread'iyle temizlenir.
|
|
94
125
|
"""
|
|
95
|
-
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
|
|
96
|
-
self._ttl
|
|
97
|
-
self._data
|
|
98
|
-
self._times
|
|
126
|
+
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60, max_size=10000):
|
|
127
|
+
self._ttl = ttl
|
|
128
|
+
self._data = {}
|
|
129
|
+
self._times = {}
|
|
130
|
+
self._access_counts = {} # LRU tracker
|
|
131
|
+
self._max_size = max_size
|
|
99
132
|
|
|
100
|
-
# TTL sınırsız değilse, cleanup_interval
|
|
101
|
-
self._cleanup_interval =
|
|
133
|
+
# TTL sınırsız değilse, cleanup_interval kullanıyoruz.
|
|
134
|
+
self._cleanup_interval = cleanup_interval if ttl is UNLIMITED else min(ttl, cleanup_interval)
|
|
102
135
|
|
|
103
136
|
# Arka planda çalışan ve periyodik olarak expired entry'leri temizleyen thread başlatılıyor.
|
|
104
|
-
self._lock
|
|
105
|
-
self._cleanup_thread
|
|
137
|
+
self._lock = threading.RLock()
|
|
138
|
+
self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
|
|
139
|
+
self._cleanup_thread.daemon = True
|
|
106
140
|
self._cleanup_thread.start()
|
|
107
141
|
|
|
108
142
|
def _auto_cleanup(self):
|
|
109
143
|
"""Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
|
|
110
144
|
while True:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
145
|
+
try:
|
|
146
|
+
time.sleep(self._cleanup_interval)
|
|
147
|
+
with self._lock:
|
|
148
|
+
keys = list(self._data.keys())
|
|
149
|
+
for key in keys:
|
|
150
|
+
self.remove_if_expired(key)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
konsol.log(f"[red]Cache cleanup hatası: {e}")
|
|
116
153
|
|
|
117
154
|
def _is_expired(self, key):
|
|
118
155
|
"""Belirtilen key'in süresi dolmuşsa True döner."""
|
|
@@ -138,15 +175,108 @@ class SyncCache:
|
|
|
138
175
|
with self._lock:
|
|
139
176
|
self.remove_if_expired(key)
|
|
140
177
|
veri = self._data[key]
|
|
178
|
+
# LRU tracker'ı güncelle
|
|
179
|
+
self._access_counts[key] = self._access_counts.get(key, 0) + 1
|
|
141
180
|
# konsol.log(f"[yellow][~] {key}")
|
|
142
181
|
return veri
|
|
143
182
|
|
|
144
183
|
def __setitem__(self, key, value):
|
|
145
184
|
with self._lock:
|
|
146
|
-
|
|
185
|
+
# Kapasite kontrolü - LRU temizlemesi
|
|
186
|
+
if len(self._data) >= self._max_size and key not in self._data:
|
|
187
|
+
# En az kullanılan key'i bul ve sil
|
|
188
|
+
lru_key = min(self._access_counts, key=self._access_counts.get)
|
|
189
|
+
self._data.pop(lru_key, None)
|
|
190
|
+
self._times.pop(lru_key, None)
|
|
191
|
+
self._access_counts.pop(lru_key, None)
|
|
192
|
+
# konsol.log(f"[red][-] LRU eviction: {lru_key}")
|
|
193
|
+
|
|
194
|
+
self._data[key] = value
|
|
195
|
+
self._access_counts[key] = 0
|
|
147
196
|
if self._ttl is not UNLIMITED:
|
|
148
197
|
self._times[key] = time.time()
|
|
149
198
|
|
|
199
|
+
class HybridSyncCache:
|
|
200
|
+
"""
|
|
201
|
+
Senkron işlemler için, öncelikle Redis cache kullanılmaya çalışılır.
|
|
202
|
+
Redis'ten veri alınamazsa ya da hata oluşursa, SyncCache (in-memory) fallback uygulanır.
|
|
203
|
+
"""
|
|
204
|
+
def __init__(self, ttl=None, max_size=10000):
|
|
205
|
+
self._ttl = ttl
|
|
206
|
+
self._max_size = max_size
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
self.redis = redis.Redis(
|
|
210
|
+
host = REDIS_HOST,
|
|
211
|
+
port = REDIS_PORT,
|
|
212
|
+
db = REDIS_DB,
|
|
213
|
+
password = REDIS_PASS,
|
|
214
|
+
decode_responses = False
|
|
215
|
+
)
|
|
216
|
+
self.redis.ping()
|
|
217
|
+
except Exception as e:
|
|
218
|
+
konsol.log(f"[yellow]Redis bağlantısı başarısız, in-memory cache kullanılıyor: {e}")
|
|
219
|
+
self.redis = None
|
|
220
|
+
|
|
221
|
+
self.memory = SyncCache(ttl, max_size=max_size)
|
|
222
|
+
|
|
223
|
+
def get(self, key):
|
|
224
|
+
# Önce Redis ile deniyoruz:
|
|
225
|
+
if self.redis:
|
|
226
|
+
try:
|
|
227
|
+
data = self.redis.get(key)
|
|
228
|
+
except Exception as e:
|
|
229
|
+
# konsol.log(f"[yellow]Redis get hatası: {e}")
|
|
230
|
+
data = None
|
|
231
|
+
if data is not None:
|
|
232
|
+
try:
|
|
233
|
+
result = pickle.loads(data)
|
|
234
|
+
# konsol.log(f"[yellow][~] {key}")
|
|
235
|
+
return result
|
|
236
|
+
except Exception as e:
|
|
237
|
+
# Deserialize hatası durumunda fallback'e geç
|
|
238
|
+
# konsol.log(f"[yellow]Pickle deserialize hatası: {e}")
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
# Redis'te veri yoksa, yerel cache'ten alıyoruz.
|
|
242
|
+
try:
|
|
243
|
+
return self.memory[key]
|
|
244
|
+
except KeyError:
|
|
245
|
+
raise KeyError(key)
|
|
246
|
+
|
|
247
|
+
def set(self, key, value):
|
|
248
|
+
try:
|
|
249
|
+
ser = pickle.dumps(value)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
# Serialization hatası durumunda yerel cache'e yazalım.
|
|
252
|
+
# (TemplateResponse, FileResponse gibi pickle'lanamayan objeler için)
|
|
253
|
+
# konsol.log(f"[yellow]Pickle serialize hatası: {e}, in-memory cache kullanılıyor")
|
|
254
|
+
self.memory[key] = value
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
if self.redis:
|
|
258
|
+
try:
|
|
259
|
+
if self._ttl is not None:
|
|
260
|
+
self.redis.set(key, ser, ex=self._ttl)
|
|
261
|
+
else:
|
|
262
|
+
self.redis.set(key, ser)
|
|
263
|
+
return
|
|
264
|
+
except Exception as e:
|
|
265
|
+
# Redis'e yazılamazsa yerel cache'e geçelim.
|
|
266
|
+
# konsol.log(f"[yellow]Redis set hatası: {e}, in-memory cache kullanılıyor")
|
|
267
|
+
self.memory[key] = value
|
|
268
|
+
return
|
|
269
|
+
else:
|
|
270
|
+
# Redis kullanılmıyorsa direkt yerel cache'e yaz.
|
|
271
|
+
self.memory[key] = value
|
|
272
|
+
|
|
273
|
+
# HybridSyncCache'in subscriptable olması için:
|
|
274
|
+
def __getitem__(self, key):
|
|
275
|
+
return self.get(key)
|
|
276
|
+
|
|
277
|
+
def __setitem__(self, key, value):
|
|
278
|
+
self.set(key, value)
|
|
279
|
+
|
|
150
280
|
# -----------------------------------------------------
|
|
151
281
|
# Asenkron Cache (In-Memory) ve Redis Hybrid Cache
|
|
152
282
|
# -----------------------------------------------------
|
|
@@ -155,18 +285,21 @@ class AsyncCache:
|
|
|
155
285
|
"""
|
|
156
286
|
Temel in-memory asenkron cache.
|
|
157
287
|
"""
|
|
158
|
-
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
|
|
288
|
+
def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60, max_size=10000):
|
|
159
289
|
"""
|
|
160
290
|
:param ttl: Her entry için geçerli süre (saniye). Örneğin 3600 saniye 1 saattir.
|
|
161
291
|
:param cleanup_interval: Otomatik temizleme görevinin kaç saniyede bir çalışacağını belirler.
|
|
292
|
+
:param max_size: Maksimum cache boyutu (en eski entry'ler silinir).
|
|
162
293
|
"""
|
|
163
|
-
self._ttl
|
|
164
|
-
self._data
|
|
165
|
-
self._times
|
|
166
|
-
self.
|
|
294
|
+
self._ttl = ttl
|
|
295
|
+
self._data = {}
|
|
296
|
+
self._times = {}
|
|
297
|
+
self._access_counts = {} # LRU tracker
|
|
298
|
+
self.futures = {}
|
|
299
|
+
self._max_size = max_size
|
|
167
300
|
|
|
168
|
-
# TTL sınırsız değilse, cleanup_interval
|
|
169
|
-
self._cleanup_interval =
|
|
301
|
+
# TTL sınırsız değilse, cleanup_interval kullanıyoruz.
|
|
302
|
+
self._cleanup_interval = cleanup_interval if ttl is UNLIMITED else min(ttl, cleanup_interval)
|
|
170
303
|
|
|
171
304
|
# Aktif bir event loop varsa otomatik temizlik görevini başlatıyoruz.
|
|
172
305
|
try:
|
|
@@ -177,9 +310,12 @@ class AsyncCache:
|
|
|
177
310
|
async def _auto_cleanup(self):
|
|
178
311
|
"""Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
|
|
179
312
|
while True:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
self.
|
|
313
|
+
try:
|
|
314
|
+
await asyncio.sleep(self._cleanup_interval)
|
|
315
|
+
for key in list(self._data.keys()):
|
|
316
|
+
self.remove_if_expired(key)
|
|
317
|
+
except Exception as e:
|
|
318
|
+
konsol.log(f"[red]Async cache cleanup hatası: {e}")
|
|
183
319
|
|
|
184
320
|
def ensure_cleanup_task(self):
|
|
185
321
|
"""Event loop mevcutsa, cleanup task henüz başlatılmadıysa oluştur."""
|
|
@@ -214,6 +350,8 @@ class AsyncCache:
|
|
|
214
350
|
try:
|
|
215
351
|
# Cache içerisinde key varsa direkt değeri döndür.
|
|
216
352
|
value = self._data[key]
|
|
353
|
+
# LRU tracker'ı güncelle
|
|
354
|
+
self._access_counts[key] = self._access_counts.get(key, 0) + 1
|
|
217
355
|
# konsol.log(f"[yellow][~] {key}")
|
|
218
356
|
return value
|
|
219
357
|
except KeyError:
|
|
@@ -231,28 +369,44 @@ class AsyncCache:
|
|
|
231
369
|
async def set(self, key, value):
|
|
232
370
|
"""Belirtilen key için cache'e değer ekler."""
|
|
233
371
|
self.ensure_cleanup_task()
|
|
234
|
-
|
|
372
|
+
|
|
373
|
+
# Kapasite kontrolü - LRU temizlemesi
|
|
374
|
+
if len(self._data) >= self._max_size and key not in self._data:
|
|
375
|
+
# En az kullanılan key'i bul ve sil
|
|
376
|
+
lru_key = min(self._access_counts, key=self._access_counts.get)
|
|
377
|
+
self._data.pop(lru_key, None)
|
|
378
|
+
self._times.pop(lru_key, None)
|
|
379
|
+
self._access_counts.pop(lru_key, None)
|
|
380
|
+
self.futures.pop(lru_key, None)
|
|
381
|
+
# konsol.log(f"[red][-] Async LRU eviction: {lru_key}")
|
|
382
|
+
|
|
383
|
+
self._data[key] = value
|
|
384
|
+
self._access_counts[key] = 0
|
|
235
385
|
if self._ttl is not UNLIMITED:
|
|
236
386
|
self._times[key] = time.time()
|
|
237
387
|
|
|
238
388
|
class HybridAsyncCache:
|
|
239
389
|
"""
|
|
240
390
|
Öncelikle Redis cache kullanılmaya çalışılır.
|
|
241
|
-
Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCache
|
|
391
|
+
Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCache'e geçilir.
|
|
242
392
|
"""
|
|
243
|
-
def __init__(self, ttl=UNLIMITED):
|
|
393
|
+
def __init__(self, ttl=UNLIMITED, max_size=10000):
|
|
244
394
|
self._ttl = ttl
|
|
395
|
+
self._max_size = max_size
|
|
245
396
|
|
|
246
397
|
try:
|
|
247
|
-
self.redis =
|
|
248
|
-
host =
|
|
249
|
-
port =
|
|
398
|
+
self.redis = redisAsync.Redis(
|
|
399
|
+
host = REDIS_HOST,
|
|
400
|
+
port = REDIS_PORT,
|
|
401
|
+
db = REDIS_DB,
|
|
402
|
+
password = REDIS_PASS,
|
|
250
403
|
decode_responses = False
|
|
251
404
|
)
|
|
252
|
-
except Exception:
|
|
405
|
+
except Exception as e:
|
|
406
|
+
konsol.log(f"[yellow]Async Redis bağlantısı başarısız, in-memory cache kullanılıyor: {e}")
|
|
253
407
|
self.redis = None
|
|
254
408
|
|
|
255
|
-
self.memory = AsyncCache(ttl)
|
|
409
|
+
self.memory = AsyncCache(ttl, max_size=max_size)
|
|
256
410
|
self.futures = {}
|
|
257
411
|
|
|
258
412
|
async def get(self, key):
|
|
@@ -265,15 +419,17 @@ class HybridAsyncCache:
|
|
|
265
419
|
if self.redis:
|
|
266
420
|
try:
|
|
267
421
|
data = await self.redis.get(key)
|
|
268
|
-
except Exception:
|
|
422
|
+
except Exception as e:
|
|
423
|
+
konsol.log(f"[yellow]Async Redis get hatası: {e}")
|
|
269
424
|
return await self.memory.get(key)
|
|
270
425
|
if data is not None:
|
|
271
426
|
try:
|
|
272
427
|
result = pickle.loads(data)
|
|
273
428
|
# konsol.log(f"[yellow][~] {key}")
|
|
274
429
|
return result
|
|
275
|
-
except Exception:
|
|
430
|
+
except Exception as e:
|
|
276
431
|
# Deserialize hatası durumunda in-memory cache'ten dene
|
|
432
|
+
konsol.log(f"[yellow]Async pickle deserialize hatası: {e}")
|
|
277
433
|
return await self.memory.get(key)
|
|
278
434
|
else:
|
|
279
435
|
# Redis'te veri yoksa, in-memory cache'e bak
|
|
@@ -286,8 +442,10 @@ class HybridAsyncCache:
|
|
|
286
442
|
# Önce veriyi pickle etmeyi deniyoruz.
|
|
287
443
|
try:
|
|
288
444
|
ser = pickle.dumps(value)
|
|
289
|
-
except Exception:
|
|
445
|
+
except Exception as e:
|
|
290
446
|
# Serialization hatası durumunda sadece in-memory cache'e yaz
|
|
447
|
+
# (TemplateResponse, FileResponse gibi pickle'lanamayan objeler için)
|
|
448
|
+
# konsol.log(f"[yellow]Async pickle serialize hatası: {e}, in-memory cache kullanılıyor")
|
|
291
449
|
await self.memory.set(key, value)
|
|
292
450
|
return
|
|
293
451
|
|
|
@@ -298,8 +456,9 @@ class HybridAsyncCache:
|
|
|
298
456
|
else:
|
|
299
457
|
await self.redis.set(key, ser)
|
|
300
458
|
return
|
|
301
|
-
except Exception:
|
|
459
|
+
except Exception as e:
|
|
302
460
|
# Redis yazma hatası durumunda in-memory fallback
|
|
461
|
+
# konsol.log(f"[yellow]Async Redis set hatası: {e}, in-memory cache kullanılıyor")
|
|
303
462
|
await self.memory.set(key, value)
|
|
304
463
|
return
|
|
305
464
|
else:
|
|
@@ -354,35 +513,61 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
|
|
|
354
513
|
# kekik_cache Dekoratörü (Senkrondan Asenkrona)
|
|
355
514
|
# -----------------------------------------------------
|
|
356
515
|
|
|
357
|
-
def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=
|
|
516
|
+
def kekik_cache(ttl=UNLIMITED, unless=None, use_redis=True, max_size=10000, is_fastapi=None, include_auth=False):
|
|
358
517
|
"""
|
|
359
518
|
Bir fonksiyonun (senkron/asenkron) sonucunu cache'ler.
|
|
519
|
+
FastAPI endpoint'leri otomatik detekt edilir (is_fastapi=None ise) veya manuel olarak belirtilebilir.
|
|
360
520
|
|
|
361
521
|
Parametreler:
|
|
362
522
|
- ttl: Cache'in geçerlilik süresi (saniye). UNLIMITED ise süresizdir.
|
|
363
523
|
- unless: Sonuç alınmadan önce çağrılan, True dönerse cache'e alınmaz.
|
|
364
|
-
- is_fastapi: True
|
|
524
|
+
- is_fastapi: True/False ile açıkça belirt, None ise otomatik detekt (Request nesnesine bakarak).
|
|
365
525
|
- use_redis: Asenkron fonksiyonlarda Redis kullanımı (Hybrid cache) için True verilebilir.
|
|
526
|
+
- max_size: Cache'in maksimum boyutu. Kapasiteyi aşarsa LRU temizlemesi yapılır.
|
|
527
|
+
- include_auth: Authorization header'ını key'e dahil et (user-specific cache).
|
|
366
528
|
|
|
367
529
|
Örnek Kullanım:
|
|
368
530
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
531
|
+
# Basit fonksiyon
|
|
532
|
+
@kekik_cache(ttl=300)
|
|
533
|
+
async def hesapla(param):
|
|
534
|
+
return param * 2
|
|
535
|
+
|
|
536
|
+
# FastAPI endpoint (otomatik detekt)
|
|
537
|
+
@app.get("/users/{user_id}")
|
|
538
|
+
@kekik_cache(ttl=300, include_auth=True)
|
|
539
|
+
async def get_user(user_id: int, request: Request):
|
|
540
|
+
return {"id": user_id, "name": "..."}
|
|
541
|
+
|
|
542
|
+
# FastAPI endpoint (manuel belirtim)
|
|
543
|
+
@app.get("/data")
|
|
544
|
+
@kekik_cache(ttl=600, is_fastapi=True)
|
|
545
|
+
async def get_data(request: Request):
|
|
546
|
+
return {"data": "..."}
|
|
547
|
+
|
|
548
|
+
# Hata response'larını cache'leme
|
|
549
|
+
@app.get("/data")
|
|
550
|
+
@kekik_cache(ttl=300, unless=lambda r: r.status_code >= 400)
|
|
551
|
+
async def get_data(request: Request):
|
|
552
|
+
return {}
|
|
372
553
|
"""
|
|
373
554
|
# Parametresiz kullanım durumunda
|
|
374
555
|
if callable(ttl):
|
|
375
|
-
return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi,
|
|
556
|
+
return kekik_cache(UNLIMITED, unless=unless, use_redis=use_redis, max_size=max_size, is_fastapi=is_fastapi, include_auth=include_auth)(ttl)
|
|
376
557
|
|
|
377
558
|
def decorator(func):
|
|
378
559
|
if asyncio.iscoroutinefunction(func):
|
|
379
560
|
# Asenkron fonksiyonlar için cache türünü seçelim:
|
|
380
|
-
|
|
381
|
-
func.__cache = HybridAsyncCache(ttl) if use_redis else AsyncCache(ttl)
|
|
561
|
+
func.__cache = HybridAsyncCache(ttl, max_size=max_size) if use_redis else AsyncCache(ttl, max_size=max_size)
|
|
382
562
|
|
|
383
563
|
@wraps(func)
|
|
384
564
|
async def async_wrapper(*args, **kwargs):
|
|
385
|
-
|
|
565
|
+
# is_fastapi otomatik deteksiyon (None ise) ya da manuel belirtim
|
|
566
|
+
detect_fastapi = is_fastapi
|
|
567
|
+
if detect_fastapi is None:
|
|
568
|
+
detect_fastapi = args and hasattr(args[0], "url") and hasattr(args[0], "method")
|
|
569
|
+
|
|
570
|
+
key = await make_cache_key(func, args, kwargs, detect_fastapi, include_auth=include_auth)
|
|
386
571
|
|
|
387
572
|
try:
|
|
388
573
|
return await func.__cache.get(key)
|
|
@@ -392,7 +577,7 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False, use_redis=True):
|
|
|
392
577
|
return async_wrapper
|
|
393
578
|
else:
|
|
394
579
|
# Senkron fonksiyonlar için
|
|
395
|
-
func.__cache = SyncCache(ttl)
|
|
580
|
+
func.__cache = HybridSyncCache(ttl, max_size=max_size) if use_redis else SyncCache(ttl, max_size=max_size)
|
|
396
581
|
|
|
397
582
|
@wraps(func)
|
|
398
583
|
def sync_wrapper(*args, **kwargs):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: Kekik
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.9
|
|
4
4
|
Summary: İşlerimizi kolaylaştıracak fonksiyonların el altında durduğu kütüphane..
|
|
5
5
|
Home-page: https://github.com/keyiflerolsun/Kekik
|
|
6
6
|
Author: keyiflerolsun
|
|
@@ -26,6 +26,7 @@ Requires-Dist: simplejson
|
|
|
26
26
|
Requires-Dist: rich
|
|
27
27
|
Requires-Dist: tabulate
|
|
28
28
|
Requires-Dist: pycryptodome
|
|
29
|
+
Requires-Dist: redis
|
|
29
30
|
Dynamic: author
|
|
30
31
|
Dynamic: author-email
|
|
31
32
|
Dynamic: classifier
|
|
@@ -34,6 +35,7 @@ Dynamic: description-content-type
|
|
|
34
35
|
Dynamic: home-page
|
|
35
36
|
Dynamic: keywords
|
|
36
37
|
Dynamic: license
|
|
38
|
+
Dynamic: license-file
|
|
37
39
|
Dynamic: requires-dist
|
|
38
40
|
Dynamic: requires-python
|
|
39
41
|
Dynamic: summary
|
|
@@ -315,6 +317,11 @@ var played = 0;
|
|
|
315
317
|
eval_jwSetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(veri)[0]
|
|
316
318
|
print(Packer.unpack(Packer.unpack(eval_jwSetup)))
|
|
317
319
|
# jwSetup.sources=[{"default":true,"file":"\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x32\x2e\x69\x6d\x61\x67\x65\x73\x70\x6f\x74\x2e\x62\x75\x7a\x7a\x2f\x66\x32\x2f\x4e\x74\x4f\x31\x4e\x51\x5a\x6a\x44\x51\x41\x6b\x78\x6c\x58\x45\x47\x33\x6c\x62\x66\x62\x30\x31\x79\x74\x70\x57\x66\x4e\x30\x66\x62\x66\x50\x58\x5a\x55\x31\x6a\x50\x77\x5a\x6d\x48\x71\x58\x41\x37\x6c\x6d\x6d\x4b\x67\x47\x59\x31\x66\x47\x42\x6d\x6c\x38\x68\x32\x7a\x33\x4f\x5a\x69\x4f\x63\x4c\x6b\x51\x70\x7a\x57\x78\x4b\x45\x4c\x57\x42\x63\x79\x4d\x74\x75\x55\x44\x57\x46\x4e\x6c\x69\x64\x70\x46\x46\x65\x6e\x65\x64\x66\x48\x30\x69\x74\x66\x59\x67\x38\x52\x47\x41\x6b\x38\x6c\x76\x72\x31","label":"0","type":"hls","preload":"none"}];var mu=getLocation(jwSetup.sources[0].file);
|
|
320
|
+
|
|
321
|
+
# ! Veya
|
|
322
|
+
|
|
323
|
+
while Packer.detect_packed(veri):
|
|
324
|
+
veri = Packer.unpack(veri)
|
|
318
325
|
```
|
|
319
326
|
|
|
320
327
|
### **[CryptoJS](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/Sifreleme/CryptoJS.py)**
|
|
@@ -367,6 +374,26 @@ print(NaysHash().generate_xtoken(
|
|
|
367
374
|
# EygcmEIe3aU0TWIubaQTuBwbrqpY7HFcNDajlSKCT5c=
|
|
368
375
|
```
|
|
369
376
|
|
|
377
|
+
### **[kekik_cache](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/cache.py)**
|
|
378
|
+
```python
|
|
379
|
+
import Kekik.cache as cache
|
|
380
|
+
cache.REDIS_HOST = "127.0.0.1"
|
|
381
|
+
cache.REDIS_PORT = 6379
|
|
382
|
+
cache.REDIS_DB = 0
|
|
383
|
+
cache.REDIS_PASS = None
|
|
384
|
+
kekik_cache = cache.kekik_cache
|
|
385
|
+
|
|
386
|
+
@kekik_cache(ttl=5)
|
|
387
|
+
def sync_func(x, y):
|
|
388
|
+
time.sleep(1)
|
|
389
|
+
return x + y
|
|
390
|
+
|
|
391
|
+
@kekik_cache(ttl=5)
|
|
392
|
+
async def async_func(x, y):
|
|
393
|
+
await asyncio.sleep(1)
|
|
394
|
+
return x * y
|
|
395
|
+
```
|
|
396
|
+
|
|
370
397
|
### **[dict2json](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/dict2json.py)**
|
|
371
398
|
### **[dosya_indir](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/dosya_indir.py)**
|
|
372
399
|
### **[benim_hwid](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/hwid_kontrol.py)**
|
|
@@ -2,7 +2,7 @@ Kekik/BIST.py,sha256=gFvEFdLGgsxUq33AkqOr5pVGsSm9jR0v6DVwoSzhX7w,1375
|
|
|
2
2
|
Kekik/Domain2IP.py,sha256=dMDatPElTMfLORzC_QN9znex8Se1eid3EPsxAe5S1aQ,4851
|
|
3
3
|
Kekik/Nesne.py,sha256=33pp49cZkRCoa0aUFfzVMpx5FSJchSdiKhm97Nkol1Q,7020
|
|
4
4
|
Kekik/__init__.py,sha256=Iy-O4c_DuY2ttQJv6j7xoVk3IS9EcC0Aqx3vhFvCgd8,1057
|
|
5
|
-
Kekik/cache.py,sha256=
|
|
5
|
+
Kekik/cache.py,sha256=to3ihJyxPasQDr5QO1hJPRVDbidBSILK2kGKUfPZ2wE,23308
|
|
6
6
|
Kekik/cli.py,sha256=I--MV8-okBSUYAaV4fRsMeB2n57OJITrgbEnK1OdTPY,3648
|
|
7
7
|
Kekik/csv2dict.py,sha256=HO0AgREXE0yM7cL4OwqpkCGm2pz31RTb0mhnTg6gdwo,423
|
|
8
8
|
Kekik/dict2csv.py,sha256=AcGvEg9i5MXtwMwg7WiDxOj8h9LldNjjcRiWFwA9XJU,560
|
|
@@ -27,16 +27,16 @@ Kekik/Sifreleme/AESManager.py,sha256=eYgbHANtYrWFJgLeUuj63tlL0Yn8gyPCJYbXfWlpNbo
|
|
|
27
27
|
Kekik/Sifreleme/CryptoJS.py,sha256=qDlgTaSXcs5jF4DNmjuwK5CL3VL1P7xyJzDTof1--As,3126
|
|
28
28
|
Kekik/Sifreleme/HexCodec.py,sha256=fB1ZGBYCQLUUiZNXqn0sxYhEMhxPoyC1BPYkRJ5G7hY,900
|
|
29
29
|
Kekik/Sifreleme/NaysHash.py,sha256=CJVlyHCXSv8JN8fitF2LQHLcd-opY6zergcgmX3pj_Y,2234
|
|
30
|
-
Kekik/Sifreleme/Packer.py,sha256=
|
|
30
|
+
Kekik/Sifreleme/Packer.py,sha256=o4jkoKdI0UOg9jqGsCg2H82vLbfsuYQnWFYS1DzRWrU,7290
|
|
31
31
|
Kekik/Sifreleme/StringCodec.py,sha256=5kmFLN7g2c_ENxD491lAlH0AGtW7tIy5h5KPHEuTzyM,1426
|
|
32
32
|
Kekik/Sifreleme/__init__.py,sha256=IDSVFP4xshnpPLyCX2ndY_N9cYIzikqu5LFi_E-3-5Q,284
|
|
33
33
|
Kekik/kisi_ver/__init__.py,sha256=gH613YZC3ziE7f67dViefRSuwDwsHyVvXHpM5CzZ2q4,1386
|
|
34
34
|
Kekik/kisi_ver/biyografiler.py,sha256=5Xv1ixaDGHGtl5Nf92jo9dPgF3jDXOGEPmWgoEsn9j8,189486
|
|
35
35
|
Kekik/kisi_ver/isimler.py,sha256=zHVimWL_4TvoLE3qzWQslDBc8-IJZSB02s0vRwsVM1g,88066
|
|
36
36
|
Kekik/kisi_ver/soyisimler.py,sha256=YQJYp2SjENgwOaCa9mmShxPYeeUll2cq8Vox-d8_kB8,78485
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
kekik-1.7.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
38
|
+
kekik-1.7.9.dist-info/METADATA,sha256=z_24aSaYTNhm4UWDjgfeC5dk4EiHs7SRm_Uys-6Nn54,44515
|
|
39
|
+
kekik-1.7.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
40
|
+
kekik-1.7.9.dist-info/entry_points.txt,sha256=yjBifxtRlqfg8lPkH4Bu-urSa5ecptCHsuth-DcyWcg,59
|
|
41
|
+
kekik-1.7.9.dist-info/top_level.txt,sha256=NotddscfgxawvuRyAa7xkgnMhyteFDcBxb5aU5GY3BM,6
|
|
42
|
+
kekik-1.7.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|