Kekik 1.7.5__tar.gz → 1.7.9__tar.gz

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.

Potentially problematic release.


This version of Kekik might be problematic. Click here for more details.

Files changed (49) hide show
  1. kekik-1.7.9/Kekik/Sifreleme/Packer.py +199 -0
  2. {kekik-1.7.5 → kekik-1.7.9}/Kekik/cache.py +169 -66
  3. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/PKG-INFO +8 -2
  4. {kekik-1.7.5 → kekik-1.7.9}/PKG-INFO +8 -2
  5. {kekik-1.7.5 → kekik-1.7.9}/README.md +5 -0
  6. {kekik-1.7.5 → kekik-1.7.9}/setup.py +1 -1
  7. kekik-1.7.5/Kekik/Sifreleme/Packer.py +0 -62
  8. {kekik-1.7.5 → kekik-1.7.9}/Kekik/BIST.py +0 -0
  9. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Domain2IP.py +0 -0
  10. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Nesne.py +0 -0
  11. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/AESManager.py +0 -0
  12. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/CryptoJS.py +0 -0
  13. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/HexCodec.py +0 -0
  14. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/NaysHash.py +0 -0
  15. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/StringCodec.py +0 -0
  16. {kekik-1.7.5 → kekik-1.7.9}/Kekik/Sifreleme/__init__.py +0 -0
  17. {kekik-1.7.5 → kekik-1.7.9}/Kekik/__init__.py +0 -0
  18. {kekik-1.7.5 → kekik-1.7.9}/Kekik/cli.py +0 -0
  19. {kekik-1.7.5 → kekik-1.7.9}/Kekik/csv2dict.py +0 -0
  20. {kekik-1.7.5 → kekik-1.7.9}/Kekik/dict2csv.py +0 -0
  21. {kekik-1.7.5 → kekik-1.7.9}/Kekik/dict2json.py +0 -0
  22. {kekik-1.7.5 → kekik-1.7.9}/Kekik/dosya2set.py +0 -0
  23. {kekik-1.7.5 → kekik-1.7.9}/Kekik/dosya_indir.py +0 -0
  24. {kekik-1.7.5 → kekik-1.7.9}/Kekik/hwid_kontrol.py +0 -0
  25. {kekik-1.7.5 → kekik-1.7.9}/Kekik/kisi_ver/__init__.py +0 -0
  26. {kekik-1.7.5 → kekik-1.7.9}/Kekik/kisi_ver/biyografiler.py +0 -0
  27. {kekik-1.7.5 → kekik-1.7.9}/Kekik/kisi_ver/isimler.py +0 -0
  28. {kekik-1.7.5 → kekik-1.7.9}/Kekik/kisi_ver/soyisimler.py +0 -0
  29. {kekik-1.7.5 → kekik-1.7.9}/Kekik/link_islemleri.py +0 -0
  30. {kekik-1.7.5 → kekik-1.7.9}/Kekik/list2html.py +0 -0
  31. {kekik-1.7.5 → kekik-1.7.9}/Kekik/liste_fetis.py +0 -0
  32. {kekik-1.7.5 → kekik-1.7.9}/Kekik/mail_gonder.py +0 -0
  33. {kekik-1.7.5 → kekik-1.7.9}/Kekik/okunabilir_byte.py +0 -0
  34. {kekik-1.7.5 → kekik-1.7.9}/Kekik/proxy_ver.py +0 -0
  35. {kekik-1.7.5 → kekik-1.7.9}/Kekik/qr_ver.py +0 -0
  36. {kekik-1.7.5 → kekik-1.7.9}/Kekik/ses_fetis.py +0 -0
  37. {kekik-1.7.5 → kekik-1.7.9}/Kekik/slugify.py +0 -0
  38. {kekik-1.7.5 → kekik-1.7.9}/Kekik/terminal_baslik.py +0 -0
  39. {kekik-1.7.5 → kekik-1.7.9}/Kekik/txt_fetis.py +0 -0
  40. {kekik-1.7.5 → kekik-1.7.9}/Kekik/unicode_tr.py +0 -0
  41. {kekik-1.7.5 → kekik-1.7.9}/Kekik/zaman_donustur.py +0 -0
  42. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/SOURCES.txt +0 -0
  43. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/dependency_links.txt +0 -0
  44. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/entry_points.txt +0 -0
  45. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/requires.txt +0 -0
  46. {kekik-1.7.5 → kekik-1.7.9}/Kekik.egg-info/top_level.txt +0 -0
  47. {kekik-1.7.5 → kekik-1.7.9}/LICENSE +0 -0
  48. {kekik-1.7.5 → kekik-1.7.9}/MANIFEST.in +0 -0
  49. {kekik-1.7.5 → kekik-1.7.9}/setup.cfg +0 -0
@@ -0,0 +1,199 @@
1
+ # ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
+
3
+ import re
4
+
5
+ class Packer:
6
+ """
7
+ P.A.C.K.E.R. sıkıştırma ve çözme işlemleri için kapsamlı bir sınıf.
8
+ ! » https://github.com/beautifier/js-beautify/blob/main/python/jsbeautifier/unpackers/packer.py
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
+
50
+ @staticmethod
51
+ def clean_escape_sequences(source: str) -> str:
52
+ """Kaçış dizilerini temizler."""
53
+ source = re.sub(r'\\\\', r'\\', source)
54
+ source = source.replace("\\'", "'")
55
+ source = source.replace('\\"', '"')
56
+ return source
57
+
58
+ @staticmethod
59
+ def extract_arguments(source: str) -> tuple[str, list[str], int, int]:
60
+ """P.A.C.K.E.R. formatındaki kaynak koddan argümanları çıkarır."""
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
70
+
71
+ if not match:
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.")
85
+
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()
97
+
98
+ return payload, symtab.split("|"), int(radix), int(count)
99
+
100
+ @staticmethod
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
112
+
113
+ # Geniş taban desteği (37-95 arası)
114
+ if base > 95:
115
+ raise ValueError(f"Desteklenmeyen taban: {base}")
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
+
138
+ @staticmethod
139
+ def lookup_symbol(match: re.Match, symtab: list[str], radix: int) -> str:
140
+ """Sembolleri arar ve yerine koyar."""
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
150
+
151
+ return word
152
+
153
+ @staticmethod
154
+ def unpack(source: str) -> str:
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
160
+ source = Packer.clean_escape_sequences(source)
161
+
162
+ # Argümanları çıkar
163
+ try:
164
+ payload, symtab, radix, count = Packer.extract_arguments(source)
165
+
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.")
169
+
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]}...")
178
+
179
+ @staticmethod
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)
@@ -23,6 +23,10 @@ REDIS_PORT = 6379
23
23
  REDIS_DB = 0
24
24
  REDIS_PASS = None
25
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}
29
+
26
30
  def normalize_for_key(value):
27
31
  """
28
32
  Cache key oluşturma amacıyla verilen değeri normalize eder.
@@ -49,6 +53,7 @@ def simple_cache_key(func, args, kwargs) -> str:
49
53
  Oluşturulan stringin sonuna MD5 hash eklenebilir.
50
54
  """
51
55
  base_key = f"{func.__module__}.{func.__qualname__}"
56
+ base_key = "|".join(base_key.split("."))
52
57
 
53
58
  if args:
54
59
  norm_args = [normalize_for_key(arg) for arg in args]
@@ -61,11 +66,12 @@ def simple_cache_key(func, args, kwargs) -> str:
61
66
  hashed = md5(base_key.encode('utf-8')).hexdigest()
62
67
  return f"{base_key}" # |{hashed}
63
68
 
64
- 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:
65
70
  """
66
71
  Cache key'ini oluşturur.
67
72
  - is_fastapi=False ise simple_cache_key() kullanılır.
68
73
  - True ise FastAPI Request nesnesine göre özel key oluşturulur.
74
+ - include_auth=True ise authorization header'ı key'e dahil edilir.
69
75
  """
70
76
  if not is_fastapi:
71
77
  return simple_cache_key(func, args, kwargs)
@@ -73,21 +79,40 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
73
79
  # FastAPI: request ilk argümandan ya da kwargs'dan alınır.
74
80
  request = args[0] if args else kwargs.get("request")
75
81
 
76
- if request.method == "GET":
77
- # Eğer query_params boşsa {} olarak ayarla
78
- veri = dict(request.query_params) if request.query_params else {}
79
- else:
80
- try:
81
- veri = await request.json()
82
- except Exception:
83
- form_data = await request.form()
84
- veri = dict(form_data.items())
85
-
86
- # Eğer "kurek" gibi özel parametreler varsa temizleyebilirsiniz:
87
- 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)
88
85
 
89
- args_hash = md5(urlencode(veri).encode()).hexdigest() if veri else ""
90
- return f"{request.url.path}?{veri}"
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)
91
116
 
92
117
  # -----------------------------------------------------
93
118
  # Senkron Cache (RAM) Sınıfı
@@ -96,29 +121,35 @@ async def make_cache_key(func, args, kwargs, is_fastapi=False) -> str:
96
121
  class SyncCache:
97
122
  """
98
123
  Senkron fonksiyonlar için basit in-memory cache.
99
- TTL süresi dolan veriler periyodik olarak arka plan threadiyle temizlenir.
124
+ TTL süresi dolan veriler periyodik olarak arka plan thread'iyle temizlenir.
100
125
  """
101
- def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
102
- self._ttl = ttl
103
- self._data = {}
104
- 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
105
132
 
106
- # TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
107
- self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else 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)
108
135
 
109
136
  # Arka planda çalışan ve periyodik olarak expired entry'leri temizleyen thread başlatılıyor.
110
- self._lock = threading.RLock()
111
- self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
137
+ self._lock = threading.RLock()
138
+ self._cleanup_thread = threading.Thread(target=self._auto_cleanup, daemon=True)
139
+ self._cleanup_thread.daemon = True
112
140
  self._cleanup_thread.start()
113
141
 
114
142
  def _auto_cleanup(self):
115
143
  """Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
116
144
  while True:
117
- time.sleep(self._cleanup_interval)
118
- with self._lock:
119
- keys = list(self._data.keys())
120
- for key in keys:
121
- self.remove_if_expired(key)
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}")
122
153
 
123
154
  def _is_expired(self, key):
124
155
  """Belirtilen key'in süresi dolmuşsa True döner."""
@@ -144,12 +175,24 @@ class SyncCache:
144
175
  with self._lock:
145
176
  self.remove_if_expired(key)
146
177
  veri = self._data[key]
178
+ # LRU tracker'ı güncelle
179
+ self._access_counts[key] = self._access_counts.get(key, 0) + 1
147
180
  # konsol.log(f"[yellow][~] {key}")
148
181
  return veri
149
182
 
150
183
  def __setitem__(self, key, value):
151
184
  with self._lock:
152
- self._data[key] = value
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
153
196
  if self._ttl is not UNLIMITED:
154
197
  self._times[key] = time.time()
155
198
 
@@ -158,8 +201,9 @@ class HybridSyncCache:
158
201
  Senkron işlemler için, öncelikle Redis cache kullanılmaya çalışılır.
159
202
  Redis'ten veri alınamazsa ya da hata oluşursa, SyncCache (in-memory) fallback uygulanır.
160
203
  """
161
- def __init__(self, ttl=None):
204
+ def __init__(self, ttl=None, max_size=10000):
162
205
  self._ttl = ttl
206
+ self._max_size = max_size
163
207
 
164
208
  try:
165
209
  self.redis = redis.Redis(
@@ -170,25 +214,28 @@ class HybridSyncCache:
170
214
  decode_responses = False
171
215
  )
172
216
  self.redis.ping()
173
- except Exception:
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}")
174
219
  self.redis = None
175
220
 
176
- self.memory = SyncCache(ttl)
221
+ self.memory = SyncCache(ttl, max_size=max_size)
177
222
 
178
223
  def get(self, key):
179
224
  # Önce Redis ile deniyoruz:
180
225
  if self.redis:
181
226
  try:
182
227
  data = self.redis.get(key)
183
- except Exception:
228
+ except Exception as e:
229
+ # konsol.log(f"[yellow]Redis get hatası: {e}")
184
230
  data = None
185
231
  if data is not None:
186
232
  try:
187
233
  result = pickle.loads(data)
188
234
  # konsol.log(f"[yellow][~] {key}")
189
235
  return result
190
- except Exception:
236
+ except Exception as e:
191
237
  # Deserialize hatası durumunda fallback'e geç
238
+ # konsol.log(f"[yellow]Pickle deserialize hatası: {e}")
192
239
  pass
193
240
 
194
241
  # Redis'te veri yoksa, yerel cache'ten alıyoruz.
@@ -200,8 +247,10 @@ class HybridSyncCache:
200
247
  def set(self, key, value):
201
248
  try:
202
249
  ser = pickle.dumps(value)
203
- except Exception:
250
+ except Exception as e:
204
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")
205
254
  self.memory[key] = value
206
255
  return
207
256
 
@@ -212,8 +261,9 @@ class HybridSyncCache:
212
261
  else:
213
262
  self.redis.set(key, ser)
214
263
  return
215
- except Exception:
264
+ except Exception as e:
216
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")
217
267
  self.memory[key] = value
218
268
  return
219
269
  else:
@@ -235,18 +285,21 @@ class AsyncCache:
235
285
  """
236
286
  Temel in-memory asenkron cache.
237
287
  """
238
- def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60):
288
+ def __init__(self, ttl=UNLIMITED, cleanup_interval=60 * 60, max_size=10000):
239
289
  """
240
290
  :param ttl: Her entry için geçerli süre (saniye). Örneğin 3600 saniye 1 saattir.
241
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).
242
293
  """
243
- self._ttl = ttl
244
- self._data = {}
245
- self._times = {}
246
- self.futures = {}
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
247
300
 
248
- # TTL sınırsız değilse, cleanup_interval ile ttl'den büyük olanı kullanıyoruz.
249
- self._cleanup_interval = max(ttl, cleanup_interval) if ttl is not UNLIMITED else 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)
250
303
 
251
304
  # Aktif bir event loop varsa otomatik temizlik görevini başlatıyoruz.
252
305
  try:
@@ -257,9 +310,12 @@ class AsyncCache:
257
310
  async def _auto_cleanup(self):
258
311
  """Belirlenen aralıklarla cache içerisindeki süresi dolmuş entry'leri temizler."""
259
312
  while True:
260
- await asyncio.sleep(self._cleanup_interval)
261
- for key in list(self._data.keys()):
262
- self.remove_if_expired(key)
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}")
263
319
 
264
320
  def ensure_cleanup_task(self):
265
321
  """Event loop mevcutsa, cleanup task henüz başlatılmadıysa oluştur."""
@@ -294,6 +350,8 @@ class AsyncCache:
294
350
  try:
295
351
  # Cache içerisinde key varsa direkt değeri döndür.
296
352
  value = self._data[key]
353
+ # LRU tracker'ı güncelle
354
+ self._access_counts[key] = self._access_counts.get(key, 0) + 1
297
355
  # konsol.log(f"[yellow][~] {key}")
298
356
  return value
299
357
  except KeyError:
@@ -311,17 +369,30 @@ class AsyncCache:
311
369
  async def set(self, key, value):
312
370
  """Belirtilen key için cache'e değer ekler."""
313
371
  self.ensure_cleanup_task()
314
- self._data[key] = value
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
315
385
  if self._ttl is not UNLIMITED:
316
386
  self._times[key] = time.time()
317
387
 
318
388
  class HybridAsyncCache:
319
389
  """
320
390
  Öncelikle Redis cache kullanılmaya çalışılır.
321
- Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCachee geçilir.
391
+ Hata durumunda veya Redis erişilemiyorsa in-memory AsyncCache'e geçilir.
322
392
  """
323
- def __init__(self, ttl=UNLIMITED):
393
+ def __init__(self, ttl=UNLIMITED, max_size=10000):
324
394
  self._ttl = ttl
395
+ self._max_size = max_size
325
396
 
326
397
  try:
327
398
  self.redis = redisAsync.Redis(
@@ -331,10 +402,11 @@ class HybridAsyncCache:
331
402
  password = REDIS_PASS,
332
403
  decode_responses = False
333
404
  )
334
- 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}")
335
407
  self.redis = None
336
408
 
337
- self.memory = AsyncCache(ttl)
409
+ self.memory = AsyncCache(ttl, max_size=max_size)
338
410
  self.futures = {}
339
411
 
340
412
  async def get(self, key):
@@ -347,15 +419,17 @@ class HybridAsyncCache:
347
419
  if self.redis:
348
420
  try:
349
421
  data = await self.redis.get(key)
350
- except Exception:
422
+ except Exception as e:
423
+ konsol.log(f"[yellow]Async Redis get hatası: {e}")
351
424
  return await self.memory.get(key)
352
425
  if data is not None:
353
426
  try:
354
427
  result = pickle.loads(data)
355
428
  # konsol.log(f"[yellow][~] {key}")
356
429
  return result
357
- except Exception:
430
+ except Exception as e:
358
431
  # Deserialize hatası durumunda in-memory cache'ten dene
432
+ konsol.log(f"[yellow]Async pickle deserialize hatası: {e}")
359
433
  return await self.memory.get(key)
360
434
  else:
361
435
  # Redis'te veri yoksa, in-memory cache'e bak
@@ -368,8 +442,10 @@ class HybridAsyncCache:
368
442
  # Önce veriyi pickle etmeyi deniyoruz.
369
443
  try:
370
444
  ser = pickle.dumps(value)
371
- except Exception:
445
+ except Exception as e:
372
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")
373
449
  await self.memory.set(key, value)
374
450
  return
375
451
 
@@ -380,8 +456,9 @@ class HybridAsyncCache:
380
456
  else:
381
457
  await self.redis.set(key, ser)
382
458
  return
383
- except Exception:
459
+ except Exception as e:
384
460
  # Redis yazma hatası durumunda in-memory fallback
461
+ # konsol.log(f"[yellow]Async Redis set hatası: {e}, in-memory cache kullanılıyor")
385
462
  await self.memory.set(key, value)
386
463
  return
387
464
  else:
@@ -436,35 +513,61 @@ async def _async_compute_and_cache(func, key, unless, *args, **kwargs):
436
513
  # kekik_cache Dekoratörü (Senkrondan Asenkrona)
437
514
  # -----------------------------------------------------
438
515
 
439
- def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False, use_redis=True):
516
+ def kekik_cache(ttl=UNLIMITED, unless=None, use_redis=True, max_size=10000, is_fastapi=None, include_auth=False):
440
517
  """
441
518
  Bir fonksiyonun (senkron/asenkron) sonucunu cache'ler.
519
+ FastAPI endpoint'leri otomatik detekt edilir (is_fastapi=None ise) veya manuel olarak belirtilebilir.
442
520
 
443
521
  Parametreler:
444
522
  - ttl: Cache'in geçerlilik süresi (saniye). UNLIMITED ise süresizdir.
445
523
  - unless: Sonuç alınmadan önce çağrılan, True dönerse cache'e alınmaz.
446
- - is_fastapi: True ise, FastAPI Request nesnesine göre key oluşturur.
524
+ - is_fastapi: True/False ile açıkça belirt, None ise otomatik detekt (Request nesnesine bakarak).
447
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).
448
528
 
449
529
  Örnek Kullanım:
450
530
 
451
- @kekik_cache(ttl=15, unless=lambda sonuc: sonuc is None)
452
- async def bakalim(param):
453
- return param
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 {}
454
553
  """
455
554
  # Parametresiz kullanım durumunda
456
555
  if callable(ttl):
457
- return kekik_cache(UNLIMITED, unless=unless, is_fastapi=is_fastapi, use_redis=use_redis)(ttl)
556
+ return kekik_cache(UNLIMITED, unless=unless, use_redis=use_redis, max_size=max_size, is_fastapi=is_fastapi, include_auth=include_auth)(ttl)
458
557
 
459
558
  def decorator(func):
460
559
  if asyncio.iscoroutinefunction(func):
461
560
  # Asenkron fonksiyonlar için cache türünü seçelim:
462
-
463
- 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)
464
562
 
465
563
  @wraps(func)
466
564
  async def async_wrapper(*args, **kwargs):
467
- key = await make_cache_key(func, args, kwargs, is_fastapi)
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)
468
571
 
469
572
  try:
470
573
  return await func.__cache.get(key)
@@ -474,7 +577,7 @@ def kekik_cache(ttl=UNLIMITED, unless=None, is_fastapi=False, use_redis=True):
474
577
  return async_wrapper
475
578
  else:
476
579
  # Senkron fonksiyonlar için
477
- func.__cache = HybridSyncCache(ttl) if use_redis else SyncCache(ttl)
580
+ func.__cache = HybridSyncCache(ttl, max_size=max_size) if use_redis else SyncCache(ttl, max_size=max_size)
478
581
 
479
582
  @wraps(func)
480
583
  def sync_wrapper(*args, **kwargs):
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: Kekik
3
- Version: 1.7.5
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
@@ -35,6 +35,7 @@ Dynamic: description-content-type
35
35
  Dynamic: home-page
36
36
  Dynamic: keywords
37
37
  Dynamic: license
38
+ Dynamic: license-file
38
39
  Dynamic: requires-dist
39
40
  Dynamic: requires-python
40
41
  Dynamic: summary
@@ -316,6 +317,11 @@ var played = 0;
316
317
  eval_jwSetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(veri)[0]
317
318
  print(Packer.unpack(Packer.unpack(eval_jwSetup)))
318
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)
319
325
  ```
320
326
 
321
327
  ### **[CryptoJS](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/Sifreleme/CryptoJS.py)**
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: Kekik
3
- Version: 1.7.5
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
@@ -35,6 +35,7 @@ Dynamic: description-content-type
35
35
  Dynamic: home-page
36
36
  Dynamic: keywords
37
37
  Dynamic: license
38
+ Dynamic: license-file
38
39
  Dynamic: requires-dist
39
40
  Dynamic: requires-python
40
41
  Dynamic: summary
@@ -316,6 +317,11 @@ var played = 0;
316
317
  eval_jwSetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(veri)[0]
317
318
  print(Packer.unpack(Packer.unpack(eval_jwSetup)))
318
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)
319
325
  ```
320
326
 
321
327
  ### **[CryptoJS](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/Sifreleme/CryptoJS.py)**
@@ -275,6 +275,11 @@ var played = 0;
275
275
  eval_jwSetup = re.compile(r'\};\s*(eval\(function[\s\S]*?)var played = \d+;').findall(veri)[0]
276
276
  print(Packer.unpack(Packer.unpack(eval_jwSetup)))
277
277
  # 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);
278
+
279
+ # ! Veya
280
+
281
+ while Packer.detect_packed(veri):
282
+ veri = Packer.unpack(veri)
278
283
  ```
279
284
 
280
285
  ### **[CryptoJS](https://github.com/keyiflerolsun/Kekik/blob/main/Kekik/Sifreleme/CryptoJS.py)**
@@ -6,7 +6,7 @@ from io import open
6
6
  setup(
7
7
  # ? Genel Bilgiler
8
8
  name = "Kekik",
9
- version = "1.7.5",
9
+ version = "1.7.9",
10
10
  url = "https://github.com/keyiflerolsun/Kekik",
11
11
  description = "İşlerimizi kolaylaştıracak fonksiyonların el altında durduğu kütüphane..",
12
12
  keywords = ["Kekik", "KekikAkademi", "keyiflerolsun"],
@@ -1,62 +0,0 @@
1
- # ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır.
2
-
3
- import re
4
-
5
- class Packer:
6
- """
7
- P.A.C.K.E.R. sıkıştırma ve çözme işlemleri için bir sınıf.
8
- ! » https://github.com/beautifier/js-beautify/blob/main/python/jsbeautifier/unpackers/packer.py
9
- """
10
- @staticmethod
11
- def clean_escape_sequences(source: str) -> str:
12
- """Kaçış dizilerini temizler."""
13
- source = re.sub(r'\\\\', r'\\', source)
14
- source = source.replace("\\'", "'")
15
- source = source.replace('\\"', '"')
16
- return source
17
-
18
- @staticmethod
19
- def extract_arguments(source: str) -> tuple[str, list[str], int, int]:
20
- """P.A.C.K.E.R. formatındaki kaynak koddan argümanları çıkarır."""
21
- match = re.search(r"}\('(.*)',(\d+),(\d+),'(.*)'\.split\('\|'\)", source, re.DOTALL)
22
-
23
- if not match:
24
- raise ValueError("Invalid P.A.C.K.E.R. source format.")
25
-
26
- payload, radix, count, symtab = match.groups()
27
-
28
- return payload, symtab.split("|"), int(radix), int(count)
29
-
30
- @staticmethod
31
- def convert_base(s: str, base: int) -> int:
32
- """Bir sayıyı belirli bir tabandan ondalık tabana çevirir."""
33
- alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
34
-
35
- return sum(alphabet.index(char) * (base**idx) for idx, char in enumerate(reversed(s)))
36
-
37
- @staticmethod
38
- def lookup_symbol(match: re.Match, symtab: list[str], radix: int) -> str:
39
- """Sembolleri arar ve yerine koyar."""
40
- word = match[0]
41
-
42
- return symtab[Packer.convert_base(word, radix)] or word
43
-
44
- @staticmethod
45
- def unpack(source: str) -> str:
46
- """P.A.C.K.E.R. formatındaki sıkıştırılmış bir kaynağı çözer."""
47
- source = Packer.clean_escape_sequences(source)
48
-
49
- payload, symtab, radix, count = Packer.extract_arguments(source)
50
-
51
- if count != len(symtab):
52
- raise ValueError("Malformed P.A.C.K.E.R. symtab.")
53
-
54
- return re.sub(r"\b\w+\b", lambda match: Packer.lookup_symbol(match, symtab, radix), payload)
55
-
56
- @staticmethod
57
- def pack(source: str, radix: int = 62) -> str:
58
- """Bir metni P.A.C.K.E.R. formatında sıkıştırır."""
59
- # Bu işlev, simgeleri ve sıkıştırılmış metni yeniden oluşturmak için bir yol sağlar.
60
- # Ancak bu, belirli bir algoritma veya sıkıştırma tekniğine bağlıdır.
61
- # Gerçekleştirilmesi zor olabilir çünkü P.A.C.K.E.R.'ın spesifik sıkıştırma mantığını takip etmek gerekir.
62
- raise NotImplementedError("Packing function is not implemented.")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes