From d84328d7a16e29d52610cef81f33352899a9bd01 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Sat, 26 Sep 2026 19:21:36 +0700 Subject: [PATCH] loggorilla implementation --- scripts/loggorilla.py | 13 +++++++++ services/telegram_client.py | 37 ++++++++++++------------ services/xmpp_client.py | 56 +++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 49 deletions(-) create mode 100644 scripts/loggorilla.py diff --git a/scripts/loggorilla.py b/scripts/loggorilla.py new file mode 100644 index 0000000..ea76d63 --- /dev/null +++ b/scripts/loggorilla.py @@ -0,0 +1,13 @@ +import datetime + +def prcss(loc, msg): + print(f"[loggorilla][{datetime.datetime.now()}][\033[32mprcss\033[39m][\033[95m{loc}\033[39m] {msg}", flush=True) + +def accss(loc, msg): + print(f"[loggorilla][{datetime.datetime.now()}][\033[36maccss\033[39m][\033[95m{loc}\033[39m] {msg}", flush=True) + +def fyinf(loc, msg): + print(f"[loggorilla][{datetime.datetime.now()}][\033[93mfyinf\033[39m][\033[95m{loc}\033[39m] {msg}", flush=True) + +def error(loc, msg): + print(f"[loggorilla][{datetime.datetime.now()}][\033[31merror\033[39m][\033[95m{loc}\033[39m] {msg}", flush=True) diff --git a/services/telegram_client.py b/services/telegram_client.py index 2ebef9c..089d7a9 100644 --- a/services/telegram_client.py +++ b/services/telegram_client.py @@ -10,6 +10,7 @@ from services.session_manager import SessionManager from lib.agent_loop import run_agent_loop from lib import personality from lib import ragroleplay +from scripts import loggorilla from tools.roleplayer import _name_mentioned @@ -47,7 +48,7 @@ class TelegramClient: self._app = Application.builder().token(token).build() def start(self): - print(f'[{_ts()}] Starting Telegram service...', flush=True) + loggorilla.prcss('telegram', 'Starting Telegram service...') asyncio.run(self._async_run()) def stop(self): @@ -63,14 +64,14 @@ class TelegramClient: bot_user = await self._app.bot.get_me() self._bot_username = bot_user.username or "" - print(f'[{_ts()}] Telegram bot: @{self._bot_username}', flush=True) + loggorilla.prcss('telegram', f'Telegram bot: @{self._bot_username}') self._register_handlers() await self._app.initialize() await self._app.start() await self._app.updater.start_polling() - print(f'[{_ts()}] Telegram bot is polling', flush=True) + loggorilla.prcss('telegram', 'Telegram bot is polling') try: await self._stopped.wait() @@ -80,15 +81,15 @@ class TelegramClient: await self._app.updater.stop() await self._app.stop() await self._app.shutdown() - print(f'[{_ts()}] Telegram service stopped', flush=True) + loggorilla.prcss('telegram', 'Telegram service stopped') async def _error_handler(self, update, context): from telegram.error import NetworkError cause = context.error if isinstance(cause, NetworkError) and "ReadError" in str(cause): - print(f'[{_ts()}] [Network Warning] Telegram ReadError occurred. Retrying...', flush=True) + loggorilla.fyinf('telegram', 'Network Warning: Telegram ReadError occurred. Retrying...') else: - print(f'[{_ts()}] Exception while handling an update: {cause}', flush=True) + loggorilla.error('telegram', f'Exception while handling an update: {cause}') # Untuk error selain ReadError, tetap cetak full traceback jika perlu via logging # atau biarkan default handler mengurusnya. @@ -131,7 +132,7 @@ class TelegramClient: msg_id = update.message.message_id user = update.effective_user tg_username = user.username if user else None - print(f'[{_ts()}] Telegram DM from {chat_id}: {text[:60]}', flush=True) + loggorilla.accss('telegram', f'Telegram DM from {chat_id}: {text}') threading.Thread( target=self._process_message, args=(chat_id, text, 'private', '', msg_id, tg_username), @@ -172,7 +173,7 @@ class TelegramClient: name_mentioned = _name_mentioned(personality.PERSONALITY.codename, text) if not replied_to_bot and not has_mention and not name_mentioned: - print(f'[{_ts()}] Telegram Group [{chat_id}] NO-REPLY: {text[:60]}', flush=True) + loggorilla.fyinf('telegram', f'Telegram Group [{chat_id}] NO-REPLY: {text}') return if has_mention and self._bot_username: @@ -183,7 +184,7 @@ class TelegramClient: msg_id = update.message.message_id sender = update.effective_user sender_name = sender.full_name or sender.username or str(sender.id) if sender else str(chat_id) - print(f'[{_ts()}] Telegram Group [{chat_id}] <{sender_name}>: {text[:60]}', flush=True) + loggorilla.accss('telegram', f'Telegram Group [{chat_id}] <{sender_name}>: {text}') threading.Thread( target=self._process_message, args=(chat_id, text, 'group', sender_name, msg_id), @@ -202,7 +203,7 @@ class TelegramClient: if body in (':new', '/new'): self._session_mgr.reset(str(chat_id)) - print(f'[{_ts()}] Session reset for {chat_id}', flush=True) + loggorilla.prcss('telegram', f'Session reset for {chat_id}') self._schedule_send(chat_id, 'Memulai sesi baru. Ada yang bisa dibantu?', reply_to_msg_id) return @@ -305,17 +306,17 @@ class TelegramClient: recent_history=recent_history, my_name=my_name, ): - print(f'[{_ts()}] need_response=True → sending response', flush=True) + loggorilla.fyinf('telegram', 'need_response=True → sending response') self._schedule_send(chat_id, final_content, reply_to_msg_id) else: - print(f'[{_ts()}] need_response=False → staying silent', flush=True) + loggorilla.fyinf('telegram', 'need_response=False → staying silent') else: from tools.roleplayer import _name_mentioned if _name_mentioned(my_name, body): - print(f'[{_ts()}] Name mentioned → sending response', flush=True) + loggorilla.fyinf('telegram', 'Name mentioned → sending response') self._schedule_send(chat_id, final_content, reply_to_msg_id) else: - print(f'[{_ts()}] Name not mentioned → staying silent', flush=True) + loggorilla.fyinf('telegram', 'Name not mentioned → staying silent') else: self._schedule_send(chat_id, final_content, reply_to_msg_id) else: @@ -328,7 +329,7 @@ class TelegramClient: # Natural close via tool end_session: berlaku untuk private & group # roleplay. Konten penutup sudah dikirim, sekarang tutup sesi. if should_close and is_roleplay: - print(f'[{_ts()}] Natural close triggered for {chat_id}', flush=True) + loggorilla.prcss('telegram', f'Natural close triggered for {chat_id}') self._session_mgr.reset(str(chat_id)) else: timeout = 86400 if chat_type == 'private' else 300 @@ -338,7 +339,7 @@ class TelegramClient: if self._loop and not self._loop.is_closed(): char_count = len(text) if text else 0 sleep_delay = max(1.0, min(char_count / config.TYPING_SPEED, config.TYPING_MAX)) - print(f'[{_ts()}] Typing delay: {sleep_delay:.1f}s ({char_count} chars)', flush=True) + loggorilla.fyinf('telegram', f'Typing delay: {sleep_delay:.1f}s ({char_count} chars)') time.sleep(sleep_delay) asyncio.run_coroutine_threadsafe( @@ -350,10 +351,10 @@ class TelegramClient: self._loop ) else: - print(f'[{_ts()}] WARNING: cannot send to {chat_id} — loop unavailable', flush=True) + loggorilla.error('telegram', f'WARNING: cannot send to {chat_id} — loop unavailable') def _timeout_session(self, chat_id): - print(f'[{_ts()}] Session timeout: {chat_id}', flush=True) + loggorilla.fyinf('telegram', f'Session timeout: {chat_id}') body = '*session end*' if 'roleplayer' in self._skill else 'Sesi ditutup. Sampai jumpa' self._schedule_send(chat_id, body) self._session_mgr.reset(str(chat_id)) diff --git a/services/xmpp_client.py b/services/xmpp_client.py index 7a47258..762777c 100644 --- a/services/xmpp_client.py +++ b/services/xmpp_client.py @@ -1,5 +1,5 @@ import asyncio -import logging +from scripts import loggorilla import random import signal import threading @@ -350,14 +350,14 @@ class XMPPClient(ClientXMPP): self._schedule_muc_rejoin(room) async def _on_connected(self, event): - print(f'[{_ts()}] XMPP connected', flush=True) + loggorilla.prcss('xmpp', 'XMPP connected') _dbg(f'connected state: authenticated={self.authenticated}, bound={self.bound}, ' f'sessionstarted={self.sessionstarted}, ' f'_session_started={getattr(self, "_session_started", None)}') _dbg(f'connected via: {self.transport}') async def _on_disconnected(self, event): - print(f'[{_ts()}] XMPP disconnected', flush=True) + loggorilla.prcss('xmpp', 'XMPP disconnected') # Anti-ban: cancel all pending rejoin tasks on disconnect for room, task in list(self._muc_rejoin_tasks.items()): if not task.done(): @@ -398,7 +398,7 @@ class XMPPClient(ClientXMPP): f'_session_started={getattr(self, "_session_started", None)}') _dbg(f'boundjid: full={self.boundjid.full}, bare={self.boundjid.bare}, ' f'resource={self.boundjid.resource}, host={self.boundjid.host}') - print(f'[{_ts()}] XMPP online as {self.boundjid.full}', flush=True) + loggorilla.prcss('xmpp', f'XMPP online as {self.boundjid.full}') # Anti-ban: set disco identity SEKARANG (setelah bind, boundjid ber-resource) # supaya identity masuk ke caps ver (presence) DAN respon disco#info server. @@ -418,15 +418,15 @@ class XMPPClient(ClientXMPP): _dbg(f'disco identity check failed: {e}') # Anti-ban: send presence + request roster (wajib sebelum kirim pesan DM) - print(f'[{_ts()}] Sending initial presence...', flush=True) + loggorilla.prcss('xmpp', 'Sending initial presence...') self.send_presence() - print(f'[{_ts()}] Requesting roster...', flush=True) + loggorilla.prcss('xmpp', 'Requesting roster...') self.get_roster() # Anti-ban: delay sebelum join MUC pertama agar startup tidak terlihat bot if self._muc_rooms: pre_delay = random.uniform(5.0, 15.0) - print(f'[{_ts()}] MUC pre-join delay {pre_delay:.1f}s (anti-ban)...', flush=True) + loggorilla.prcss('xmpp', f'MUC pre-join delay {pre_delay:.1f}s (anti-ban)...') await asyncio.sleep(pre_delay) # Anti-ban: delay sebelum join MUC untuk menghindari koneksi yang terlalu agresif @@ -434,7 +434,7 @@ class XMPPClient(ClientXMPP): # Delay antar room join (3-8 detik per room) if i > 0: join_delay = random.uniform(3.0, 8.0) - print(f'[{_ts()}] MUC [{room}] Waiting {join_delay:.1f}s before join...', flush=True) + loggorilla.prcss('xmpp', f'MUC [{room}] Waiting {join_delay:.1f}s before join...') await asyncio.sleep(join_delay) # Anti-ban: retry join dengan incremental delay & nick fallback @@ -443,26 +443,26 @@ class XMPPClient(ClientXMPP): nick = self._get_muc_nick(room) try: await self.plugin['xep_0045'].join_muc_wait(room, nick, maxstanzas=0) - print(f'[{_ts()}] Joined MUC room: {room} as {nick}', flush=True) + loggorilla.prcss('xmpp', f'Joined MUC room: {room} as {nick}') self._muc_last_join[room] = datetime.now() self._muc_rejoin_attempts.pop(room, None) self._muc_rejoin_attempts.pop("_nick_" + room, None) success = True break except Exception as e: - print(f'[{_ts()}] MUC join attempt #{attempt} failed ({room}): {e}', flush=True) + loggorilla.error('xmpp', f'MUC join attempt #{attempt} failed ({room}): {e}') # Anti-ban: handle 409 Conflict - coba nick alternatif if '409' in str(e) or 'conflict' in str(e).lower(): nick_attempts = self._muc_rejoin_attempts.get("_nick_" + room, 0) if nick_attempts < MUC_NICK_SUFFIX_MAX: nick_attempts += 1 self._muc_rejoin_attempts["_nick_" + room] = nick_attempts - print(f'[{_ts()}] MUC [{room}] Nick conflict, switching to: {self._get_muc_nick(room)}', flush=True) + loggorilla.fyinf('xmpp', f'MUC [{room}] Nick conflict, switching to: {self._get_muc_nick(room)}') # Retry segera dengan nick baru (jangan wait) continue else: # Anti-ban: semua nick alternatif habis - print(f'[{_ts()}] MUC [{room}] All nick variations exhausted', flush=True) + loggorilla.fyinf('xmpp', f'MUC [{room}] All nick variations exhausted') break elif attempt < 3: # Anti-ban: error biasa, wait before retry (5s, 10s, 15s) @@ -481,18 +481,14 @@ class XMPPClient(ClientXMPP): body = msg['body'].strip() if not body: return - print(f'[{_ts()}] DM from {jid}: {body[:60]}', flush=True) + loggorilla.accss('xmpp', f'DM from {jid}: {body}') # Anti-ban: proses langsung di event loop, bukan thread baru # Ini memastikan msg.send() dipanggil dari thread yang benar asyncio.create_task(self._process_dm_async(jid, body)) def _on_message_error(self, msg): """Anti-ban: handle error message dari server.""" - print(f'[{_ts()}] MESSAGE ERROR from server:', flush=True) - print(f'[{_ts()}] Type: {msg.get("type", "unknown")}', flush=True) - print(f'[{_ts()}] From: {msg.get("from", "unknown")}', flush=True) - print(f'[{_ts()}] To: {msg.get("to", "unknown")}', flush=True) - print(f'[{_ts()}] Error: {msg.get("error", "unknown")}', flush=True) + loggorilla.error('xmpp', f'MESSAGE ERROR from server:\n Type: {msg.get("type", "unknown")}\n From: {msg.get("from", "unknown")}\n To: {msg.get("to", "unknown")}\n Error: {msg.get("error", "unknown")}') print(f'[{_ts()}] Full stanza: {msg}', flush=True) # Catat error untuk adaptive rate limiting @@ -500,13 +496,11 @@ class XMPPClient(ClientXMPP): def _on_stream_error(self, error): """Anti-ban: handle stream error dari server.""" - print(f'[{_ts()}] STREAM ERROR from server:', flush=True) - print(f'[{_ts()}] Condition: {error.get("condition", "unknown")}', flush=True) - print(f'[{_ts()}] Text: {error.get("text", "unknown")}', flush=True) + loggorilla.error('xmpp', f'STREAM ERROR from server:\n Condition: {error.get("condition", "unknown")}\n Text: {error.get("text", "unknown")}') print(f'[{_ts()}] Full error: {error}', flush=True) async def _on_roster_subscription_request(self, presence): - print(f'[{_ts()}] Subscription request from {presence["from"]}', flush=True) + loggorilla.fyinf('xmpp', f'Subscription request from {presence["from"]}') # Anti-ban: TIDAK merespons subscribe otomatis (auto_subscribe=False). # Balasan 'subscribe' balik adalah red flag (contact farming) di conversations.im. _dbg(f'NOT auto-responding subscribe from {presence["from"]} ' @@ -526,7 +520,7 @@ class XMPPClient(ClientXMPP): body = msg['body'].strip() if not body: return - print(f'[{_ts()}] MUC [{room}] <{nick}>: {body[:60]}', flush=True) + loggorilla.accss('xmpp', f'MUC [{room}] <{nick}>: {body}') # Anti-ban: proses langsung di event loop, bukan thread baru asyncio.create_task(self._process_muc_async(room, nick, body)) @@ -674,7 +668,7 @@ class XMPPClient(ClientXMPP): if body == ':new': self._session_mgr.reset(room) - print(f'[{_ts()}] Session reset for MUC room {room}', flush=True) + loggorilla.prcss('xmpp', f'Session reset for MUC room {room}') self._schedule_send(room, 'Memulai sesi baru. Ada yang bisa di bantu?', mtype='groupchat') return @@ -745,17 +739,17 @@ class XMPPClient(ClientXMPP): # END-SESSION: sinyal model bahwa sesi selesai. # Jangan kirim apa pun ke room, tutup sesi langsung. if sentinel == "END-SESSION": - print(f'[{_ts()}] MUC: Agent decided END-SESSION → closing session', flush=True) + loggorilla.prcss('xmpp', 'MUC: Agent decided END-SESSION → closing session') _pop_ghost("END-SESSION") self._session_mgr.reset(room) return # NO-REPLY: agent memilih diam → jangan kirim apa pun. if sentinel == "NO-REPLY": - print(f'[{_ts()}] MUC: Agent decided NO-REPLY → staying silent', flush=True) + loggorilla.fyinf('xmpp', 'MUC: Agent decided NO-REPLY → staying silent') _pop_ghost("NO-REPLY") else: - print(f'[{_ts()}] MUC: sending response', flush=True) + loggorilla.fyinf('xmpp', 'MUC: sending response') self._schedule_send(room, final_content, 'groupchat') _flush_pending() else: @@ -773,7 +767,7 @@ class XMPPClient(ClientXMPP): # Natural close via tool end_session: kirim sisa buffer (farewell), # lalu tutup sesi. if _is_roleplay and should_close: - print(f'[{_ts()}] MUC: natural close triggered for {room}', flush=True) + loggorilla.prcss('xmpp', f'MUC: natural close triggered for {room}') _flush_pending() self._session_mgr.reset(room) return @@ -890,7 +884,7 @@ class XMPPClient(ClientXMPP): self._session_mgr.reset(session_id) def start(self): - print(f'[{_ts()}] Starting XMPP service...', flush=True) + loggorilla.prcss('xmpp', 'Starting XMPP service...') _setup_debug_logging() asyncio.run(self._run()) @@ -910,14 +904,14 @@ class XMPPClient(ClientXMPP): except (NotImplementedError, RuntimeError): pass - print(f'[{_ts()}] Connecting to server (jid={self.jid})...', flush=True) + loggorilla.prcss('xmpp', f'Connecting to server (jid={self.jid})...') try: await self.connect() except Exception as e: print(f'[{_ts()}] CONNECT ERROR: {e}', flush=True) print(traceback.format_exc(), flush=True) raise - print(f'[{_ts()}] Connected, waiting for stream events...', flush=True) + loggorilla.prcss('xmpp', 'Connected, waiting for stream events...') try: await self._stopped.wait() except (asyncio.CancelledError, KeyboardInterrupt):