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.")