IRCdForum.Net  

Ana Sayfa

Geri Git   IRCdForum.Net > IRCd Genel > UnrealIRCd

UnrealIRCd UnrealIRCd tabanlı sunucunuzda rastladığınız sorunların çözümüne bu bölümden ulaşabilirsiniz.


Python BNC bot

UnrealIRCd tabanlı sunucunuzda rastladığınız sorunların çözümüne bu bölümden ulaşabilirsiniz.


Kullanıcı Etiket Listesi

2Beğeniler
  • 2 Post By TeMoNDe

  
 
LinkBack Konu Araçları Görünüm Modları
Eski 02 Nisan 2026, 09:39   #1
Çevrimdışı
TeMoNDe
Çevrimdışı

irc.Sohbetix.net

TeMoNDe Kullanıcısının Avatarı

Varsayılan Python BNC bot

Merhaba arkadaslar,
Ilgili kategorinin olmamasindan dolayi konuyu buraya aciyorum. Belki daha sonra tasinabilir.
Bu botu normal bnc'den cok nickim o sunucuda online gozuksun diye kodlamistim. Botun islevi ayarladiginiz ana sunucu ve kanala baglanir, !ekle komutu ile eklediginiz sunucuya ayarladiginiz nicki sokar (eger ayarlarsaniz kayit eder nicki). DC yeme durumunda vs tekrar baglanir. !durum komutu ile botu takip edebilir ozeline gelen son 5 mesaji gorebilirsiniz. Dilerseniz kodlari inceleyip kendinize gore daha da gelistirebilirsiniz.

Kod:
import socket
import time
import threading
import logging
import json
import os
import re
import collections

# ─── ANA BOT AYARLARI (BncBot - irc.zevklisohbet.net) ────────────────────────
MASTER_SERVER   = "irc.sohbetix.net"
MASTER_PORT     = 6667
MASTER_NICK     = "BncBot"
MASTER_PASSWORD = ""
MASTER_CHANNEL  = "#bnc"
MASTER_IDENT    = "bncbot"
MASTER_REALNAME = "BncBot Controller"

# ─── OPER BİLGİLERİ â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
OPER_NAME     = "Eggdrop"
OPER_PASSWORD = ""

# ─── SLAVE BOT AYARLARI ───────────────────────────────────────────────────────
SLAVE_NICK     = "ArsivDK"
SLAVE_PASSWORD = ""
SLAVE_CHANNEL  = "#Sohbet"
SLAVE_IDENT    = "Arsivci"
SLAVE_REALNAME = "www.Arsiv.dk"
SLAVE_PORT     = 6667
SLAVE_EMAIL    = ""

# ─── GENEL AYARLAR ────────────────────────────────────────────────────────────
RECONNECT_DELAY  = 10
PING_TIMEOUT     = 300
BOT_OWNER        = ""
DB_FILE          = "servers.json"
LOG_DIR          = "logs"          # Her sunucu için ayrı log klasörü
DAILY_REPORT_HOUR = 9              # Günlük özet saati (sunucu saati)
SLAVE_PING_INTERVAL = 60           # Slave'e PING gönderme aralığı (sn)
SLAVE_PING_TIMEOUT  = 120          # Bu kadar sn içinde PONG gelmezse reconnect
# ──────────────────────────────────────────────────────────────────────────────

os.makedirs(LOG_DIR, exist_ok=True)

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
log = logging.getLogger("IRCBot")

slave_bots: dict  = {}
slave_lock        = threading.Lock()
watch_keywords: list = []          # [(keyword, ekleyen_nick), ...]
watch_lock        = threading.Lock()
master_ref        = None           # MasterBot instance (bildirim için)


# ══════════════════════════════════════════════════════════════════════════════
#  YARDIMCI FONKSİYONLAR
# ══════════════════════════════════════════════════════════════════════════════
def is_nickserv(prefix: str) -> bool:
    return prefix.lstrip(":").split("!")[0].lower() == "nickserv"

def notify_master(msg: str):
    """#bnc kanalına bildirim gönder."""
    global master_ref
    if master_ref:
        try:
            master_ref.send(f"PRIVMSG {MASTER_CHANNEL} :{msg}")
        except Exception:
            pass

def get_server_logger(server: str) -> logging.Logger:
    """Her sunucu için ayrı dosya logger'ı döndürür."""
    name = server.replace(".", "_").replace(":", "_")
    logger = logging.getLogger(f"srv.{name}")
    if not logger.handlers:
        fh = logging.FileHandler(
            os.path.join(LOG_DIR, f"{name}.log"),
            encoding="utf-8"
        )
        fh.setFormatter(logging.Formatter(
            "%(asctime)s %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
        ))
        logger.addHandler(fh)
        logger.setLevel(logging.INFO)
        logger.propagate = False
    return logger


# ══════════════════════════════════════════════════════════════════════════════
#  KAYIT SİSTEMİ — servers.json
# ══════════════════════════════════════════════════════════════════════════════
def db_load() -> list:
    if not os.path.exists(DB_FILE):
        return []
    try:
        with open(DB_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
        log.info(f"[DB] {len(data)} sunucu yüklendi.")
        return data
    except Exception as e:
        log.error(f"[DB] Okuma hatası: {e}")
        return []

def db_save():
    with slave_lock:
        entries = [{"host": bot.server, "port": bot.port} for bot in slave_bots.values()]
    try:
        with open(DB_FILE, "w", encoding="utf-8") as f:
            json.dump(entries, f, indent=2)
    except Exception as e:
        log.error(f"[DB] Yazma hatası: {e}")

def db_remove(host: str, port: int):
    entries = db_load()
    entries = [e for e in entries if not (e["host"] == host and e["port"] == port)]
    try:
        with open(DB_FILE, "w", encoding="utf-8") as f:
            json.dump(entries, f, indent=2)
    except Exception as e:
        log.error(f"[DB] Silme hatası: {e}")


# ══════════════════════════════════════════════════════════════════════════════
#  TEMEL IRC BAĞLANTISI
# ══════════════════════════════════════════════════════════════════════════════
class IRCConnection:
    def __init__(self, server, port, nick, password, channel, ident, realname, label="Bot"):
        self.server   = server
        self.port     = port
        self.nick     = nick
        self.password = password
        self.channel  = channel
        self.ident    = ident
        self.realname = realname
        self.label    = label
        self.sock     = None
        self.running  = True
        self._joined  = False
        self._buffer  = ""

    def connect(self):
        log.info(f"[{self.label}] Bağlanılıyor: {self.server}:{self.port}")
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(PING_TIMEOUT)
        self.sock.connect((self.server, self.port))
        self._joined = False
        self._buffer = ""
        log.info(f"[{self.label}] TCP bağlantısı kuruldu.")

    def disconnect(self):
        try:
            self.sock.close()
        except Exception:
            pass
        self.sock = None

    def send(self, msg: str):
        raw = (msg + "\r\n").encode("utf-8", errors="replace")
        log.debug(f"[{self.label}] >> {msg}")
        self.sock.sendall(raw)

    def register(self):
        self.send(f"PASS {self.password}")
        self.send(f"NICK {self.nick}")
        self.send(f"USER {self.ident} 0 * :{self.realname}")

    def read_lines(self):
        chunk = self.sock.recv(4096).decode("utf-8", errors="replace")
        if not chunk:
            raise ConnectionResetError("Sunucu bağlantıyı kapattı.")
        self._buffer += chunk
        lines = []
        while "\r\n" in self._buffer:
            line, self._buffer = self._buffer.split("\r\n", 1)
            if line:
                lines.append(line)
        return lines

    def stop(self):
        self.running = False
        try:
            self.send("QUIT :Bot kaldırıldı.")
        except Exception:
            pass
        time.sleep(0.5)
        self.disconnect()


# ══════════════════════════════════════════════════════════════════════════════
#  SLAVE BOT
# ══════════════════════════════════════════════════════════════════════════════
class SlaveBot(IRCConnection):
    def __init__(self, server, port=SLAVE_PORT):
        label = f"Slave:{server}"
        super().__init__(server, port, SLAVE_NICK, SLAVE_PASSWORD,
                         SLAVE_CHANNEL, SLAVE_IDENT, SLAVE_REALNAME, label)

        # ── Bağlantı durumu ──────────────────────────────────────────────
        self.status        = "başlatılıyor"
        self.connected_at  = None
        self.reconnects    = 0
        self.last_error    = ""
        self.last_activity = time.time()

        # ── Kanal takibi ─────────────────────────────────────────────────
        self.channels: set = set()

        # ── PM takibi (son 20) ────────────────────────────────────────────
        self.pm_log:  list = []
        self.pm_lock       = threading.Lock()

        # ── Nick / NickServ durumu ────────────────────────────────────────
        self.nick_status      = "unknown"
        self._nick_registered = False
        self._register_retry_at  = None
        self._register_retry_sec = 0

        # ── Restart sinyali ──────────────────────────────────────────────
        self._restart_flag = False

        # ── İstatistikler ─────────────────────────────────────────────────
        self.msg_count      = 0     # bu oturumda görülen kanal mesajı
        self.pm_count_total = 0     # toplam PM sayısı
        self.connect_history = collections.deque(maxlen=5)  # son 5 bağlanma/kopma

        # ── PING/PONG takibi ─────────────────────────────────────────────
        self._last_pong     = time.time()
        self._ping_sent_at  = None

        # ── Bağlantı kopma bildirimi ──────────────────────────────────────
        self._notified_disconnect = False

        # ── Günlük rapor ──────────────────────────────────────────────────
        self._last_daily_report = None   # datetime.date

        # ── Sunucu logger ─────────────────────────────────────────────────
        self.srv_log = get_server_logger(server)

    # ── Yardımcılar ───────────────────────────────────────────────────────
    def _set_status(self, s: str, err: str = ""):
        self.status     = s
        self.last_error = err

    def _touch(self):
        self.last_activity = time.time()

    def _fmt_duration(self, seconds: int) -> str:
        if seconds < 60:
            return f"{seconds}sn"
        dk, sn = divmod(seconds, 60)
        if dk < 60:
            return f"{dk}dk {sn}sn"
        sa, dk = divmod(dk, 60)
        return f"{sa}sa {dk}dk {sn}sn"

    def _idle_str(self) -> str:
        return self._fmt_duration(int(time.time() - self.last_activity))

    def _uptime_str(self) -> str:
        if not self.connected_at:
            return "-"
        return self._fmt_duration(int(time.time() - self.connected_at))

    # ── Sunucu logu ───────────────────────────────────────────────────────
    def _slog(self, msg: str):
        self.srv_log.info(msg)

    # ── GHOST — nick alınmışsa ─────────────────────────────────────────────
    def _try_ghost(self):
        """Asıl nick başkası tarafından alınmışsa GHOST ile geri al."""
        log.info(f"[{self.label}] GHOST deneniyor: {SLAVE_NICK}")
        self.send(f"PRIVMSG NickServ :GHOST {SLAVE_NICK} {self.password}")
        time.sleep(2)
        self.send(f"NICK {SLAVE_NICK}")

    # ── NickServ mesajını işle ────────────────────────────────────────────
    def _handle_nickserv(self, body: str):
        # ── Identify başarılı ─────────────────────────────────────────────
        if any(k in body for k in (
            "sifre kabul edildi", "zaten identify", "login oldunuz",
            "you are now identified", "password accepted",
            "identified successfully", "now recognized",
            "you are already identified", "you are already logged in",
        )):
            self.nick_status = "identified"
            log.info(f"[{self.label}] NickServ: identify başarılı.")
            return

        # ── Nick kayıtlı değil → register et ─────────────────────────────
        if any(k in body for k in (
            "kayitli degil", "kayitli de", "kayit altina almak",
            "is not registered", "isn't registered",
            "no such nick", "not registered",
        )):
            self.nick_status = "not_registered"
            if not self._nick_registered:
                self._nick_registered = True
                log.info(f"[{self.label}] NickServ: kayıtlı değil, register deneniyor.")
                self.send(f"PRIVMSG NickServ :REGISTER {self.password} {SLAVE_EMAIL}")
            return

        # ── Register bekleme süresi ───────────────────────────────────────
        m = re.search(r"(\d+)\s*saniye\s*bekledikten", body)
        if m and self.nick_status == "not_registered":
            sn = int(m.group(1)) + 2
            self._register_retry_at  = time.time() + sn
            self._register_retry_sec = sn
            self._nick_registered    = False
            log.info(f"[{self.label}] NickServ: register için {sn}sn bekleniyor.")
            def _delayed_register(bot=self, delay=sn):
                time.sleep(delay)
                if bot.running and bot.nick_status == "not_registered" and not bot._nick_registered:
                    bot._nick_registered = True
                    log.info(f"[{bot.label}] NickServ: bekleme bitti, register yeniden deneniyor.")
                    try:
                        bot.send(f"PRIVMSG NickServ :REGISTER {bot.password} {SLAVE_EMAIL}")
                    except Exception:
                        pass
            threading.Thread(target=_delayed_register, daemon=True).start()
            return

        # ── Yanlış şifre ─────────────────────────────────────────────────
        if any(k in body for k in (
            "password incorrect", "invalid password",
            "authentication failed", "password does not match",
            "yanlis sifre", "hatali sifre",
            "yanlış şifre", "hatalı şifre",
        )):
            self.nick_status = "wrong_password"
            log.warning(f"[{self.label}] NickServ: yanlış şifre!")
            return

        # ── Register onayı ────────────────────────────────────────────────
        if any(k in body for k in (
            "has been registered", "registration successful",
            "your nick has been registered",
            "kayit edildi", "tescil edildi",
        )):
            self.nick_status         = "registered"
            self._register_retry_at  = None
            self._register_retry_sec = 0
            log.info(f"[{self.label}] NickServ: nick register edildi.")
            return

        # ── Genel identify isteği ─────────────────────────────────────────
        if any(k in body for k in (
            "identify sifreniz", "msg nickserv identify", "/msg nickserv",
        )):
            if self.nick_status not in ("identified", "registered"):
                self.send(f"PRIVMSG NickServ :IDENTIFY {self.password}")

    # ── Keyword izleme ────────────────────────────────────────────────────
    def _check_watch(self, channel: str, sender: str, message: str):
        with watch_lock:
            kws = list(watch_keywords)
        msg_lower = message.lower()
        for kw, ekleyen in kws:
            if kw.lower() in msg_lower:
                notify_master(
                    f"[İZLEME] [{self.server}] [{channel}] "
                    f"{sender}: {message}  (keyword: {kw})"
                )
                self._slog(f"[WATCH:{kw}] {channel} <{sender}> {message}")

    # ── Satır işleme ──────────────────────────────────────────────────────
    def handle_line(self, line: str):
        self._touch()
        parts = line.split()
        cmd   = parts[1] if len(parts) > 1 else ""

        # ── PING ──────────────────────────────────────────────────────────
        if line.startswith("PING"):
            token = line.split(":", 1)[1] if ":" in line else parts[1]
            self.send(f"PONG :{token}")
            return

        # ── PONG — kendi gönderdiğimiz PING'e cevap ───────────────────────
        if cmd == "PONG" or line.startswith("PONG"):
            self._last_pong    = time.time()
            self._ping_sent_at = None
            return

        # ── 001 Kayıt tamamlandı ──────────────────────────────────────────
        if cmd == "001":
            log.info(f"[{self.label}] Kayıt olundu.")
            self._slog(f"[CONNECT] {self.server}:{self.port}")
            if self.connected_at is None:
                self.connected_at = time.time()
            self.connect_history.appendleft(
                ("bağlandı", time.strftime("%H:%M:%S"))
            )
            self._set_status("bağlı")
            self._nick_registered    = False
            self.nick_status         = "unknown"
            self._register_retry_at  = None
            self._register_retry_sec = 0
            self._notified_disconnect = False
            self.channels.clear()
            self.msg_count = 0
            self.send(f"PRIVMSG NickServ :IDENTIFY {self.password}")
            time.sleep(1)
            self.send(f"JOIN {self.channel}")
            return

        # ── JOIN ──────────────────────────────────────────────────────────
        if cmd == "JOIN":
            joiner    = parts[0].split("!")[0].lstrip(":")
            joined_ch = (parts[2].lstrip(":") if len(parts) > 2 else "").strip()
            if joiner.lower() == self.nick.lower():
                self.channels.add(joined_ch)
                log.info(f"[{self.label}] Kanala katıldım: {joined_ch}")
            return

        # ── PART ──────────────────────────────────────────────────────────
        if cmd == "PART":
            leaver = parts[0].split("!")[0].lstrip(":")
            ch     = parts[2].lstrip(":") if len(parts) > 2 else ""
            if leaver.lower() == self.nick.lower():
                self.channels.discard(ch)
            return

        # ── KICK ──────────────────────────────────────────────────────────
        if cmd == "KICK":
            channel = parts[2] if len(parts) > 2 else ""
            kicked  = parts[3] if len(parts) > 3 else ""
            if kicked.lower() == self.nick.lower():
                self.channels.discard(channel)
                notify_master(f"âš* [{self.server}] Kanaldan atıldım: {channel}")
                self._slog(f"[KICKED] {channel}")
                if channel.lower() == self.channel.lower():
                    log.warning(f"[{self.label}] Ana kanaldan atıldım! 5 sn sonra yeniden katılıyorum.")
                    time.sleep(5)
                    self.send(f"JOIN {self.channel}")
            return

        # ── 433 Nick kullanımda ───────────────────────────────────────────
        if cmd == "433":
            log.warning(f"[{self.label}] Nick kullanımda, GHOST deneniyor.")
            self._try_ghost()
            return

        # ── NICK değişikliği takibi ────────────────────────────────────────
        if cmd == "NICK":
            changer  = parts[0].split("!")[0].lstrip(":")
            new_nick = parts[2].lstrip(":") if len(parts) > 2 else ""
            # Birisi SLAVE_NICK'i bıraktıysa al
            if new_nick.lower() == SLAVE_NICK.lower() and changer.lower() != self.nick.lower():
                log.info(f"[{self.label}] {SLAVE_NICK} serbest kaldı, alınıyor.")
                self.send(f"NICK {SLAVE_NICK}")
                self.nick = SLAVE_NICK
            return

        # ── PRIVMSG ───────────────────────────────────────────────────────
        if cmd == "PRIVMSG":
            if len(parts) < 4:
                return
            prefix  = parts[0]
            sender  = prefix.split("!")[0].lstrip(":")
            target  = parts[2]
            message = " ".join(parts[3:]).lstrip(":")

            if target.lower() == self.nick.lower():
                # NickServ PRIVMSG
                if is_nickserv(prefix):
                    self._handle_nickserv(message.lower())
                    return
                # Gerçek PM
                with self.pm_lock:
                    self.pm_log.append((time.time(), sender, message))
                    if len(self.pm_log) > 20:
                        self.pm_log = self.pm_log[-20:]
                self.pm_count_total += 1
                log.info(f"[{self.label}] PM alındı — {sender}: {message}")
                self._slog(f"[PM] <{sender}> {message}")
                notify_master(f"�� [{self.server}] PM — {sender}: {message}")
            else:
                # Kanal mesajı
                self.msg_count += 1
                self._slog(f"[{target}] <{sender}> {message}")
                self._check_watch(target, sender, message)
            return

        # ── NOTICE ────────────────────────────────────────────────────────
        if cmd == "NOTICE":
            if len(parts) < 4:
                return
            prefix = parts[0]
            body   = " ".join(parts[3:]).lstrip(":").lower()
            if is_nickserv(prefix):
                self._handle_nickserv(body)
            return

    # ── Aktif PING gönder ─────────────────────────────────────────────────
    def _ping_loop(self):
        """Arka planda PING gönder, PONG gelmezse reconnect yap."""
        while self.running:
            time.sleep(SLAVE_PING_INTERVAL)
            if not self.running:
                break
            if self.status != "bağlı":
                continue
            # PONG bekliyorsak ve süre aştıysa
            if self._ping_sent_at:
                if time.time() - self._ping_sent_at > SLAVE_PING_TIMEOUT:
                    log.warning(f"[{self.label}] PONG alınamadı, yeniden bağlanılıyor.")
                    self._set_status("yeniden bağlanıyor", "pong timeout")
                    self.disconnect()
                    return
            else:
                try:
                    self._ping_sent_at = time.time()
                    self.send(f"PING :{self.server}")
                except Exception:
                    pass

    # ── Günlük rapor ─────────────────────────────────────────────────────
    def _check_daily_report(self):
        import datetime
        now  = datetime.datetime.now()
        today = now.date()
        if now.hour == DAILY_REPORT_HOUR:
            if self._last_daily_report != today:
                self._last_daily_report = today
                with self.pm_lock:
                    pm_c = len(self.pm_log)
                notify_master(
                    f"�� Günlük Rapor [{self.server}] — "
                    f"Uptime: {self._uptime_str()} | "
                    f"Mesaj: {self.msg_count} | "
                    f"PM: {self.pm_count_total} | "
                    f"Kanallar: {len(self.channels)} | "
                    f"Reconnect: {self.reconnects}x | "
                    f"NickServ: {self.nick_status}"
                )
                self.msg_count = 0   # Günlük sayacı sıfırla

    # ── Detaylı durum raporu ──────────────────────────────────────────────
    def durum_detay(self) -> list:
        lines = []
        lines.append(f"┌─── {self.server}:{self.port} ───────────────────────")
        lines.append(
            f"│ Durum  : {self.status}"
            f"  │  Uptime: {self._uptime_str()}"
            f"  │  Idle: {self._idle_str()}"
        )
        if self.reconnects or self.last_error:
            hata = f"  │  Son hata: {self.last_error}" if self.last_error and self.status != "bağlı" else ""
            lines.append(f"│ Reconnect: {self.reconnects}x{hata}")

        # Bağlantı geçmişi
        if self.connect_history:
            gecmis = " → ".join(f"{ev}@{zaman}" for ev, zaman in self.connect_history)
            lines.append(f"│ Geçmiş : {gecmis}")

        # Nick / NickServ
        retry_str = ""
        if self._register_retry_at and self.nick_status == "not_registered":
            kalan = int(self._register_retry_at - time.time())
            if kalan > 0:
                retry_str = f" (retry: {kalan}sn kaldı)"
        nick_map = {
            "identified"    : "✔ Identify edildi",
            "registered"    : "✔ Register edildi (bu oturumda)",
            "not_registered": "✘ Kayıtlı değil → register deneniyor",
            "wrong_password": "✘ Yanlış şifre",
            "unknown"       : "? Yanıt alınamadı",
        }
        lines.append(
            f"│ Nick   : {self.nick}"
            f"  │  NickServ: {nick_map.get(self.nick_status, self.nick_status)}{retry_str}"
        )

        # İstatistikler
        lines.append(
            f"│ Mesaj  : {self.msg_count} (bugün)"
            f"  │  PM toplam: {self.pm_count_total}"
        )

        # Kanallar
        if self.channels:
            lines.append(f"│ Kanallar: {', '.join(sorted(self.channels))}")
        else:
            lines.append("│ Kanallar: (henüz hiçbir kanalda değil)")

        # PM'ler
        with self.pm_lock:
            pm_copy = list(self.pm_log)
        if pm_copy:
            lines.append(f"│ PM ({len(pm_copy)} adet — son 5):")
            for ts, nick, msg in pm_copy[-5:]:
                zaman = time.strftime("%H:%M:%S", time.localtime(ts))
                lines.append(f"│   [{zaman}] {nick}: {msg}")
        else:
            lines.append("│ PM: yok")

        lines.append("└────────────────────────────────────────────────")
        return lines

    # ── Restart ───────────────────────────────────────────────────────────
    def restart(self):
        log.info(f"[{self.label}] Restart istendi.")
        self._restart_flag = True
        try:
            self.send("QUIT :Yeniden bağlanılıyor...")
        except Exception:
            pass
        time.sleep(0.3)
        self.disconnect()

    # ── Ana döngü ─────────────────────────────────────────────────────────
    def run(self):
        key = f"{self.server}:{self.port}"
        log.info(f"[{self.label}] Slave bot başlatıldı.")

        # PING loop thread
        threading.Thread(target=self._ping_loop, name=f"ping-{key}", daemon=True).start()

        while self.running:
            self._restart_flag = False
            try:
                self._set_status("bağlanıyor")
                self.connect()
                self.register()
                while self.running:
                    try:
                        for line in self.read_lines():
                            self.handle_line(line)
                        self._check_daily_report()
                        if self._restart_flag:
                            break
                    except socket.timeout:
                        log.warning(f"[{self.label}] Ping zaman aşımı.")
                        self._set_status("yeniden bağlanıyor", "ping timeout")
                        break
                    except (ConnectionResetError, OSError) as e:
                        log.error(f"[{self.label}] Bağlantı hatası: {e}")
                        self._set_status("yeniden bağlanıyor", str(e))
                        break
            except Exception as e:
                log.error(f"[{self.label}] Beklenmeyen hata: {e}")
                self._set_status("yeniden bağlanıyor", str(e))
            finally:
                self.disconnect()
                self.channels.clear()
                if self.running and not self._notified_disconnect:
                    self._notified_disconnect = True
                    self.connect_history.appendleft(
                        ("koptu", time.strftime("%H:%M:%S"))
                    )
                    notify_master(
                        f"âš* [{self.server}:{self.port}] Bağlantı koptu! "
                        f"{RECONNECT_DELAY}sn sonra yeniden bağlanılacak."
                    )
                    self._slog(f"[DISCONNECT] yeniden bağlanılıyor...")
                if self.running:
                    self.reconnects += 1
                    log.info(f"[{self.label}] {RECONNECT_DELAY} sn sonra yeniden bağlanılacak...")
                    time.sleep(RECONNECT_DELAY)

        self._set_status("durduruldu")
        with slave_lock:
            slave_bots.pop(key, None)
        log.info(f"[{self.label}] Slave bot durduruldu.")


# ══════════════════════════════════════════════════════════════════════════════
#  ANA BOT (BncBot)
# ══════════════════════════════════════════════════════════════════════════════
class MasterBot(IRCConnection):
    def __init__(self):
        super().__init__(
            MASTER_SERVER, MASTER_PORT,
            MASTER_NICK, MASTER_PASSWORD,
            MASTER_CHANNEL, MASTER_IDENT, MASTER_REALNAME,
            label="Master:BncBot"
        )
        self._oper_done = False

    def do_oper(self):
        if not self._oper_done:
            log.info(f"[Master] OPER girişi: {OPER_NAME}")
            self.send(f"OPER {OPER_NAME} {OPER_PASSWORD}")

    def _start_slave(self, host: str, port: int):
        bot = SlaveBot(host, port)
        key = f"{host}:{port}"
        with slave_lock:
            slave_bots[key] = bot
        threading.Thread(target=bot.run, name=f"slave-{key}", daemon=True).start()
        return bot

    def handle_line(self, line: str):
        parts = line.split()
        cmd   = parts[1] if len(parts) > 1 else ""

        if line.startswith("PING"):
            token = line.split(":", 1)[1] if ":" in line else parts[1]
            self.send(f"PONG :{token}")
            return

        if cmd == "001":
            log.info("[Master] Kayıt olundu.")
            self.send(f"PRIVMSG NickServ :IDENTIFY {self.password}")
            time.sleep(1)
            self.do_oper()
            time.sleep(1)
            self.send(f"JOIN {self.channel}")
            self._restore_servers()
            return

        if cmd == "JOIN":
            joiner    = parts[0].split("!")[0].lstrip(":")
            joined_ch = parts[2].lstrip(":") if len(parts) > 2 else ""
            if joiner.lower() == self.nick.lower():
                if joined_ch.lower() == self.channel.lower():
                    if not self._joined:
                        self._joined = True
                        log.info(f"[Master] '{self.channel}' kanalına katıldım.")
                else:
                    self.send(f"PART {joined_ch}")
            return

        if cmd == "381":
            self._oper_done = True
            log.info("[Master] OPER girişi başarılı! (Eggdrop)")
            return

        if cmd == "491":
            log.warning("[Master] OPER başarısız! (491)")
            return

        if cmd == "433":
            self.send(f"NICK {self.nick}_")
            return

        if cmd == "KICK":
            channel = parts[2] if len(parts) > 2 else ""
            kicked  = parts[3] if len(parts) > 3 else ""
            if kicked.lower() == self.nick.lower() and channel.lower() == self.channel.lower():
                time.sleep(5)
                self.send(f"JOIN {self.channel}")
            return

        if cmd == "NOTICE":
            body   = " ".join(parts[3:]).lower()
            sender = parts[0].split("!")[0].lstrip(":").lower()
            if sender == "nickserv" and "identify" in body:
                self.send(f"PRIVMSG NickServ :IDENTIFY {self.password}")
            return

        if cmd == "PRIVMSG":
            if len(parts) < 4:
                return
            sender    = parts[0].split("!")[0].lstrip(":")
            target    = parts[2]
            message   = " ".join(parts[3:]).lstrip(":")
            reply     = target if target.startswith("#") else sender
            msg_lower = message.strip().lower()

            if msg_lower.startswith("!ekle "):
                self._cmd_ekle(sender, reply, message.strip()[6:].strip())
            elif msg_lower.startswith("!sil "):
                self._cmd_sil(sender, reply, message.strip()[5:].strip())
            elif msg_lower.startswith("!restart "):
                self._cmd_restart(sender, reply, message.strip()[9:].strip())
            elif msg_lower == "!restartall":
                self._cmd_restartall(sender, reply)
            elif msg_lower.startswith("!izle "):
                self._cmd_izle(sender, reply, message.strip()[6:].strip())
            elif msg_lower.startswith("!izlesil "):
                self._cmd_izlesil(sender, reply, message.strip()[9:].strip())
            elif msg_lower == "!izlelist":
                self._cmd_izlelist(reply)
            elif msg_lower.startswith("!pmtemizle "):
                self._cmd_pmtemizle(sender, reply, message.strip()[11:].strip())
            elif msg_lower.startswith("!log "):
                self._cmd_log(sender, reply, message.strip()[5:].strip())
            elif msg_lower == "!liste":
                self._cmd_liste(reply)
            elif msg_lower == "!durum":
                self._cmd_durum(reply)
            elif msg_lower == "!yardim":
                self._cmd_yardim(reply)

    # ── Komutlar ──────────────────────────────────────────────────────────
    def _parse_host_port(self, arg: str):
        p    = arg.split(":")
        host = p[0].strip()
        try:
            port = int(p[1]) if len(p) > 1 else SLAVE_PORT
        except ValueError:
            port = SLAVE_PORT
        return host, port

    def _yetkili(self, requester, target) -> bool:
        if BOT_OWNER and requester.lower() != BOT_OWNER.lower():
            self.send(f"PRIVMSG {target} :Yetkisiz işlem.")
            return False
        return True

    def _cmd_ekle(self, requester, target, arg):
        if not self._yetkili(requester, target): return
        host, port = self._parse_host_port(arg)
        if not host:
            self.send(f"PRIVMSG {target} :Kullanım: !ekle irc.sunucu.com[:port]")
            return
        key = f"{host}:{port}"
        with slave_lock:
            if key in slave_bots:
                self.send(f"PRIVMSG {target} :Zaten bağlı: {key}")
                return
        self._start_slave(host, port)
        db_save()
        self.send(f"PRIVMSG {target} :Bağlanılıyor → {key}")

    def _cmd_sil(self, requester, target, arg):
        if not self._yetkili(requester, target): return
        host, port = self._parse_host_port(arg)
        key = f"{host}:{port}"
        with slave_lock:
            bot = slave_bots.get(key)
        if bot:
            bot.stop()
            db_remove(host, port)
            self.send(f"PRIVMSG {target} :{key} → durduruldu ve silindi.")
        else:
            with slave_lock:
                aktif = list(slave_bots.keys())
            self.send(f"PRIVMSG {target} :Bulunamadı: {key}")
            if aktif:
                self.send(f"PRIVMSG {target} :Aktif: {', '.join(aktif)}")

    def _cmd_restart(self, requester, target, arg):
        if not self._yetkili(requester, target): return
        host, port = self._parse_host_port(arg)
        key = f"{host}:{port}"
        with slave_lock:
            bot = slave_bots.get(key)
        if bot:
            bot.restart()
            self.send(f"PRIVMSG {target} :{key} → yeniden bağlanılıyor...")
        else:
            with slave_lock:
                aktif = list(slave_bots.keys())
            self.send(f"PRIVMSG {target} :Bulunamadı: {key}")
            if aktif:
                self.send(f"PRIVMSG {target} :Aktif: {', '.join(aktif)}")

    def _cmd_restartall(self, requester, target):
        """Tüm slave'leri sırayla yeniden bağlat."""
        if not self._yetkili(requester, target): return
        with slave_lock:
            bots = list(slave_bots.values())
        if not bots:
            self.send(f"PRIVMSG {target} :Aktif slave bot yok.")
            return
        self.send(f"PRIVMSG {target} :{len(bots)} slave yeniden başlatılıyor...")
        def _do_restartall():
            for bot in bots:
                bot.restart()
                time.sleep(3)
        threading.Thread(target=_do_restartall, daemon=True).start()

    def _cmd_izle(self, requester, target, keyword):
        """!izle <keyword> — Kanalda bu kelime geçince bildirim al."""
        if not self._yetkili(requester, target): return
        if not keyword:
            self.send(f"PRIVMSG {target} :Kullanım: !izle <keyword>")
            return
        with watch_lock:
            mevcut = [k for k, _ in watch_keywords]
            if keyword.lower() in [k.lower() for k in mevcut]:
                self.send(f"PRIVMSG {target} :Zaten izleniyor: {keyword}")
                return
            watch_keywords.append((keyword, requester))
        self.send(f"PRIVMSG {target} :İzlemeye alındı: {keyword}")

    def _cmd_izlesil(self, requester, target, keyword):
        """!izlesil <keyword> — İzleme listesinden çıkar."""
        if not self._yetkili(requester, target): return
        with watch_lock:
            before = len(watch_keywords)
            watch_keywords[:] = [(k, n) for k, n in watch_keywords
                                  if k.lower() != keyword.lower()]
            after = len(watch_keywords)
        if before != after:
            self.send(f"PRIVMSG {target} :İzlemeden çıkarıldı: {keyword}")
        else:
            self.send(f"PRIVMSG {target} :Bulunamadı: {keyword}")

    def _cmd_izlelist(self, target):
        """!izlelist — Aktif keyword listesi."""
        with watch_lock:
            kws = list(watch_keywords)
        if kws:
            liste = ", ".join(f"{k} ({n})" for k, n in kws)
            self.send(f"PRIVMSG {target} :İzlenen ({len(kws)}): {liste}")
        else:
            self.send(f"PRIVMSG {target} :İzleme listesi boş.")

    def _cmd_pmtemizle(self, requester, target, arg):
        """!pmtemizle irc.sunucu.com — O sunucunun PM logunu sıfırla."""
        if not self._yetkili(requester, target): return
        host, port = self._parse_host_port(arg)
        key = f"{host}:{port}"
        with slave_lock:
            bot = slave_bots.get(key)
        if bot:
            with bot.pm_lock:
                bot.pm_log.clear()
            self.send(f"PRIVMSG {target} :{key} PM logu temizlendi.")
        else:
            self.send(f"PRIVMSG {target} :Bulunamadı: {key}")

    def _cmd_log(self, requester, target, arg):
        """!log irc.sunucu.com — Son 10 log satırını kanala yaz."""
        if not self._yetkili(requester, target): return
        host, port = self._parse_host_port(arg)
        name = host.replace(".", "_")
        log_file = os.path.join(LOG_DIR, f"{name}.log")
        if not os.path.exists(log_file):
            self.send(f"PRIVMSG {target} :Log dosyası bulunamadı: {host}")
            return
        try:
            with open(log_file, "r", encoding="utf-8", errors="replace") as f:
                satirlar = f.readlines()
            son10 = satirlar[-10:]
            self.send(f"PRIVMSG {target} :── Son 10 log [{host}] ──")
            for s in son10:
                self.send(f"PRIVMSG {target} :{s.rstrip()}")
                time.sleep(0.05)
        except Exception as e:
            self.send(f"PRIVMSG {target} :Log okunamadı: {e}")

    def _cmd_liste(self, target):
        with slave_lock:
            keys = list(slave_bots.keys())
        if keys:
            self.send(f"PRIVMSG {target} :Aktif ({len(keys)}): {', '.join(keys)}")
        else:
            self.send(f"PRIVMSG {target} :Aktif slave bot yok.")

    def _cmd_durum(self, target):
        with slave_lock:
            bots = list(slave_bots.values())
        if not bots:
            self.send(f"PRIVMSG {target} :Aktif slave bot yok.")
            return
        for bot in bots:
            for satir in bot.durum_detay():
                self.send(f"PRIVMSG {target} :{satir}")
                time.sleep(0.05)

    def _cmd_yardim(self, target):
        komutlar = [
            "!ekle irc.sunucu.com[:port]    — Sunucuya slave bağla",
            "!sil irc.sunucu.com[:port]     — Slave'i durdur ve sil",
            "!restart irc.sunucu.com[:port] — Slave'i yeniden bağlat",
            "!restartall                    — Tüm slave'leri yeniden bağlat",
            "!liste                         — Aktif slave listesi",
            "!durum                         — Her slave'in detaylı durumu",
            "!izle <keyword>                — Kanalda kelime geçince bildirim",
            "!izlesil <keyword>             — İzlemeyi kaldır",
            "!izlelist                      — İzlenen keyword listesi",
            "!pmtemizle irc.sunucu.com      — PM logunu sıfırla",
            "!log irc.sunucu.com            — Son 10 log satırı",
        ]
        for c in komutlar:
            self.send(f"PRIVMSG {target} :{c}")
            time.sleep(0.05)

    def _restore_servers(self):
        entries = db_load()
        if not entries:
            return
        log.info(f"[Master] {len(entries)} kayıtlı sunucu yükleniyor...")
        for e in entries:
            host = e.get("host", "")
            port = e.get("port", SLAVE_PORT)
            key  = f"{host}:{port}"
            with slave_lock:
                if key in slave_bots:
                    continue
            self._start_slave(host, port)
            log.info(f"[Master] Otomatik başlatıldı: {key}")
            time.sleep(1)

    def run(self):
        global master_ref
        master_ref = self
        log.info("[Master] Ana bot başlatılıyor...")
        while self.running:
            self._oper_done = False
            self._joined    = False
            try:
                self.connect()
                self.register()
                while self.running:
                    try:
                        for line in self.read_lines():
                            self.handle_line(line)
                    except socket.timeout:
                        log.warning("[Master] Ping zaman aşımı.")
                        break
                    except (ConnectionResetError, OSError) as e:
                        log.error(f"[Master] Bağlantı hatası: {e}")
                        break
            except Exception as e:
                log.error(f"[Master] Beklenmeyen hata: {e}")
            finally:
                self.disconnect()
                if self.running:
                    log.info(f"[Master] {RECONNECT_DELAY} sn sonra yeniden bağlanılacak...")
                    time.sleep(RECONNECT_DELAY)


# ══════════════════════════════════════════════════════════════════════════════
if __name__ == "__main__":
    master = MasterBot()
    try:
        master.run()
    except KeyboardInterrupt:
        log.info("Kapatılıyor...")
        master.stop()
        with slave_lock:
            for bot in list(slave_bots.values()):
                bot.stop()
        log.info("Tüm botlar durduruldu.")
 
Eski 02 Nisan 2026, 12:46   #2
Çevrimdışı
Mezarkabul
Çevrimdışı
Mezarkabul Kullanıcısının Avatarı

Varsayılan Yanıt: Python BNC bot

İçeriği görmek için buraya tıklayarak kaydolunuz.
Maaşallah, ellerine sağlık.
__________________
İstemem! Eksik olsun!
Tahtlarınız, şanınız, sahte zaferleriniz!
Bir tek onurum yeter bana
Yalnız ölsem de eğilmem asla!
 
Eski 02 Nisan 2026, 13:22   #3
Çevrimdışı
Hytech
Çevrimdışı
Hayat Bazen Şaşırtır.
Hytech Kullanıcısının Avatarı

Varsayılan Yanıt: Python BNC bot

İçeriği görmek için buraya tıklayarak kaydolunuz.
eline sağlık
 
  

Yer İmleri


Şu anda bu konuyu görüntüleyen etkin kullanıcılar: 1 (0 üye ve 1 konuk)
 

Yayınlama Kuralları


Forum saati GMT +3 olarak ayarlanmıştır. Şu an saat: 11:08

Forum
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2026, vBulletin Solutions Inc.
IRCdForum.Net, lisanslı vBulletin® kullanmaktadır.
Yasal Uyarı
IRCdForum.Net; 5651 sayılı kanuna göre yer sağlayıcı bir forum sitesidir. Dolayısıyla forumdaki içeriklerden ilgili içeriği paylaşan üyeler sorumludur. Hukuka aykırı içerik bildirimleri için İLETİŞİM linkinden bildirim gönderebilirsiniz.


güzel hosting

IRCdForum.Net; altyapı gücünü Güzel Hosting'den alır.