kickzero 1.3.1__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.
kickzero/__init__.py ADDED
@@ -0,0 +1,1038 @@
1
+ import asyncio
2
+ import aiohttp
3
+ import websockets
4
+ import json
5
+ import inspect
6
+ import colorama
7
+ import re
8
+ import sys # sys mutlaka kalmalı
9
+ from typing import Optional, Dict, Callable, Any
10
+ from colorama import Fore, Style
11
+
12
+
13
+ if "kickzero" not in sys.modules:
14
+ sys.modules["kickzero"] = sys.modules[__name__]
15
+
16
+ __all__ = ['kickbot', 'message_context', 'decorators']
17
+
18
+ class zerror:
19
+ """
20
+ ### 🇹🇷 [TR] Hata ve Log Yöneticisi (Error Logger)
21
+ Projedeki tüm konsol çıktılarını, hata mesajlarını ve uyarıları merkezi bir
22
+ noktadan yönetir. Renkli çıktılar, emojiler ve çoklu dil (TR/EN) desteği sunar.
23
+ Sınıf başlatılmadan (Singleton mantığıyla) doğrudan sınıf üzerinden kullanılır.
24
+
25
+ ### 🇺🇸 [EN] Global Error and Log Manager
26
+ Centralizes all console outputs, error messages, and warnings across the project.
27
+ Features colored outputs, emojis, and multi-language (TR/EN) support.
28
+ Operates directly on the class level (Singleton) without instantiation.
29
+
30
+ ---
31
+ ### 🛠️ Değişkenler / Attributes (Variables):
32
+
33
+ #### 🖨️ L1: Çıktı Kontrolleri / Output Toggles
34
+ - print_errors (bool): Hata mesajları gösterilsin mi? (Show error logs?)
35
+ - print_warns (bool): Uyarı mesajları gösterilsin mi? (Show warning logs?)
36
+ - print_notes (bool): Not mesajları gösterilsin mi? (Show note logs?)
37
+ - print_success (bool): Başarı mesajları gösterilsin mi? (Show success logs?)
38
+ - print_messages (bool): Standart mesajlar gösterilsin mi? (Show standard messages?)
39
+
40
+ #### 🎭 L2: Görsel Ayarlar / Visual Settings
41
+ - use_colors (bool): Çıktılar renkli mi olsun? (Enable colored terminal output?)
42
+ - use_emojis (bool): Emojiler genel olarak açık mı olsun? (Global emoji toggle?)
43
+ - preety_print (bool): Gelişmiş okunabilirlik sağlansın mı? (Use pretty formatting?)
44
+
45
+ #### 😊 L3: Detaylı Emoji Kontrolleri / Granular Emoji Toggles
46
+ - print_error_emoji (bool): Hatalarda [❌] gösterilsin mi? (Show emoji on errors?)
47
+ - print_warnn_emoji (bool): Uyarılarda [🔔] gösterilsin mi? (Show emoji on warnings?)
48
+ - print_note_emoji (bool): Notlarda [📝] gösterilsin mi? (Show emoji on notes?)
49
+ - print_succes_emoji (bool): Başarılarda [✅] gösterilsin mi? (Show emoji on success?)
50
+ - print_message_emoji (bool): Mesajlarda [💬] gösterilsin mi? (Show emoji on messages?)
51
+
52
+ #### 🌍 L4: Sistem Ayarları / System Settings
53
+ - lang (str): Çıktı dili, "tr" veya "en". (Global output language)
54
+
55
+ ---
56
+ ### ⚡ Metodlar / Methods:
57
+ - log(level, msg_tr, msg_en, msg): Belirtilen seviyeye (error, warn, note, vb.) ve
58
+ sistemin o anki diline göre formatlanmış renkli log çıktısı verir.
59
+ (Prints a color-formatted log based on the specified level and language.)
60
+ """
61
+ print_errors: bool = True
62
+ print_warns: bool = True
63
+ print_notes: bool = True
64
+ print_success: bool = True
65
+ print_error_emoji: bool = True
66
+ print_warnn_emoji: bool = True
67
+ print_note_emoji: bool = True
68
+ print_succes_emoji: bool = True
69
+ print_messages: bool = True
70
+ print_message_emoji: bool = True
71
+ pretty_print: bool = True
72
+ use_emojis: bool = True
73
+ use_colors: bool = True
74
+ lang: str = "en"
75
+ @classmethod
76
+ def log(cls: 'zerror', level: str, msg_tr: str = "", msg_en: str = "",msg: str = ""):
77
+ emoji, color = "", ""
78
+ lvl = level.lower()
79
+ if lvl in ["err", "error"] and not cls.print_errors: return
80
+ elif lvl in ["warn", "warnn", "warning"] and not cls.print_warns: return
81
+ elif lvl in ["note", "not"] and not cls.print_notes: return
82
+ elif lvl in ["succ", "success"] and not cls.print_success: return
83
+ elif lvl in ["msg", "mesaj", "message"] and not cls.print_messages: return
84
+ if cls.lang.lower() in ["tr", "en"]:
85
+ message = msg if msg else msg_tr if cls.lang.lower() == "tr" else msg_en
86
+ else:
87
+ print(f"{Fore.RED}[❌] {Style.BRIGHT}[Hata/Error] {Fore.WHITE}Dil tr veya en seçilmeli{Style.RESET_ALL}")
88
+ return
89
+ if lvl in ["err", "error"]:
90
+ level_text = "hata" if cls.lang == "tr" else "error"
91
+ emoji = "[❌] " if cls.use_emojis and cls.print_error_emoji else ""
92
+ color = Fore.RED if cls.use_colors else ""
93
+ elif lvl in ["warn", "warnn", "warning"]:
94
+ level_text = "uyarı" if cls.lang == "tr" else "warning"
95
+ emoji = "[🔔] " if cls.use_emojis and cls.print_warnn_emoji else ""
96
+ color = Fore.YELLOW if cls.use_colors else ""
97
+ elif lvl in ["note", "not"]:
98
+ level_text = "not" if cls.lang == "tr" else "note"
99
+ emoji = "[📝] " if cls.use_emojis and cls.print_note_emoji else ""
100
+ color = Fore.CYAN if cls.use_colors else ""
101
+ elif lvl in ["succ", "success"]:
102
+ level_text = "başarı" if cls.lang == "tr" else "success"
103
+ emoji = "[✅] " if cls.use_emojis and cls.print_succes_emoji else ""
104
+ color = Fore.GREEN if cls.use_colors else ""
105
+ elif lvl in ["msg", "mesaj", "message"]:
106
+ level_text = "mesaj" if cls.lang == "tr" else "message"
107
+ emoji = "[💬] " if cls.use_emojis and cls.print_message_emoji else ""
108
+ color = Fore.WHITE if cls.use_colors else ""
109
+ else:
110
+ level_text = lvl
111
+ color = Fore.WHITE
112
+ print(f"{color}{emoji}{Style.BRIGHT}[{level_text.upper()}] {Fore.WHITE}{message}{Style.RESET_ALL}")
113
+
114
+ class tasks:
115
+ """
116
+ ### 🇹🇷 [TR] Görev ve Rutin Yöneticisi
117
+ Botun arka planda çalışması gereken periyodik görevlerini (timers), başlangıç
118
+ fonksiyonlarını ve sistem kontrollerini koordine eder. Asenkron (asyncio)
119
+ yapısı sayesinde ana akışı bozmadan paralel iş yükleri oluşturur.
120
+
121
+ ### 🇺🇸 [EN] Task and Routine Manager
122
+ Coordinates the bot's background periodic tasks (timers), startup functions,
123
+ and system checks. Uses asynchronous (asyncio) operations to create parallel
124
+ workloads without interrupting the main execution flow.
125
+
126
+ ---
127
+ ### ⚡ Metodlar / Methods:
128
+
129
+ #### 🚀 L1: Başlatıcılar / Startup Executers
130
+ - run_ready_funcs(bot): Bot bağlandığında `@on_ready` ile işaretlenmiş tüm
131
+ fonksiyonları birer 'task' olarak başlatır. (Starts all @on_ready functions
132
+ as individual tasks upon connection.)
133
+
134
+ #### ⏱️ L2: Zamanlayıcılar / Timer Coordination
135
+ - run_timer_tasks(bot): `@timer_task` ile tanımlanan periyodik döngüleri kurar.
136
+ Fonksiyonun parametre alıp almadığını kontrol ederek (inspect), gerekirse
137
+ sahte bir bağlam (fake context) ile besler. (Sets up periodic loops defined
138
+ by @timer_task. Checks function signatures to feed them with a fake context
139
+ if required.)
140
+
141
+ #### 🔍 L3: Sistem Denetimi / System Check
142
+ - check(bot): Botun temel bilgilerini ve bağlı olduğu kanalları doğrulayarak
143
+ konsola durum raporu geçer. (Verifies basic bot info and connected channels,
144
+ then logs a status report to the console.)
145
+ """
146
+ @staticmethod
147
+ async def run_ready_funcs(bot:'kickbot'):
148
+ for ready_func in bot._on_ready_tasks: asyncio.create_task(ready_func())
149
+ @staticmethod
150
+ async def run_timer_tasks(bot:'kickbot'):
151
+ async def _internal_worker(task_info):
152
+ while True:
153
+ await asyncio.sleep(task_info["interval"])
154
+ try:
155
+ sig = inspect.signature(task_info["func"])
156
+ if len(sig.parameters) > 0:
157
+ fake_data = {"content":"Timer","sender":{"username":"System"}}
158
+ await task_info["func"](message_context(fake_data,bot))
159
+ else:
160
+ await task_info["func"]()
161
+ except Exception as e:
162
+ zerror.log(level="warn",
163
+ msg_tr=f"Timer Hatası ({task_info['func'].__name__}): {e}",
164
+ msg_en=f"Timer Error ({task_info['func'].__name__}): {e}")
165
+ for task in bot._timer_tasks:
166
+ asyncio.create_task(_internal_worker(task))
167
+ zerror.log(level="succ",
168
+ msg_tr="Tüm zamanlayıcılar arka planda başlatıldı.",
169
+ msg_en="All timers started in the background.")
170
+ @staticmethod
171
+ async def check(bot:'kickbot'):
172
+ if len(bot._on_ready_tasks) == 0:
173
+ zerror.log(
174
+ level="succ",
175
+ msg_tr=f"Bot {bot.user_name} adıyla giriş yaptı ve filodaki {len(bot.channels)} kanalı dinliyor!",
176
+ msg_en=f"Bot logged in as {bot.user_name} and listening to {len(bot.channels)} channels in the fleet!")
177
+
178
+ class engine:
179
+ """
180
+ ### 🇹🇷 [TR] WebSocket ve Bağlantı Motoru
181
+ Kick.com'un kullandığı Pusher (WebSocket) altyapısı ile olan tüm iletişimi
182
+ yönetir. Kanallara abone olma (subscribe), ping-pong (keep-alive) trafiği
183
+ ve gelen ham verilerin ilgili işlemcilere (processors) dağıtılmasından sorumludur.
184
+
185
+ ### 🇺🇸 [EN] WebSocket and Connection Engine
186
+ Manages all communication with the Pusher (WebSocket) infrastructure used
187
+ by Kick.com. Responsible for channel subscriptions, keep-alive (ping-pong)
188
+ traffic, and routing raw incoming data to the appropriate processors.
189
+
190
+ ---
191
+ ### ⚡ Metodlar / Methods:
192
+
193
+ #### 🔌 L1: Bağlantı Kurucu / Connection Establisher
194
+ - connect(bot): Belirlenen 'cluster' ve 'app_key' üzerinden Kick WebSocket
195
+ sunucusuna fiziksel bağlantıyı başlatır. (Establishes the physical connection
196
+ to the Kick WebSocket server using the defined cluster and app_key.)
197
+
198
+ #### 📡 L2: Radar Abonelikleri / Subscription Services
199
+ - subscribe_to_chatroom(ch, ws): Sohbet mesajlarını ve mod aksiyonlarını dinlemek
200
+ için kanala abone olur. (Subscribes to the channel to listen for chat messages
201
+ and mod actions.)
202
+ - subscribe_to_channel_points(ch, ws): Kanal puanı (Reward) kullanımlarını yakalamak
203
+ için abone olur. (Subscribes to capture channel point/reward redemptions.)
204
+ - subscribe_to_channel_events(ch, ws): [Beta] Takipçi ve diğer kanal olaylarını
205
+ yakalamak için abone olur. (Subscribes to capture followers and other channel events.)
206
+ - subscribe_all(bot, ws): Filodaki tüm benzersiz kanallar için tüm abonelik
207
+ türlerini topluca başlatır. (Bulk starts all subscription types for every
208
+ unique channel in the fleet.)
209
+
210
+ #### 💓 L3: Yaşam Sinyali / Keep Alive
211
+ - keep_alive(bot, ws): Sunucudan gelen 'ping' sinyallerine 'pong' ile yanıt
212
+ vererek bağlantının kopmasını engeller. (Prevents connection loss by responding
213
+ with 'pong' to incoming 'ping' signals from the server.)
214
+
215
+ #### 🚥 L4: Olay Dağıtıcı / Event Dispatcher
216
+ - process_all_events(bot, ws): Sürekli dinleme yaparak gelen verinin türünü
217
+ ayrıştırır (Chat, Reward, Ping) ve ilgili işlemciye yönlendirir. (Constantly
218
+ listens to parse the event type and routes it to the specific processor.)
219
+ """
220
+ @staticmethod
221
+ def connect(bot):
222
+ websocket = websockets.connect(uri=f"wss://ws-{bot.cluster}.pusher.com/app/{bot.app_key}?protocol=7&client=js&version=7.6.0")
223
+ return websocket
224
+ @staticmethod
225
+ async def subscribe_to_chatroom(ch, websocket):
226
+ await websocket.send(json.dumps({ "event": "pusher:subscribe","data": {"channel": f"chatrooms.{ch.chat_id}.v2"}})) # Mod actions ve chat
227
+ @staticmethod
228
+ async def subscribe_to_channel_points(ch, websocket):
229
+ await websocket.send(json.dumps({ "event": "pusher:subscribe","data": {"channel": f"chatroom_{ch.chat_id}"}}))
230
+ """ st Untested Beta"""
231
+ @staticmethod
232
+ async def subscribe_to_channel_events(ch, websocket): # Kicks puanları ve takipler
233
+ await websocket.send(json.dumps({"event": "pusher:subscribe","data": {"channel": f"channel_{ch.channel_id}"}}))
234
+ """ en Untested Beta """
235
+
236
+ @staticmethod
237
+ async def subscribe_all(bot, websocket):
238
+ # ⚓ DÜZELTME: Sadece isimle kaydedilen gerçek objeleri alıyoruz
239
+ unique_channels = []
240
+ for key, value in bot.channels.items():
241
+ if not key.isdigit() and value not in unique_channels:
242
+ unique_channels.append(value)
243
+
244
+ for ch in unique_channels:
245
+ await engine.subscribe_to_chatroom(ch, websocket)
246
+ await engine.subscribe_to_channel_points(ch, websocket)
247
+ await engine.subscribe_to_channel_events(ch, websocket)
248
+
249
+ zerror.log(level="note", msg_tr=f"[{ch.name}] Radar abonelikleri tamamlandı!", msg_en=f"[{ch.name}] Radar subscriptions completed!")
250
+ @staticmethod
251
+ async def keep_alive(bot,websocket):
252
+ await websocket.send(json.dumps({"event": "pusher:pong"}))
253
+ @staticmethod
254
+ async def process_all_events(bot,websocket):
255
+ raw_data = await websocket.recv()
256
+ data = json.loads(raw_data)
257
+ if data.get("event") == "App\\Events\\ChatMessageEvent":
258
+ await processor.process_chat(bot,raw_data)
259
+ elif data.get("event") == "RewardRedeemedEvent":
260
+ await processor.process_channel_points(bot,raw_data)
261
+ elif data.get("event") == "pusher:ping":
262
+ await engine.keep_alive(bot,websocket)
263
+
264
+ class processor:
265
+ """
266
+ ### 🇹🇷 [TR] Veri İşleme ve Olay Dağıtıcı (Event Dispatcher)
267
+ Kick sunucularından gelen ham WebSocket verilerini analiz eder, uygun bağlam
268
+ (context) nesnelerini oluşturur ve tetikleyicileri (commands, messages, rewards)
269
+ çalıştırır. Botun mantıksal karar merkezidir.
270
+
271
+ ### 🇺🇸 [EN] Data Processor and Event Dispatcher
272
+ Analyzes raw WebSocket data from Kick servers, creates appropriate context
273
+ objects, and executes triggers (commands, messages, rewards). It serves
274
+ as the logical decision center of the bot.
275
+
276
+ ---
277
+ ### ⚡ Metodlar / Methods:
278
+
279
+ #### ⚔️ L1: Dinamik Fonksiyon Çalıştırıcı / Dynamic Executor
280
+ - execute(fx, ctx, args): Hedef fonksiyonun parametre yapısını (inspect)
281
+ analiz eder ve uygun argümanlarla güvenli bir şekilde çalıştırır.
282
+ (Analyzes the target function's signature and executes it safely
283
+ with the appropriate arguments.)
284
+
285
+ #### 💬 L2: Sohbet İşleyici / Chat Processor
286
+ - process_chat(bot, raw_data): Gelen sohbet verisini çözümler; canlı sohbeti
287
+ ekrana basar, genel mesaj izleyicilerini, özel kelime tetikleyicilerini
288
+ ve komutları paralel olarak tetikler. (Parses incoming chat data; logs
289
+ live chat, triggers global watchers, word triggers, and commands in parallel.)
290
+
291
+ #### 💎 L3: Kanal Ödülü İşleyici / Reward Processor
292
+ - process_channel_points(bot, raw_data): Kanal puanı ile alınan ödülleri
293
+ yakalar; ödül ismine göre kayıtlı fonksiyonları bulur ve ödülü kullanan
294
+ kişinin bilgilerini ilgili göreve iletir. (Captures reward redemptions;
295
+ matches reward titles with registered functions and forwards user info to the task.)
296
+ """
297
+ @staticmethod
298
+ async def execute(fx,ctx,args):
299
+ try:
300
+ sig = inspect.signature(fx)
301
+ params_count = len(sig.parameters)
302
+ if params_count == 2: await fx(ctx,args)
303
+ elif params_count == 1: await fx(ctx)
304
+ else: await fx()
305
+ except Exception as e:
306
+ zerror.log(level="error", msg_tr=f"{fx.__name__} çalışırken hata: {e}", msg_en=f"Error running {fx.__name__}: {e}")
307
+ @staticmethod
308
+ async def process_chat(bot:'kickbot',raw_data):
309
+ data = json.loads(raw_data)
310
+ inner_data = json.loads(data["data"])
311
+ ctx = message_context(inner_data,bot)
312
+ async def display_messages():
313
+ if bot.display_live_chat and (not ctx.is_bot or bot.display_bot_messages):
314
+ perms = " | ".join(ctx.badge_texts) if ctx.badge_texts else (r"İzleyici" if zerror.lang == "tr" else r"Viewer")
315
+ msg_log = f"{Fore.YELLOW}{ctx.author}{Fore.WHITE}: {ctx.content} {Fore.BLACK}({perms})"
316
+ zerror.log(level="message", msg=msg_log)
317
+ async def process_message_funcs():
318
+ if not (bot.filter_bot_messages and ctx.is_bot):
319
+ for watcher in bot._on_message_tasks:
320
+ asyncio.create_task(processor.execute(watcher,ctx,[]))
321
+ if ctx.is_bot: return
322
+ for trigger,configs in bot._message_handlers.items():
323
+ for config in configs:
324
+ if ctx.is_bot and not config.get("execute_bot",False): continue
325
+ is_lower = config.get("lower",True)
326
+ msg_c = ctx.content.lower() if is_lower else ctx.content
327
+ trig_c = trigger.lower() if is_lower else trigger
328
+ is_trig = (msg_c == trig_c) if config.get("exact",True) else msg_c.startswith(trig_c)
329
+ if is_trig:
330
+ args = ctx.content.split()[len(trig_c.split()):]
331
+ asyncio.create_task(processor.execute(config["func"],ctx,args))
332
+ async def process_command_funcs():
333
+ if ctx.content.startswith(bot.prefix):
334
+ parts = ctx.content[len(bot.prefix):].split()
335
+ if parts:
336
+ cmd_raw = parts[0]
337
+ cmd_lower = parts[0].lower()
338
+ cmd_configs = bot._commands.get(cmd_raw, []) + (bot._commands.get(cmd_lower,[]) if cmd_lower != cmd_raw else [])
339
+ for cmd_info in cmd_configs:
340
+ if ctx.is_bot and not cmd_info.get("execute_bot",False): continue
341
+ asyncio.create_task(processor.execute(cmd_info["func"],ctx,parts[1:]))
342
+ asyncio.create_task(display_messages())
343
+ asyncio.create_task(process_message_funcs())
344
+ asyncio.create_task(process_command_funcs())
345
+ @staticmethod
346
+ async def process_channel_points(bot:'kickbot',raw_data):
347
+ data = json.loads(raw_data)
348
+ rctx = points_context(data,bot)
349
+ ### Log
350
+ log_msg = f"{Fore.MAGENTA}{rctx.username}{Fore.WHITE}, {Fore.GREEN}'{rctx.title}'{Fore.WHITE} ödülünü kullandı! {Fore.BLACK}(Kanal: {rctx.channel})"
351
+ if rctx.input:
352
+ log_msg += f" {Fore.CYAN}(Mesaj: {rctx.input})"
353
+ ## Dedektör Taraması
354
+ title_lower = rctx.title.lower()
355
+ reward_configs = bot._reward_handlers.get(title_lower, [])
356
+ for config in reward_configs:
357
+ asyncio.create_task(processor.execute(config["func"], rctx, []))
358
+ zerror.log(level="succ", msg=log_msg)
359
+
360
+ ### contexts
361
+ class message_context:
362
+ """
363
+ ### 🇹🇷 [TR] Bağlam Merkezi (context)
364
+ Kick.com API'den gelen verileri, botun o anki çalışma durumuyla birleştirir.
365
+ Bu sınıf; mesajın içeriğine, gönderen kişinin yetkilerine ve botun fonksiyonlarına
366
+ tek bir noktadan (`ctx`) erişim sağlar.
367
+
368
+ ### 🇺🇸 [EN] Command context
369
+ Unifies data from the Kick.com API with the bot's current operational state.
370
+ This class provides a single point of access (`ctx`) to message content,
371
+ sender permissions, and bot methods.
372
+
373
+ ---
374
+ ### 🛠️ Değişkenler / Attributes (Variables):
375
+
376
+ #### 📦 L1: Akış Verileri / Stream Data
377
+ - id (str): Mesajın benzersiz kimliği. (Unique message ID)
378
+ - chatroom_id (int): Mesajın düştüğü odanın ID'si. (Target chatroom ID)
379
+ - content (str): Mesajın ham metni. (Raw message content)
380
+ - created_at (str): Gönderilme zamanı. (Creation timestamp)
381
+
382
+ #### 👤 L2: Aktör / The Actor (Sender)
383
+ - author (str): Kullanıcı adı. (Sender's username)
384
+ - author_id (int): Kullanıcının kalıcı sayısal ID'si. (User's permanent ID)
385
+ - slug (str): URL uyumlu kullanıcı adı. (Sender's URL slug)
386
+
387
+ #### 🎨 L3: Görsel Kimlik / Visual Identity
388
+ - color (str): Kullanıcı renk kodu. (User hex color)
389
+ - badges (list): Sahip olunan ham rozetler. (Raw badge list)
390
+ - badge_texts (list): Rozetlerin isimleri. (Badge display names)
391
+
392
+ #### 🛡️ L4: Yetki Kalkanları / Permission Shields
393
+ - is_broadcaster (bool): Kanal sahibi mi? (Is broadcaster?)
394
+ - is_mod (bool): Moderatör mü? (Is moderator?)
395
+ - is_sub (bool): Abone mi? (Is subscriber?)
396
+ - is_vip (bool): VIP mi? (Is VIP?)
397
+ - is_staff (bool): Kick görevlisi mi? (Is Kick staff?)
398
+ - is_verified (bool): Onaylı hesap mı? (Is verified?)
399
+
400
+ #### 🤖 L5: Sistem Kontrolü / System Check
401
+ - bot (kickbot): Ana bot sınıfına olan bağlantı. (Reference to main kickbot instance)
402
+ - is_bot (bool): Bu mesajı botun kendisi mi attı? (Did the bot send this message?)
403
+
404
+ ---
405
+ ### ⚡ Metodlar / Methods:
406
+ - reply(content): Kullanıcıyı etiketleyerek cevap verir. (Reply with mention)
407
+ - send(content): Kanala düz metin gönderir. (Send plain text)
408
+ """
409
+ bot: 'kickbot'
410
+ id: str
411
+ chatroom_id: int
412
+ content: str
413
+ type: str
414
+ created_at: str
415
+ sender_id: int
416
+ author: str
417
+ slug: str
418
+ color: str
419
+ badges: list
420
+ badge_texts: list
421
+ is_broadcaster: bool
422
+ is_mod: bool
423
+ is_sub: bool
424
+ is_vip: bool
425
+ is_staff: bool
426
+ is_verified: bool
427
+ is_og: bool
428
+ metadata: dict
429
+ message_ref: str
430
+ is_bot: bool
431
+
432
+ #Garanti seviyeler, ekle
433
+ def __init__(self,data:dict,bot:'kickbot'):
434
+ self.bot = bot
435
+ # --- L1: Ana Veriler ---
436
+ self.id = data.get("id")
437
+ self.chatroom_id = data.get("chatroom_id")
438
+ self.content = data.get("content")
439
+ self.type = data.get("type")
440
+ self.created_at = data.get("created_at")
441
+ # --- L2: Gönderici (Sender) --
442
+ Sender = data.get("sender",{})
443
+ self.sender_id = Sender.get("id")
444
+ self.author = Sender.get("username")
445
+ self.slug = Sender.get("slug")
446
+ # --- L3: Kimlik ve Rozetler (Identity) ---
447
+ Identity = Sender.get("identity",{})
448
+ self.color = Identity.get("color")
449
+ self.badges = Identity.get("badges",[])
450
+ # --- L4: Yetki ve Rozet İşleme ---
451
+ BadgeTypes = [badge.get("type") for badge in self.badges]
452
+ self.is_broadcaster = "broadcaster" in BadgeTypes
453
+ self.is_mod = "moderator" in BadgeTypes
454
+ self.is_sub = "subscriber" in BadgeTypes
455
+ self.is_vip = "vip" in BadgeTypes
456
+ self.is_staff = "staff" in BadgeTypes
457
+ self.is_verified = "verified" in BadgeTypes
458
+ self.is_og = "og" in BadgeTypes
459
+ #L4.1 İleride Eklenicek
460
+ self.badge_texts = [badge.get("text") for badge in self.badges]
461
+ # --- L5: Meta Veri (Metadata) ---
462
+ self.metadata = data.get("metadata", {})
463
+ self.message_ref = self.metadata.get("message_ref")
464
+ # |
465
+ AuthorName = str(self.author).lower()
466
+ BotName = str(self.bot.user_name).lower()
467
+ self.is_bot = (AuthorName == BotName)
468
+ # ---Ş6 Channel
469
+ self.channel = self.bot._get_channel(self.chatroom_id)
470
+ async def reply(self, content: str):
471
+ if hasattr(self, 'channel') and self.channel and type(self.channel) != str:
472
+ return await self.channel.send(f"@{self.author} {content}")
473
+ zerror.log(level="error", msg_tr="Kanal bulunamadı! Timer görevlerinden mesaj atıyorsanız ctx.bot.find_channel('isim').send() kullanmalısınız.")
474
+
475
+ async def send(self, content: str):
476
+ if hasattr(self, 'channel') and self.channel and type(self.channel) != str:
477
+ return await self.channel.send(content)
478
+
479
+ # Kanalı belli değilse ana gemiye at
480
+ return await self.bot.send_message(content)
481
+ class points_context:
482
+ """
483
+ ### 🇹🇷 [TR] Kanal Puanı Bağlamı (Reward Context)
484
+ Kick.com üzerindeki sadakat puanı (Stream Rewards) kullanımlarını temsil eder.
485
+ Ödülü kimin, hangi kanalda ve hangi girdiyle (input) kullandığını takip eder.
486
+
487
+ ### 🇺🇸 [EN] Reward Redemption Context
488
+ Represents stream reward redemptions on Kick.com. Tracks who used the
489
+ reward, in which channel, and with what user input.
490
+
491
+ ---
492
+ ### 🛠️ Değişkenler / Attributes (Variables):
493
+
494
+ #### 💎 L1: Ödül Detayları / Reward Details
495
+ - title (str): Kullanılan ödülün tam adı. (Title of the redeemed reward)
496
+ - input (str): Kullanıcının ödülle birlikte gönderdiği mesaj. (User's input message)
497
+ - color (str): Ödülün paneldeki arka plan renk kodu. (Reward's hex background color)
498
+
499
+ #### 👤 L2: Aktör / The Actor (User)
500
+ - username (str): Ödülü kullanan kişinin adı. (Username of the redeemer)
501
+ - user_id (int): Kullanıcının sayısal ID'si. (Permanent ID of the user)
502
+
503
+ #### 📡 L3: Konum / Location
504
+ - channel (channel_context): Ödülün tetiklendiği kanal objesi. (Channel object where the reward was used)
505
+ - channel_id (int): Kanalın sayısal ID'si. (Permanent ID of the channel)
506
+
507
+ #### 🤖 L4: Sistem / System
508
+ - bot (kickbot): Ana bot motoruna erişim. (Reference to the main kickbot instance)
509
+
510
+ ---
511
+ ### ⚡ Metodlar / Methods:
512
+ - reply(content): Ödülü kullanan kişiyi etiketleyerek cevap verir. (Reply with mention)
513
+ - send(content): Ödülün kullanıldığı kanala düz metin gönderir. (Send plain text)
514
+ """
515
+ bot: 'kickbot'
516
+ title: str
517
+ user_id: int
518
+ channel_id: int
519
+ username: str
520
+ user_input: str
521
+ color: str
522
+ channel: dict
523
+ def __init__(self,data: dict,bot:'kickbot'):
524
+ self.bot = bot
525
+ self.event = data.get("event", "")
526
+ raw_inner = data.get("data", "{}")
527
+ inner_data:dict = json.loads(raw_inner) if isinstance(raw_inner, str) else raw_inner
528
+ self.title = inner_data.get("reward_title", "")
529
+ self.user_id = inner_data.get("user_id", 0)
530
+ self.channel_id = inner_data.get("channel_id", 0)
531
+ raw_channel_str = data.get("channel", "")
532
+ extracted_chat_id = raw_channel_str.split("_")[-1] if "_" in raw_channel_str else raw_channel_str
533
+ self.channel = self.bot._get_channel(extracted_chat_id)
534
+ self.username = inner_data.get("username", "")
535
+ self.input = inner_data.get("user_input", "")
536
+ self.color = inner_data.get("reward_background_color", "")
537
+ async def reply(self,content:str):
538
+ return await self.bot.send_message(f"@{self.username} {content}")
539
+ async def send(self,content:str):
540
+ return await self.bot.send_message(f"{content}")
541
+ class channel_context:
542
+ """
543
+ ### 🇹🇷 [TR] Kanal Bağlamı (Channel Context)
544
+ Botun bağlı olduğu her bir bağımsız kanalı (odayı) temsil eder.
545
+ Kanal bazlı mesaj gönderme işlemlerinin merkezidir.
546
+
547
+ ### 🇺🇸 [EN] Channel Context
548
+ Represents each independent channel (room) the bot is connected to.
549
+ The hub for channel-specific message sending operations.
550
+
551
+ ---
552
+ ### 🛠️ Değişkenler / Attributes (Variables):
553
+
554
+ #### ⚓ L1: Kimlik Bilgileri / Identity
555
+ - name (str): Kanalın görünen adı/kullanıcı adı. (Channel's display name)
556
+ - channel_id (int): Kanalın ana sayısal ID'si. (Main channel ID)
557
+ - chat_id (int): Sohbet odasının benzersiz API ID'si. (Unique API chatroom ID)
558
+
559
+ #### 🤖 L2: Sistem / System
560
+ - bot (kickbot): Bağlı olduğu ana bot sınıfı. (The main kickbot instance it belongs to)
561
+
562
+ ---
563
+ ### ⚡ Metodlar / Methods:
564
+ - send(content): Bu kanala özel asenkron mesaj gönderir. (Sends an async message specifically to this channel.)
565
+ """
566
+ def __init__(self, bot:'kickbot',name:'str',channel_id: int,chat_id:int):
567
+ self.bot = bot
568
+ self.name:str = name
569
+ self.channel_id: int = channel_id
570
+ self.chat_id: int = chat_id
571
+ async def send(self,content:str):
572
+ url = f"https://kick.com/api/v2/messages/send/{self.chat_id}"
573
+ headers = {"Authorization": self.bot.bearer_token,"Content-Type": "application/json","User-Agent": "Mozilla/5.0"}
574
+ payload = {"content": str(content), "type": "message"}
575
+ async with aiohttp.ClientSession() as session:
576
+ async with session.post(url, json=payload, headers=headers) as resp:
577
+ if resp.status not in [200, 201]:
578
+ zerror.log(level="error",
579
+ msg_tr=f"[{self.name}] Mesaj gönderilemedi! Kod: {resp.status}",
580
+ msg_en=f"[{self.name}] Failed to send message! Code: {resp.status}")
581
+ return resp.status
582
+
583
+ class decorators:
584
+ @classmethod
585
+ def command(cls, name: str = None, lower: bool = True, execute_bot: bool = False):
586
+ """
587
+ ### 🇹🇷 [TR] Komut Kaydedici
588
+ Prefix (!) ile başlayan komutları listeye ekler.
589
+ Aynı isimde birden fazla fonksiyon tanımlanabilir.
590
+
591
+ ### 🇺🇸 [EN] Command Registerer
592
+ Registers prefix (!) commands into a list.
593
+ Multiple functions can be defined under the same name.
594
+ """
595
+ def Decorator(fx: Callable):
596
+ RawName = name if name else fx.__name__
597
+ CommandName = RawName.lower() if lower else RawName
598
+ # ⚓ Liste kontrolü ve ekleme
599
+ if CommandName not in kickbot._commands:
600
+ kickbot._commands[CommandName] = []
601
+ kickbot._commands[CommandName].append({"func": fx, "execute_bot": execute_bot})
602
+ return fx
603
+ return Decorator
604
+ @classmethod
605
+ def message(cls, content: str, exact: bool = None, lower: bool = True, execute_bot: bool = False):
606
+ """
607
+ ### 🇹🇷 [TR] Kelime Takip Kaydedici
608
+ Belirli kelimeleri bir listeye ekler. Birden fazla fonksiyon aynı kelimeyi dinleyebilir.
609
+
610
+ ### 🇺🇸 [EN] Word Watcher Registerer
611
+ Adds specific words to a list. Multiple functions can listen to the same word.
612
+ """
613
+ def Decorator(fx: Callable):
614
+ sig = inspect.signature(fx)
615
+ final_exact = (len(sig.parameters) == 1) if exact is None else exact
616
+ if content not in kickbot._message_handlers:
617
+ kickbot._message_handlers[content] = []
618
+ kickbot._message_handlers[content].append({
619
+ "func": fx, "exact": final_exact, "lower": lower, "execute_bot": execute_bot
620
+ })
621
+ return fx
622
+ return Decorator
623
+ @classmethod
624
+ def on_message(cls):
625
+ """
626
+ ### 🇹🇷 [TR] Genel Mesaj İzleyici
627
+ Tüm mesajları dinleyen fonksiyonları 'liste'ye ekler.
628
+
629
+ ### 🇺🇸 [EN] Global Message Watcher
630
+ Appends functions listening to all messages to the 'list'.
631
+ """
632
+ def Decorator(func: Callable):
633
+ kickbot._on_message_tasks.append(func)
634
+ return func
635
+ return Decorator
636
+ @classmethod
637
+ def on_ready(cls):
638
+ """
639
+ ### 🇹🇷 [TR] Hazır Olma Görevleri
640
+ Bot açıldığında çalışacak görevleri listeye ekler.
641
+
642
+ ### 🇺🇸 [EN] On Ready Tasks
643
+ Appends tasks to execute when bot is ready to the list.
644
+ """
645
+ def Decorator(func: Callable):
646
+ kickbot._on_ready_tasks.append(func)
647
+ return func
648
+ return Decorator
649
+ @classmethod
650
+ def timer_task(cls, hours: int = 0, minutes: int = 0, seconds: int = 0):
651
+ """
652
+ ### 🇹🇷 [TR] Zamanlanmış Görevler
653
+ Periyodik görevleri paketleyip listeye ekler.
654
+ """
655
+ def Decorator(fx: Callable):
656
+ total_time = (hours * 3600) + (minutes * 60) + seconds
657
+ if total_time > 0:
658
+ kickbot._timer_tasks.append({"func": fx, "interval": total_time})
659
+ return fx
660
+ return Decorator
661
+ ## ⚓
662
+ @classmethod
663
+ def on_rewards_redemption(cls,title:str):
664
+ def Decorator(fx: Callable):
665
+ title_lower = title.lower()
666
+ if title_lower not in kickbot._reward_handlers:
667
+ kickbot._reward_handlers[title_lower] = []
668
+ kickbot._reward_handlers[title_lower].append({"func":fx})
669
+ return fx
670
+ return Decorator
671
+
672
+ # ⚓
673
+ class kickbot:
674
+ _commands: Dict[str, list] = {}
675
+ _message_handlers: Dict[str, list] = {}
676
+ _timer_tasks: list = []
677
+ _on_message_tasks: list = []
678
+ _on_ready_tasks: list = []
679
+ _reward_handlers: Dict[str, list] = {}
680
+
681
+ # ⚓ GEREKSİZ DEĞİŞKENLER SİLİNDİ, SADECE TEMEL TAŞLAR KALDI
682
+ user_name: str = ""
683
+ bearer_token: str = ""
684
+ app_key: str = ""
685
+ cluster: str = "us2"
686
+ prefix: str = "!"
687
+
688
+ def __init__(self, user_name, bearer_token, **kwargs): # ⚓ channel_name parametresi silindi!
689
+ _raw_lang = kwargs.get("framework_lang", "en")
690
+ if not isinstance(_raw_lang, str):
691
+ print(f"[❌] [ERROR/HATA] framework_lang must be a string! | framework_lang bir metin (string) olmalı! (Provided/Verilen: {type(_raw_lang).__name__})")
692
+ sys.exit(1)
693
+ zerror.lang = _raw_lang.lower()
694
+
695
+ def __validate(param_value, expected_types: list, param_name: Optional[str] = "",param_name_tr: Optional[str] = "",param_name_en: Optional[str]=""):
696
+ is_invalid_bool = isinstance(param_value, bool) and bool not in expected_types
697
+ if not any(isinstance(param_value, t) for t in expected_types) or is_invalid_bool:
698
+ _is_tr = zerror.lang.lower() == "tr"
699
+ _sep = " veya " if _is_tr else " or "
700
+ _final_name = (param_name_tr if _is_tr else param_name_en) if param_name == "" else param_name
701
+ types_str = _sep.join([t.__name__ for t in expected_types])
702
+ zerror.log(level="error",
703
+ msg_tr=f"{_final_name} şu tiplerden biri olmalı: {types_str}! (Verilen: {type(param_value).__name__})",
704
+ msg_en=f"{_final_name} must be one of these: {types_str}! (Provided: {type(param_value).__name__})")
705
+ zerror.log(level="warn", msg_tr=f"Çıkış yapılıyor...", msg_en=f"Exiting...")
706
+ sys.exit(1)
707
+
708
+ __validate(user_name,[str],param_name_tr="Kullanıcı Adı",param_name_en="User Name"); self.user_name = user_name.lower()
709
+ __validate(bearer_token, [str], param_name="Bearer Token"); _bt = bearer_token.strip(); self.bearer_token = f"Bearer {_bt[7:].strip()}" if _bt.lower().startswith("bearer ") else f"Bearer {_bt}"
710
+
711
+ if "prefix" in kwargs:
712
+ _val = kwargs.get("prefix","!")
713
+ __validate(_val,[str,int], param_name="Prefix")
714
+ self.prefix = _val
715
+ else:
716
+ self.prefix = '!'
717
+
718
+ if "cluster" in kwargs:
719
+ _val = kwargs.get("cluster","us2")
720
+ __validate(_val,[str],param_name="Cluster")
721
+ self.cluster = _val
722
+ else:
723
+ self.cluster = "us2"
724
+
725
+ if "display_live_chat" in kwargs:
726
+ _val = kwargs.get("display_live_chat",True)
727
+ __validate(_val,[bool],param_name="DisplayLiveChat")
728
+ self.display_live_chat = _val
729
+ else:
730
+ self.display_live_chat = True
731
+
732
+ if "display_bot_messages" in kwargs:
733
+ _val = kwargs.get("display_bot_messages",True)
734
+ __validate(_val,[bool],param_name="DisplayBotMessages")
735
+ self.display_bot_messages = _val
736
+ else:
737
+ self.display_bot_messages = True
738
+
739
+ if "filter_bot_messages" in kwargs:
740
+ _val = kwargs.get("filter_bot_messages",True)
741
+ __validate(_val,[bool],param_name="filter_bot_messages")
742
+ self.filter_bot_messages = _val
743
+ else:
744
+ self.filter_bot_messages = True
745
+
746
+ if "app_key" in kwargs:
747
+ _val = kwargs.get("app_key",False)
748
+ __validate(_val,[str,int],param_name="App Key")
749
+ self.app_key = _val
750
+ else:
751
+ self.app_key = 0
752
+
753
+ # ⚓ SADECE BOŞ FİLO SÖZLÜĞÜ KALDI (Kullanıcı add_channel ile dolduracak)
754
+ self.channels: Dict[str, 'channel_context'] = {}
755
+ # --- ⭐ SARI METODLAR (Yıldızlı Yetenekler) ---
756
+
757
+ """"""
758
+
759
+ def add_channel(self,name:str,channel_id:int,chat_id:int):
760
+ channel = channel_context(self,name,channel_id,chat_id)
761
+ self.channels[name.lower()] = channel
762
+ self.channels[str(channel_id)] = channel
763
+ self.channels[str(chat_id)] = channel
764
+ zerror.log(level="note", msg_tr=f"Yeni kanal eklendi: {name}")
765
+ return channel
766
+ def _get_channel(self,identifier) -> Optional['channel_context']:
767
+ return self.channels.get(str(identifier).lower())
768
+ def find_channel(self, identifier) -> Optional['channel_context']:
769
+ """Kanalı isminden veya ID'sinden bulup objesini döndürür (get_channel ile aynıdır)."""
770
+ return self._get_channel(identifier)
771
+
772
+ """"""
773
+
774
+ def command(self, name: str = None, *, lower: bool = True, execute_bot: bool = False):
775
+ """
776
+ ### 🇹🇷 [TR] Komut Kaydedici
777
+ Prefix (örn: !) ile başlayan tetikleyicileri bir listeye ekler.
778
+ Aynı isimde birden fazla fonksiyon tanımlanabilir.
779
+
780
+ ### 🇺🇸 [EN] Command Registerer
781
+ Registers triggers starting with a prefix (e.g., !) into a list.
782
+ Multiple functions can be defined under the same command name.
783
+
784
+ ---
785
+ **Args:**
786
+ - name (str): 🇹🇷 Komut ismi (örn: 'selam'). Boş bırakılırsa fonksiyon adını alır. / 🇺🇸 Command name.
787
+ - lower (bool): 🇹🇷 True ise '!SELAM' ve '!selam' aynı kabul edilir. / 🇺🇸 Case insensitivity.
788
+ - execute_bot (bool): 🇹🇷 Botların bu komutu kullanmasına izin verir. / 🇺🇸 Allows bots to trigger this.
789
+ """
790
+ return decorators.command(name, lower=lower, execute_bot=execute_bot)
791
+
792
+ def message(self, content: str, *, exact: bool = None, lower: bool = True, execute_bot: bool = False):
793
+ """
794
+ ### 🇹🇷 [TR] Kelime/Mesaj İzleyici Kaydedici
795
+ Belirli bir kelime veya cümle chate yazıldığında tetiklenecek fonksiyonları kaydeder.
796
+
797
+ ### 🇺🇸 [EN] Word/Message Watcher Registerer
798
+ Registers functions to be triggered when a specific word or phrase is typed in chat.
799
+
800
+ ---
801
+ **Args:**
802
+ - content (str): 🇹🇷 Takip edilecek kelime. / 🇺🇸 Word to follow.
803
+ - exact (bool): 🇹🇷 Tam eşleşme mi? (True: Sadece 'sa', False: 'sa nasılsın' içinde de yakalar). / 🇺🇸 Exact match?
804
+ - lower (bool): 🇹🇷 Büyük/küçük harf duyarsızlığı. / 🇺🇸 Case insensitivity.
805
+ - execute_bot (bool): 🇹🇷 Bot mesajları bu izleyiciyi tetiklesin mi? / 🇺🇸 Should bot messages trigger this?
806
+ """
807
+ return decorators.message(content, exact, lower, execute_bot)
808
+
809
+ def on_message(self):
810
+ """
811
+ ### 🇹🇷 [TR] Genel Mesaj İzleyici Kaydedici
812
+ Gelen her mesajda (komut olsun ya da olmasın) çalışacak fonksiyonları bir listeye ekler.
813
+ Artık birden fazla genel izleyici tanımlayabilirsiniz.
814
+
815
+ ### 🇺🇸 [EN] Global Message Watcher Registerer
816
+ Appends functions to a list that will execute on every incoming message
817
+ (whether it's a command or not). Multiple global watchers can now be defined.
818
+
819
+ ---
820
+ **Args:** (Yok / None)
821
+ """
822
+ return decorators.on_message()
823
+
824
+ def on_ready(self):
825
+ """
826
+ ### 🇹🇷 [TR] Hazır Olma Görevi Kaydedici
827
+ Bot Kick sunucularına başarıyla bağlandığında çalışacak fonksiyonları listeye ekler.
828
+ Artık birden fazla 'on_ready' fonksiyonu tanımlayabilirsiniz.
829
+
830
+ ### 🇺🇸 [EN] On Ready Task Registerer
831
+ Appends functions to a list that will execute once the bot successfully
832
+ connects to Kick servers. Multiple 'on_ready' functions can now be defined.
833
+
834
+ ----
835
+ **Args:** (None / Yok)
836
+ """
837
+ return decorators.on_ready()
838
+
839
+ def timer_task(self, hours: int = 0, minutes: int = 0, seconds: int = 0):
840
+ """
841
+ ### 🇹🇷 [TR] Zamanlanmış Görev Kaydedici
842
+ Belirlenen saat, dakika veya saniye aralıklarıyla sürekli çalışacak fonksiyonları listeye ekler.
843
+
844
+ ### 🇺🇸 [EN] Scheduled Task Registerer
845
+ Appends functions to a list that will execute repeatedly at defined
846
+ hour, minute, or second intervals.
847
+
848
+ ---
849
+ **Args:**
850
+ - hours (int): 🇹🇷 Kaç saatte bir çalışsın? / 🇺🇸 Every X hours.
851
+ - minutes (int): 🇹🇷 Kaç dakikada bir çalışsın? / 🇺🇸 Every X minutes.
852
+ - seconds (int): 🇹🇷 Kaç saniyede bir çalışsın? / 🇺🇸 Every X seconds.
853
+ """
854
+ return decorators.timer_task(hours, minutes, seconds)
855
+
856
+ """"""
857
+
858
+ def on_rewards_redemption(self,title:str):
859
+ return decorators.on_rewards_redemption(title=title)
860
+
861
+
862
+ r"""
863
+ ### 🛡️ [DEPRECATED / V2.0] - Emekli Metodlar (Legacy Methods)
864
+
865
+ 🇹🇷 [TR] Aşağıdaki `__fetch_chat_id` ve `__fetch_app_key` metodları, Kick'in Cloudflare korumasını
866
+ artırması ve dinamik yapıya geçmesi nedeniyle v2.0 sürümüyle birlikte emekli edilmiştir.
867
+ Bağlantı kararlılığı için `chat_id` ve `app_key` parametrelerinin manuel girilmesi zorunludur.
868
+
869
+ 🇺🇸 [EN] The following `__fetch_chat_id` and `__fetch_app_key` methods have been deprecated
870
+ with v2.0 due to enhanced Cloudflare protections and Kick's dynamic infrastructure.
871
+ For connection stability, `chat_id` and `app_key` parameters must now be provided manually.
872
+
873
+
874
+ @classmethod
875
+ async def __fetch_chat_id(cls) -> bool:
876
+ ---
877
+ ### 🇹🇷 [TR] Gizli Koordinat Belirleyici (Name Mangling)
878
+ Hedef kanalın (`channel_name`) Kick API üzerindeki benzersiz sohbet odası kimliğini (Chat ID) bulur.
879
+ Bu metod çift alttan tire (`__`) ile korunmaktadır, sınıf dışından doğrudan erişilemez.
880
+
881
+ ### 🇺🇸 [EN] Private Coordinate Resolver
882
+ Fetches the unique chatroom ID for the target `channel_name` via Kick API.
883
+ Protected by name mangling (`__`), preventing direct external access.
884
+ ---
885
+ __url = f"https://kick.com/api/v1/channels/{cls.channel_name}"
886
+ __headers = {
887
+ "accept": "application/json",
888
+ "authorization": cls.bearer_token,
889
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
890
+ "referer": "https://kick.com/"
891
+ }
892
+ Zerror.log(level="warn",msg_tr="Chat id tanımlanmamış, otomatik olarak alınıyor...",msg_en="Chat id is undefined, fetching...")
893
+ try:
894
+ async with aiohttp.ClientSession(headers=__headers) as __session:
895
+ async with __session.get(__url) as __resp:
896
+ if __resp.status == 200:
897
+ __data = await __resp.json()
898
+ cls.chat_id = __data.get("chatroom", {}).get("id")
899
+ if cls.chat_id:
900
+ Zerror.log(level="succ",
901
+ msg_tr=f"Chat ID başarıyla alındı: {cls.chat_id} | İPUCU: Botun daha hızlı başlaması için bu ID'yi 'chat_id' parametresine manuel ekle!",
902
+ msg_en=f"Chat ID fetched: {cls.chat_id} | HINT: To run the bot faster, define this ID manually to the 'chat_id' parameter!")
903
+ return True
904
+ Zerror.log(level="error",
905
+ msg_tr=f"Chat ID alınırken bir hata oluştu! Sunucu yanıtı: {__resp.status}",
906
+ msg_en=f"An error occurred while fetching Chat ID! Server response: {__resp.status}")
907
+ return False
908
+ except Exception as __e:
909
+ Zerror.log(level="error",
910
+ msg_tr=f"Chat ID alınırken kritik bir hata oluştu: {__e}",
911
+ msg_en=f"A critical error occurred while fetching Chat ID: {__e}")
912
+ return False
913
+ @classmethod
914
+ async def __fetch_app_key(cls) -> Optional[str]:
915
+ ---
916
+ ### 🇹🇷 [TR] Dinamik Anahtar Avcısı
917
+ Kick'in ana sayfasındaki JavaScript chunk'larını tarayarak güncel Pusher
918
+ App Key'i (anahtarı) bulur ve geri döndürür.
919
+
920
+ ### 🇺🇸 [EN] Dynamic Key Hunter
921
+ Fetches the current Pusher App Key by scanning JavaScript chunks
922
+ on Kick's main page and returns it.
923
+ ---
924
+ _base_url = "https://kick.com"
925
+ # Tarayıcı gibi görünmek için maske takıyoruz
926
+ _headers = {
927
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
928
+ }
929
+ Zerror.log(level="warnn",
930
+ msg_tr="App Key tanımlanmamış; güncel anahtar JavaScript paketleri içinde aranıyor...",
931
+ msg_en="App Key is undefined; searching for current key within JavaScript chunks...")
932
+ try:
933
+ async with aiohttp.ClientSession(headers=_headers) as _session:
934
+ # 1. Ana sayfaya sızıp HTML iskeletini alıyoruz
935
+ async with _session.get(_base_url, timeout=10) as _resp:
936
+ if _resp.status != 200:
937
+ Zerror.log(level="error",
938
+ msg_tr=f"Kick ana sayfasına ulaşılamadı! Durum kodu: {_resp.status}",
939
+ msg_en=f"Could not reach Kick main page! Status code: {_resp.status}")
940
+ return None
941
+ _html = await _resp.text()
942
+
943
+ # 2. HTML içindeki tüm .js dosyalarını (chunk'ları) buluyoruz
944
+ _scripts = re.findall(r'src="([^"]+\.js)"', _html)
945
+
946
+ for _script_url in _scripts:
947
+ # Kısa yolları tam URL'ye çeviriyoruz
948
+ if not _script_url.startswith('http'):
949
+ _script_url = f"{_base_url}{_script_url}"
950
+
951
+ # Sadece potansiyel hazine olan 'chunks' klasörüne bakıyoruz
952
+ if "_next/static/chunks/" in _script_url:
953
+ try:
954
+ async with _session.get(_script_url, timeout=5) as _s_resp:
955
+ if _s_resp.status == 200:
956
+ _js_content = await _s_resp.text()
957
+
958
+ # Nokta atışı: NEXT_PUBLIC_PUSHER_KEY kalıbını arıyoruz
959
+ _match = re.search(r'NEXT_PUBLIC_PUSHER_KEY\s*:\s*"([^"]+)"', _js_content)
960
+ if _match:
961
+ _found_key = _match.group(1)
962
+ Zerror.log(level="succ",
963
+ msg_tr=f"App Key başarıyla yakalandı: {_found_key} | İPUCU: Daha hızlı açılış için bu anahtarı 'app_key' parametresine manuel ekle!",
964
+ msg_en=f"App Key captured: {_found_key} | HINT: To start the bot faster, provide this key manually to the 'app_key' parameter!")
965
+ return _found_key
966
+ except:
967
+ continue # Bir dosya okunamazsa pes etme, sonrakine geç
968
+
969
+ except Exception as _e:
970
+ Zerror.log(level="error",
971
+ msg_tr=f"App Key avı sırasında kritik bir hata oluştu: {_e}",
972
+ msg_en=f"A critical error occurred during App Key hunt: {_e}")
973
+ return None
974
+ """
975
+
976
+ """"""
977
+
978
+ async def send_message(self, content: str, identifier=None):
979
+ """
980
+ ### 🇹🇷 [TR] Genel Mesaj Gönderme / 🇺🇸 [EN] Global Message Sender
981
+ Belirtilen kanala (isim veya ID ile) veya varsayılan (ilk eklenen) kanala mesaj gönderir.
982
+ """
983
+ # Eğer özel bir tanımlayıcı (İsim veya ID) verilmişse o kanalı bul
984
+ if identifier:
985
+ target_channel = self._get_channel(identifier)
986
+ else:
987
+ # Belirtilmemişse, listeye eklenen ilk kanalı seç (Ana kanal)
988
+ target_channel = next(iter(self.channels.values())) if self.channels else None
989
+
990
+ if target_channel:
991
+ return await target_channel.send(content)
992
+
993
+ zerror.log(level="error",
994
+ msg_tr=f"[{identifier}] adında/ID'sinde bir kanal bulunamadı! add_channel() ile eklediğine emin ol.",
995
+ msg_en=f"No channel found with identifier [{identifier}]! Make sure you added it with add_channel().")
996
+ return False
997
+
998
+ """"""
999
+
1000
+ async def __start(self):
1001
+ try:
1002
+ colorama.init(autoreset=True)
1003
+ await tasks.check(self)
1004
+ await tasks.run_ready_funcs(self)
1005
+ await tasks.run_timer_tasks(self)
1006
+ zerror.log(level="succ", msg_tr=f"KickZero Framework Aktif! (Kaptan: {self.user_name})", msg_en=f"KickZero Framework Active! (Captain: {self.user_name})")
1007
+ while True:
1008
+ try:
1009
+ async with engine.connect(self) as websocket:
1010
+ await engine.subscribe_all(self,websocket)
1011
+ while True:
1012
+ await engine.process_all_events(self,websocket)
1013
+
1014
+ except Exception as e:
1015
+ zerror.log(level="warn", msg_tr=f"Bağlantı koptu, 5sn sonra tekrar bağlanılıyor: {e}", msg_en=f"Connection lost, reconnecting in 5s: {e}")
1016
+ await asyncio.sleep(5)
1017
+
1018
+ except KeyboardInterrupt:
1019
+ print("\n")
1020
+ zerror.log(level="warn", msg_tr="Bot durduruluyor...", msg_en="Bot stopping...")
1021
+ sys.exit(0)
1022
+ return
1023
+ except Exception as e:
1024
+ zerror.log(level="error", msg_tr=f"Kritik Başlatma Hatası: {e}", msg_en=f"Critical Startup Error: {e}")
1025
+ def run(self):
1026
+ try:
1027
+ asyncio.run(self.__start())
1028
+ except KeyboardInterrupt:
1029
+ zerror.log(
1030
+ level="note",
1031
+ msg_tr=f"{Fore.MAGENTA}Bot durduruldu.",
1032
+ msg_en=f"{Fore.MAGENTA}Bot has been stopped."
1033
+ )
1034
+ pass
1035
+ except Exception as e:
1036
+ zerror.log(level="error",
1037
+ msg_tr=f"Bot başlatılırken beklenmedik bir hata: {e}",
1038
+ msg_en=f"Unexpected error while starting the bot: {e}")
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: kickzero
3
+ Version: 1.3.1
4
+ Summary: A modular and asynchronous framework for Kick.com chatbots.
5
+ Home-page: https://github.com/SeymenSozen/KickZero
6
+ Author: Seymen Sözen
7
+ Project-URL: Bug Tracker, https://github.com/SeymenSozen/KickZero/issues
8
+ Project-URL: Documentation, https://github.com/SeymenSozen/KickZero#readme
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: Other/Proprietary License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Communications :: Chat
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: aiohttp
16
+ Requires-Dist: websockets
17
+ Requires-Dist: colorama
18
+ Dynamic: author
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: project-url
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+ # 🏴‍☠️ KickZero Framework (v1.3.1)
29
+
30
+ ![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)
31
+ ![License](https://img.shields.io/badge/license-Proprietary-red)
32
+ ![Maintainer](https://img.shields.io/badge/maintainer-Seymen%20S%C3%B6zen-orange)
33
+
34
+ **KickZero**, Kick.com platformu için geliştirilmiş, modüler ve tamamen asenkron bir chatbot framework'üdür. Geliştiricilere karmaşık WebSocket trafiğiyle uğraşmadan, hızlı ve güçlü botlar üretme imkanı sağlar.
35
+
36
+ ---
37
+
38
+ ## 🚀 Özellikler / Features
39
+
40
+ - **Asynchronous Engine:** `asyncio` ve `aiohttp` tabanlı, donma yapmayan hızlı yapı.
41
+ - **Multi-Channel Support:** Aynı anda birden fazla kanalı tek bir botla yönetme.
42
+ - **Easy Decoration:** `@bot.command`, `@bot.message` ve `@bot.timer_task` dekoratörleri ile kolay geliştirme.
43
+ - **Smart Context:** Mesajın yetki, renk ve içerik bilgilerine tek noktadan (`ctx`) erişim.
44
+ - **Reward Integration:** Kanal puanı (Reward) kullanımlarını anında yakalama.
45
+
46
+ ---
47
+
48
+ ## 📦 Kurulum / Installation
49
+
50
+ Terminalinize şu komutu yazarak okyanusa açılabilirsiniz:
51
+
52
+ ```bash
53
+ pip install kickzero
@@ -0,0 +1,5 @@
1
+ kickzero/__init__.py,sha256=O0ApX9xpQ-cMkkdz_RL3LGXKozDAQrATBnF4bUu6Z0M,51932
2
+ kickzero-1.3.1.dist-info/METADATA,sha256=naLcRT7QcFsf_BU7dWYQuej2IrNKXVYOASvvJ3xFWSs,2041
3
+ kickzero-1.3.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
4
+ kickzero-1.3.1.dist-info/top_level.txt,sha256=_orY-HradFlzPKqx8lP-GPqV5ZyXaEG1go3ZlfzEtlE,9
5
+ kickzero-1.3.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ kickzero