NodeMCU ile Sunucu Durumu İzleme Sistemi Oluşturma

NodeMCU ile Sunucu Durumu İzleme Sistemi Oluşturma

NodeMCU ile Sunucu Durumu İzleme Sistemi Oluşturma


Merhaba sevgili okuyucular! Bu yazıda, NodeMCU geliştirme kartını kullanarak Telegram botuyla entegre bir sunucu durumu izleme sistemi nasıl oluşturulacağını adım adım göstereceğiz. Bu sistem, sunucularınızın portlarının durumunu izlemek ve herhangi bir port çöktüğünde bildirim almak için kullanışlı bir araç sağlar. Haydi başlayalım!

Özet

Gereksinimler

NodeMCU (ESP8266 tabanlı)
WiFi ağına erişim
Telegram hesabı ve bot token
Arduino IDE ve gerekli kütüphaneler (ESP8266WiFi, UniversalTelegramBot, LittleFS, ArduinoJson)

Adım 1: Gerekli Kütüphanelerin İndirilmesi

İlk olarak, Arduino IDE'yi açın ve gerekli kütüphaneleri indirin. ESP8266WiFi, UniversalTelegramBot, LittleFS ve ArduinoJson kütüphanelerine ihtiyacımız olacak. Bu kütüphaneleri Arduino IDE'ye ekledikten sonra projemize başlayabiliriz.

Adım 2: Telegram Bot Oluşturma

Telegram botunuzu oluşturmak için BotFather ile iletişime geçin ve yeni bir bot oluşturun. Bot oluşturduktan sonra size bir bot token'i verilecektir. Bu token'i ileride kullanmak üzere saklayın.

Adım 3: Kodun Hazırlanması

Yukarıdaki kodu, Arduino IDE'de yeni bir proje oluşturarak yapıştırın. Kodun içinde WiFi ağına ve Telegram botunuzun token'ına dair gerekli bilgileri güncelleyin.

Adım 4: Ana Kontrol Döngüsü

Kodun ana kontrol döngüsü, Telegram mesajlarını dinlemek ve sunucu durumunu izlemek için iki önemli işlevi gerçekleştirir. Telegram mesajları işlenirken, /start, /commands, /register gibi komutlar kullanıcıya yardımcı mesajlar gönderir. Sunucu durumu izlemesi yapılırken, belirli aralıklarla sunucuların durumu kontrol edilir ve çöken portlar için kullanıcıya bildirim gönderilir.

Adım 5: Sunucu Bilgilerinin Kaydedilmesi

Kullanıcıların eklediği sunucu bilgileri LittleFS üzerinde JSON formatında saklanır. Bu sayede cihaz yeniden başlatıldığında bile bilgiler kaybolmaz.

Adım 6: Bot İstatistikleri

Kod, komut /stats kullanıldığında kayıtlı hesap sayısını ve toplam sunucu sayısını gösterir.

Adım 7: Derleme ve Yükleme

Kodu Arduino IDE'de derleyin ve NodeMCU'ya yükleyin. Daha sonra NodeMCU'yu güç kaynağına bağlayın ve kodunuzun çalıştığından emin olun.

Sonuç

Artık kendi sunucu durumu izleme sistemini oluşturduk! NodeMCU cihazınız, sunucularınızın portlarını sürekli olarak izleyecek ve herhangi bir sorun oluştuğunda size bildirim gönderecektir. Bu proje, sunucu yönetimi ve izleme konusunda büyük kolaylık sağlar.

Proje Kodları ve Açıklaması

Urhoba.h - Kodlar

#ifndef URHOBA_H
#define URHOBA_H

#include 
#include 
#include 
#include 

class UrhobaServer{
    private:
        String serverName;
        IPAddress targetIP;
        int targetPort;
    public:
        UrhobaServer(String server_name, IPAddress ip_address, int port);
        IPAddress getTargetIP() const;
        int getTargetPort() const;
        String getServerName() const;
        bool getServerStatus() const;
        void setServerName(String server_name);
        void setServerPort(int port);
        void setServerIp(IPAddress ip_address);
};

class Account{
    private:
        String chatId;
        std::vector servers;
    public:
        Account(String chat_id);
        String getChatId();
        bool addServer(String server_name, IPAddress ip_address, int port);
        bool removeServer(int server_index);
        UrhobaServer getServer(int server_index);
        UrhobaServer& getServerRef(int server_index);
        std::vector getAllServers();
        std::vector getAllServersConst() const;
};  

class AccountManager{
    private:
        std::vector accounts;
    public:
        bool addAccount(String chat_id);
        bool removeAccount(String chat_id);
        Account* findAccount(String chat_id);
        std::vector getAccounts();
};

#endif

Kodların Açıklaması

UrhobaServer Sınıfı

Bu sınıf, izlenecek sunucunun bilgilerini tutan bir sunucu nesnesi oluşturur. İşte bu sınıfın özellikleri ve işlevleri:

  • private bölümünde serverName, targetIP ve targetPort adlı üye değişkenler bulunur. Bu değişkenler, sunucu adı, hedef IP adresi ve hedef portunu tutar.
  • UrhobaServer constructor'ı, bir sunucu nesnesi oluştururken gerekli olan bilgileri alır.
  • getTargetIP()getTargetPort() ve getServerName(), ilgili sunucu özelliklerini döndürür.
  • getServerStatus(), sunucunun durumunu (çalışıyor/çöktü) belirler.
  • setServerName(String server_name), setServerPort(int port) ve setServerIp(IPAddress ip_address), ilgili sunucu özelliklerini günceller.

Account Sınıfı

Bu sınıf, kullanıcının sahip olduğu sunucuları ve bu sunuculara dair işlemleri yönetir. İşte bu sınıfın özellikleri ve işlevleri:

private bölümünde chatId ve servers adlı üye değişkenler bulunur. chatId, kullanıcının Telegram sohbet kimliğini tutar. servers, kullanıcının eklediği sunucuları içeren bir UrhobaServer vektörünü tutar.
Account constructor'ı, kullanıcının hesabını oluştururken chat kimliğini alır.
getChatId(), kullanıcının chat kimliğini döndürür.
addServer(String server_name, IPAddress ip_address, int port), kullanıcının hesabına yeni bir sunucu ekler.
removeServer(int server_index), belirtilen indeksteki sunucuyu hesaptan kaldırır.
getServer(int server_index), belirtilen indeksteki sunucunun bilgilerini döndürür.
getServerRef(int server_index), belirtilen indeksteki sunucunun referansını döndürür (güncelleme için kullanılır).
getAllServers(), hesaba eklenmiş tüm sunucuları döndürür.
getAllServersConst(), hesaba eklenmiş tüm sunucuları sadece okuma amaçlı döndürür.

AccountManager Sınıfı

Bu sınıf, tüm kullanıcı hesaplarını yönetir. İşte bu sınıfın özellikleri ve işlevleri:

private bölümünde accounts adlı bir Account vektörü bulunur. Bu vektör, tüm kullanıcı hesaplarını tutar.
addAccount(String chat_id), yeni bir kullanıcı hesabı ekler.
removeAccount(String chat_id), belirtilen chat kimliğine sahip kullanıcı hesabını kaldırır.
findAccount(String chat_id), belirtilen chat kimliğine sahip kullanıcı hesabını bulur ve geri döndürür.
getAccounts(), tüm kullanıcı hesaplarını döndürür.

Urhoba.cpp - Kodlar

#include "urhoba.h"

UrhobaServer::UrhobaServer(String server_name, IPAddress ip_address, int port) {
  serverName = server_name;
  targetIP = ip_address;
  targetPort = port;
}

IPAddress UrhobaServer::getTargetIP() const {
  return targetIP;
}

int UrhobaServer::getTargetPort() const {
  return targetPort;
}

String UrhobaServer::getServerName() const {
  return serverName;
}

bool UrhobaServer::getServerStatus() const {
  WiFiClient client;

  IPAddress serverIP = targetIP;
  int serverPort = targetPort;

  int maxAttempts = 3;
  unsigned long startTime = millis();
  for (int i = 0; i < maxAttempts; i++) {
    if (client.connect(serverIP, serverPort)) {
      client.stop();
      return true;
    }
    unsigned long currentTime = millis();
    if (currentTime - startTime >= 100) {
      startTime = currentTime;
    }
  }
  return false;
}

void UrhobaServer::setServerName(String server_name) {
  serverName = server_name;
}

void UrhobaServer::setServerPort(int port) {
  targetPort = port;
}

void UrhobaServer::setServerIp(IPAddress ip_address) {
  targetIP = ip_address;
}

Account::Account(String chat_id) {
  chatId = chat_id;
}

String Account::getChatId() {
  return chatId;
}

bool Account::addServer(String server_name, IPAddress ip_address, int port) {
  for (const UrhobaServer& server : servers) {
    if (server.getTargetIP() == ip_address && server.getTargetPort() == port) {
      return false;
    }
  }
  UrhobaServer addedServer(server_name, ip_address, port);
  servers.push_back(addedServer);
  return true;
}

bool Account::removeServer(int server_index) {
  if (server_index >= 0 && server_index < servers.size()) {
    servers.erase(servers.begin() + server_index);
    return true;
  }
  return false;
}

UrhobaServer Account::getServer(int server_index) {
  if (server_index >= 0 && server_index < servers.size()) {
    return servers[server_index];
  }
  return UrhobaServer("", IPAddress(0, 0, 0, 0), 0);
}

UrhobaServer& Account::getServerRef(int server_index) {
  if (server_index >= 0 && server_index < servers.size()) {
    return servers[server_index];
  }
  static UrhobaServer dummyServer("", IPAddress(0, 0, 0, 0), 0);
  return dummyServer;
}

std::vector Account::getAllServers() {
  return servers;
}

std::vector Account::getAllServersConst() const {
  return servers;
}


bool AccountManager::addAccount(String chat_id) {
  bool accountExists = false;
  for (int i = 0; i < accounts.size(); i++) {
    if (accounts[i].getChatId() == chat_id) {
      accountExists = true;
      break;
    }
  }

  if (!accountExists) {
    accounts.push_back(Account(chat_id));
    return true;
  } else {
    return false;
  }
}

bool AccountManager::removeAccount(String chat_id) {
  for (int i = 0; i < accounts.size(); i++) {
    if (accounts[i].getChatId() == chat_id) {
      accounts.erase(accounts.begin() + i);
      return true;
    }
  }

  return false;
}

Account* AccountManager::findAccount(String chat_id) {
  for (int i = 0; i < accounts.size(); i++) {
    if (accounts[i].getChatId() == chat_id) {
      return &accounts[i];
    }
  }
  return nullptr;
}

std::vector AccountManager::getAccounts() {
  return accounts;
}

Kodların Açıklanması

UrhobaServer Sınıfı Implementasyonu
Bu bölüm, UrhobaServer sınıfının üye işlevlerinin ve constructor'ının implementasyonunu içerir.

  • UrhobaServer::UrhobaServer(String server_name, IPAddress ip_address, int port) constructor, sunucu nesnesi oluştururken gerekli olan bilgileri alır ve üye değişkenleri ayarlar.
  • UrhobaServer::getTargetIP() const üye işlevi, sunucunun hedef IP adresini döndürür.
  • UrhobaServer::getTargetPort() const üye işlevi, sunucunun hedef portunu döndürür.
  • UrhobaServer::getServerName() const üye işlevi, sunucunun adını döndürür.
  • UrhobaServer::getServerStatus() const üye işlevi, sunucunun durumunu (çalışıyor/çöktü) kontrol eder. Bu işlev, sunucuya bağlanarak durum kontrolünü yapar.
  • UrhobaServer::setServerName(String server_name) üye işlevi, sunucunun adını günceller.
  • UrhobaServer::setServerPort(int port) üye işlevi, sunucunun portunu günceller.
  • UrhobaServer::setServerIp(IPAddress ip_address) üye işlevi, sunucunun IP adresini günceller.

Account Sınıfı Implementasyonu
Bu bölüm, Account sınıfının üye işlevlerinin ve constructor'ının implementasyonunu içerir.

  • Account::Account(String chat_id) constructor, bir kullanıcı hesabını oluştururken chat kimliğini alır ve ilgili üye değişkeni ayarlar.
  • Account::getChatId() üye işlevi, kullanıcının chat kimliğini döndürür.
  • Account::addServer(String server_name, IPAddress ip_address, int port) üye işlevi, kullanıcının hesabına yeni bir sunucu ekler.
  • Account::removeServer(int server_index) üye işlevi, belirtilen indeksteki sunucuyu hesaptan kaldırır.
  • Account::getServer(int server_index) üye işlevi, belirtilen indeksteki sunucunun bilgilerini döndürür.
  • Account::getServerRef(int server_index) üye işlevi, belirtilen indeksteki sunucunun referansını döndürür (güncelleme için kullanılır).
  • Account::getAllServers() üye işlevi, hesaba eklenmiş tüm sunucuları döndürür.
  • Account::getAllServersConst() const üye işlevi, hesaba eklenmiş tüm sunucuları sadece okuma amaçlı döndürür.

AccountManager Sınıfı Implementasyonu
Bu bölüm, AccountManager sınıfının üye işlevlerinin implementasyonunu içerir.

  • AccountManager::addAccount(String chat_id) üye işlevi, yeni bir kullanıcı hesabı ekler.
  • AccountManager::removeAccount(String chat_id) üye işlevi, belirtilen chat kimliğine sahip kullanıcı hesabını kaldırır.
  • AccountManager::findAccount(String chat_id) üye işlevi, belirtilen chat kimliğine sahip kullanıcı hesabını bulur ve geri döndürür.
  • AccountManager::getAccounts() üye işlevi, tüm kullanıcı hesaplarını döndürür.

server_checker.ino - Kodlar

// www.urhoba.net
#include 
#include 
#include 
#include 
#include 
#include 
#include "urhoba.h"

#define WIFI_SSID "wifi_ssid"
#define WIFI_PASSWORD "wifi_sifresi"
#define BOT_TOKEN "telegram_bot_token"

AccountManager accountManager;

const unsigned long BOT_MTBS = 1000;

X509List cert(TELEGRAM_CERTIFICATE_ROOT);
WiFiClientSecure secured_client;
UniversalTelegramBot bot(BOT_TOKEN, secured_client);
unsigned long bot_lasttime;

const unsigned long SERVER_CHECK_INTERVAL = 1800000;
unsigned long lastServerCheckTime = 0;

// Start messages
void startMessage(String chat_id, String text, String from_name) {
  if (text == "/start") {
    String msg = "Welcome to UrhobA Server Checker System, " + from_name + ".\n\n";
    msg += "This system is designed to help you monitor the status of your server's ports. If any port crashes, you'll receive a notification message to keep you informed.\n";
    msg += "To get started, use the /commands command to see a list of available commands.\n";
    msg += "Feel free to explore the system and manage your server effortlessly!";
    bot.sendMessage(chat_id, msg, "");
  }
}

// Command messages
void commandsMessage(String chat_id, String text, String from_name) {
  if (text == "/commands") {
    String msg = "Hello " + from_name + "!\n\n";
    msg += "Available commands:\n\n";
    msg += "Account:\n";
    msg += "/register: Create a new account.\n";
    msg += "/remove: Remove your account.\n\n";
    msg += "Server:\n";
    msg += "/add_server [ip] [port] [name]: Add a new server.\n";
    msg += "/remove_server [index]: Remove a server.\n";
    msg += "/get_server [index]: Get server details.\n";
    msg += "/set_server_name [index] [name]: Set server name.\n";
    msg += "/set_server_port [index] [port]: Set server port.\n";
    msg += "/set_server_ip [index] [ip]: Set server ip.\n";
    msg += "/get_servers: List all your servers.\n";
    msg += "/stats: Show bot stats.\n\n";
    msg += "For more info or help, feel free to ask. Enjoy using our system!";
    bot.sendMessage(chat_id, msg, "");
  }
}

// Create account
void registerCommand(String chat_id, String text, String from_name) {
  if (text == "/register") {
    String msg = "Your registration request has been received.";

    bot.sendMessage(chat_id, msg, "");

    bool accountAdded = accountManager.addAccount(chat_id);

    if (accountAdded) {
      msg = "You have successfully registered!";
      saveAccountsToFile();
    } else {
      msg = "Your account already exists!";
    }

    bot.sendMessage(chat_id, msg, "");
  }
}

// Remove account
void removeCommand(String chat_id, String text, String from_name) {
  if (text == "/remove") {
    String msg = "Removing your account.";

    bot.sendMessage(chat_id, msg, "");

    bool found = accountManager.removeAccount(chat_id);

    if (found) {
      msg = "Your account has been removed.";
      saveAccountsToFile();
    } else {
      msg = "Your account does not exist.";
    }

    bot.sendMessage(chat_id, msg, "");
  }
}

// Add server to account
void addServerCommand(String chat_id, String text, String from_name) {
  if (text.startsWith("/add_server")) {
    String msg;

    // Extracting server address, port, and server name from text
    int ip_start = text.indexOf("[") + 1;
    int ip_end = text.indexOf("]");
    int port_start = text.indexOf("[", ip_end) + 1;
    int port_end = text.indexOf("]", port_start);
    int name_start = text.indexOf("[", port_end) + 1;
    int name_end = text.indexOf("]", name_start);

    if (ip_start != -1 && ip_end != -1 && port_start != -1 && port_end != -1 && name_start != -1 && name_end != -1) {
      String server_address = text.substring(ip_start, ip_end);
      String port_str = text.substring(port_start, port_end);
      String server_name = text.substring(name_start, name_end);

      IPAddress ip_address;
      if (WiFi.hostByName(server_address.c_str(), ip_address)) {
        int port = port_str.toInt();

        Account* account = accountManager.findAccount(chat_id);
        if (account) {
          bool serverAdded = account->addServer(server_name, ip_address, port);
          if (serverAdded) {
            msg = "Server added successfully!";
            saveAccountsToFile();
          } else {
            msg = "Server with the same IP address and port already exists.";
          }
        } else {
          msg = "Account not found. Please register first.";
        }
      } else {
        msg = "Invalid server address.";
      }
    } else {
      msg = "Invalid format. Please use /add_server [server_address] [port] [server_name].";
    }

    bot.sendMessage(chat_id, msg, "");
  }
}

// Remove server to account
void removeServerCommand(String chat_id, String text, String from_name) {
  if (text.startsWith("/remove_server")) {
    String msg;

    int index_start = text.indexOf("[") + 1;
    int index_end = text.indexOf("]");

    if (index_start != -1 && index_end != -1) {
      String index_str = text.substring(index_start, index_end);

      int server_index = index_str.toInt();

      Account* account = accountManager.findAccount(chat_id);

      if (account) {
        bool serverRemoved = account->removeServer(server_index);

        if (serverRemoved) {
          msg = "Server removed successfully!";
          saveAccountsToFile();
        } else {
          msg = "Invalid server index.";
        }
      } else {
        msg = "Account not found. Please register first.";
      }
    } else {
      msg = "Invalid format. Please use /remove_server [server_index].";
    }

    bot.sendMessage(chat_id, msg, "");
  }
}

// Get server
void getServerCommand(String chat_id, String text, String from_name) {
  if (text.startsWith("/get_server")) {
    int server_index = -1;
    int index_start = text.indexOf("[") + 1;
    int index_end = text.indexOf("]");

    if (index_start != -1 && index_end != -1) {
      String index_str = text.substring(index_start, index_end);
      server_index = index_str.toInt();
    }

    if (server_index >= 0) {
      Account* account = accountManager.findAccount(chat_id);

      if (account) {
        UrhobaServer server = account->getServer(server_index);

        if (server.getTargetPort() != -1) {
          String msg = "Here is server number " + String(server_index) + ":\n";
          msg += "Name: " + server.getServerName() + "\n";
          msg += "IP: " + server.getTargetIP().toString() + "\n";
          msg += "Port: " + String(server.getTargetPort()) + "\n";
          msg += "Status: " + String(server.getServerStatus()) + "\n";

          bot.sendMessage(chat_id, msg, "");
        } else {
          bot.sendMessage(chat_id, "Invalid server index.", "");
        }
      } else {
        bot.sendMessage(chat_id, "Account not found. Please register first.", "");
      }
    } else {
      bot.sendMessage(chat_id, "Invalid server index format. Please use /get_server [server_index].", "");
    }
  }
}

// Get all servers
void getServersCommand(String chat_id, String text, String from_name) {
  if (text == "/get_servers") {
    Account* account = accountManager.findAccount(chat_id);

    if (account) {
      std::vector servers = account->getAllServers();

      if (!servers.empty()) {
        String msg = "Here are your added servers:\n\n";

        for (int i = 0; i < servers.size(); i++) {
          UrhobaServer server = servers[i];
          msg += "[" + String(i) + "] Name: " + server.getServerName() + ", Status: " + String(server.getServerStatus()) + "\n";
        }

        msg += "\nTo view details of a specific server, use /get_server [server_index].";
        bot.sendMessage(chat_id, msg, "");
      } else {
        bot.sendMessage(chat_id, "You haven't added any servers yet.", "");
      }
    } else {
      bot.sendMessage(chat_id, "Account not found. Please register first.", "");
    }
  }
}

// Bot stats
void statsCommand(String chat_id, String text) {
  if (text == "/stats") {
    int numAccounts = accountManager.getAccounts().size();
    int totalServers = 0;

    for (const Account& account : accountManager.getAccounts()) {
      totalServers += account.getAllServersConst().size();
    }

    String msg = "Total accounts: " + String(numAccounts) + "\n";
    msg += "Total servers: " + String(totalServers);

    bot.sendMessage(chat_id, msg, "");
  }
}

// Set server name
void setServerNameCommand(String chat_id, String text) {
  if (text.startsWith("/set_server_name")) {
    int index_start = text.indexOf("[") + 1;
    int index_end = text.indexOf("]");
    int name_start = text.indexOf("[", index_end) + 1;
    int name_end = text.indexOf("]", name_start);

    if (index_start != -1 && index_end != -1 && name_start != -1 && name_end != -1) {
      String index_str = text.substring(index_start, index_end);
      String server_name = text.substring(name_start, name_end);

      int server_index = index_str.toInt();

      Account* account = accountManager.findAccount(chat_id);

      if (account) {
        UrhobaServer& server = account->getServerRef(server_index);
        server.setServerName(server_name);
        bot.sendMessage(chat_id, "Server name updated successfully!", "");
        saveAccountsToFile();
      } else {
        bot.sendMessage(chat_id, "Account not found. Please register first.", "");
      }
    } else {
      bot.sendMessage(chat_id, "Invalid format. Please use /set_server_name [server_index] [server_name].", "");
    }
  }
}

// Set server port
void setServerPortCommand(String chat_id, String text) {
  if (text.startsWith("/set_server_port")) {
    int index_start = text.indexOf("[") + 1;
    int index_end = text.indexOf("]");
    int port_start = text.indexOf("[", index_end) + 1;
    int port_end = text.indexOf("]", port_start);

    if (index_start != -1 && index_end != -1 && port_start != -1 && port_end != -1) {
      String index_str = text.substring(index_start, index_end);
      String port_str = text.substring(port_start, port_end);

      int server_index = index_str.toInt();
      int new_port = port_str.toInt();

      Account* account = accountManager.findAccount(chat_id);

      if (account) {
        UrhobaServer& server = account->getServerRef(server_index);
        server.setServerPort(new_port);
        bot.sendMessage(chat_id, "Server port updated successfully!", "");
        saveAccountsToFile();
      } else {
        bot.sendMessage(chat_id, "Account not found. Please register first.", "");
      }
    } else {
      bot.sendMessage(chat_id, "Invalid format. Please use /set_server_port [server_index] [new_port].", "");
    }
  } 
}

// Set server ip
void setServerIpCommand(String chat_id, String text) {
  if (text.startsWith("/set_server_ip")) {
    int index_start = text.indexOf("[") + 1;
    int index_end = text.indexOf("]");
    int ip_start = text.indexOf("[", index_end) + 1;
    int ip_end = text.indexOf("]", ip_start);

    if (index_start != -1 && index_end != -1 && ip_start != -1 && ip_end != -1) {
      String index_str = text.substring(index_start, index_end);
      String ip_str = text.substring(ip_start, ip_end);

      int server_index = index_str.toInt();
      IPAddress new_ip;

      if (WiFi.hostByName(ip_str.c_str(), new_ip)) {
        Account* account = accountManager.findAccount(chat_id);

        if (account) {
          UrhobaServer& server = account->getServerRef(server_index);
          server.setServerIp(new_ip);
          bot.sendMessage(chat_id, "Server IP updated successfully!", "");
          saveAccountsToFile();
        } else {
          bot.sendMessage(chat_id, "Account not found. Please register first.", "");
        }
      } else {
        bot.sendMessage(chat_id, "Invalid IP address.", "");
      }
    } else {
      bot.sendMessage(chat_id, "Invalid format. Please use /set_server_ip [server_index] [new_ip].", "");
    }
  }
}

// Handle to telegram messages
void handleNewMessages(int numNewMessages) {
  for (int i = 0; i < numNewMessages; i++) {
    String chat_id = bot.messages[i].chat_id;
    String text = bot.messages[i].text;
    String from_name = bot.messages[i].from_name;
    if (from_name == "")
      from_name = "Guest";

    startMessage(chat_id, text, from_name);
    commandsMessage(chat_id, text, from_name);
    registerCommand(chat_id, text, from_name);
    removeCommand(chat_id, text, from_name);
    addServerCommand(chat_id, text, from_name);
    statsCommand(chat_id, text);
    removeServerCommand(chat_id, text, from_name);
    setServerNameCommand(chat_id, text);
    setServerPortCommand(chat_id, text);
    setServerIpCommand(chat_id, text);
    if (text == "/get_servers")
      getServersCommand(chat_id, text, from_name);
    else
      getServerCommand(chat_id, text, from_name);
  }
}

// Check servers
void checkServers() {
  unsigned long currentTime = millis();

  if (currentTime - lastServerCheckTime >= SERVER_CHECK_INTERVAL) {
    lastServerCheckTime = currentTime;

    for (Account& account : accountManager.getAccounts()) {
      String chatId = account.getChatId();
      String msg = "Your servers:\n";

      for (int i = 0; i < account.getAllServers().size(); i++) {
        UrhobaServer& server = account.getServerRef(i);
        bool serverStatus = server.getServerStatus();

        if (!serverStatus) {
          msg += "Server IP: " + server.getTargetIP().toString() + ", Port: " + String(server.getTargetPort()) + " is down.\n";
        }
      }

      if (msg != "Your servers:\n") {
        bot.sendMessage(chatId, msg, "");
      }
    }
  }
}

// Save all accounts
void saveAccountsToFile() {
  File file = LittleFS.open("/accounts.json", "w");
  if (!file) {
    Serial.println("Error opening file for writing");
    return;
  }

  DynamicJsonDocument doc(1024);
  JsonArray accountsArray = doc.createNestedArray("accounts");

  for (Account& account : accountManager.getAccounts()) {
    JsonObject accountObject = accountsArray.createNestedObject();
    accountObject["chatId"] = account.getChatId();

    JsonArray serversArray = accountObject.createNestedArray("servers");
    std::vector servers = account.getAllServers();
    for (const UrhobaServer& server : servers) {
      JsonObject serverObject = serversArray.createNestedObject();
      serverObject["name"] = server.getServerName();
      serverObject["ip"] = server.getTargetIP().toString();
      serverObject["port"] = server.getTargetPort();
    }
  }

  serializeJson(doc, file);

  file.close();
}

// Get all accounts
void loadAccountsFromFile() {
  File file = LittleFS.open("/accounts.json", "r");
  if (!file) {
    Serial.println("Error opening file for reading");
    return;
  }

  DynamicJsonDocument doc(1024);
  DeserializationError error = deserializeJson(doc, file);

  if (error) {
    Serial.println("Error parsing JSON");
    return;
  }

  JsonArray accountsArray = doc["accounts"];

  for (const JsonVariant& accountVariant : accountsArray) {
    JsonObject accountObject = accountVariant.as();
    String chatId = accountObject["chatId"].as();
    accountManager.addAccount(chatId);

    JsonArray serversArray = accountObject["servers"];

    for (const JsonVariant& serverVariant : serversArray) {
      JsonObject serverObject = serverVariant.as();
      String ipStr = serverObject["ip"].as();
      String nameStr = serverObject["name"].as();
      IPAddress ip;
      if (ip.fromString(ipStr)) {
        int port = serverObject["port"].as();
        accountManager.findAccount(chatId)->addServer(nameStr, ip, port);
      }
    }
  }

  file.close();
}

void setup() {

  // WIFI & Telegram Begin

  Serial.begin(115200);
  Serial.println();

  Serial.print("Connecting to Wifi SSID ");
  Serial.print(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  secured_client.setTrustAnchors(&cert);

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.print("\nWiFi connected. IP address: ");
  Serial.println(WiFi.localIP());

  Serial.print("Retrieving time: ");
  configTime(0, 0, "pool.ntp.org");
  time_t now = time(nullptr);
  while (now < 24 * 3600) {
    Serial.print(".");
    delay(100);
    now = time(nullptr);
  }
  Serial.println(now);

  // Save System Begin

  LittleFS.begin();

  loadAccountsFromFile();

  // OTA Begin
  //ArduinoOTA.begin();
}

void loop() {

  // Telegram Loop

  if (millis() - bot_lasttime > BOT_MTBS) {
    int numNewMessages = bot.getUpdates(bot.last_message_received + 1);

    while (numNewMessages) {
      Serial.println("Message sending");
      handleNewMessages(numNewMessages);
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    }

    bot_lasttime = millis();
  }

  // Server Check Loop

  checkServers();

  // OTA Loop
  //ArduinoOTA.handle();
}

Kodların Açıklanması



Kütüphane ve Bağımlılıkların Dahil Edilmesi:
  • Kodun başında, gerekli kütüphaneler (ESP8266WiFi, WiFiClient, WiFiClientSecure, UniversalTelegramBot, LittleFS, ArduinoJson) ve önceki koddan geldiği gibi "urhoba.h" başlık dosyası dahil edilir.
  • Daha sonra WiFi ağ bilgileri (WIFI_SSID ve WIFI_PASSWORD) ve Telegram botunun token'ı (BOT_TOKEN) tanımlanır.

AccountManager ve Bot Nesnelerinin Oluşturulması:
  • AccountManager nesnesi, kullanıcı hesaplarını yönetmek için oluşturulur.
  • Telegram botu için gerekli nesneler oluşturulur (X509List, WiFiClientSecure, UniversalTelegramBot) ve bot token'ı ve güvenli bağlantı yapılandırılır.

Zaman Değişkenleri:
  • BOT_MTBS, Telegram botunun güncellemeleri kontrol etmek için belirli bir süre aralığını belirtir.
  • SERVER_CHECK_INTERVAL, sunucu durumlarının belirli aralıklarla kontrol edileceği süreyi belirtir.
  • bot_lasttime ve lastServerCheckTime, son zamanları saklamak için kullanılır.

Mesaj İşleme Fonksiyonları:
  • startMessage, /start komutuna karşılık gelen hoş geldin mesajını oluşturur ve kullanıcıya gönderir.
  • commandsMessage, /commands komutuna karşılık gelen mevcut komutları içeren bir mesaj oluşturur ve gönderir.
  • Diğer işlevler benzer şekillerde komutlara karşılık gelen mesajları oluşturur ve kullanıcıya gönderir.

Kullanıcı Hesap İşlemleri:
  • registerCommand, /register komutuna karşılık gelen bir mesajı yanıtlar ve kullanıcıyı kaydeder.
  • removeCommand, /remove komutuna karşılık gelen bir mesajı yanıtlar ve kullanıcının hesabını kaldırır.
  • addServerCommand, /add_server komutuna karşılık gelen bir mesajı yanıtlar ve kullanıcının hesabına yeni bir sunucu ekler.
  • removeServerCommand, /remove_server komutuna karşılık gelen bir mesajı yanıtlar ve kullanıcının hesabından bir sunucu kaldırır.
  • Diğer hesap işlemleri de benzer şekillerde gerçekleştirilir.

Sunucu Durumu Kontrolü:
  • checkServers fonksiyonu, belirli aralıklarla tüm kullanıcıların eklediği sunucuların durumunu kontrol eder. Çöken sunucuları bildirir.

Hesap Bilgilerinin Kaydedilmesi ve Yüklenmesi:
  • saveAccountsToFile, tüm kullanıcı hesaplarını ve bu hesaplara eklenmiş sunucu bilgilerini JSON formatında bir dosyaya kaydeder.
  • loadAccountsFromFile, kaydedilmiş hesap ve sunucu bilgilerini dosyadan yükler ve AccountManager nesnesine ekler.
  • setup() ve loop() Fonksiyonları:setup(), WiFi'ye bağlanmayı, zaman senkronizasyonunu, dosya sistemi başlatmayı ve hesap bilgilerini yüklemeyi içerir.
  • loop(), Telegram güncellemelerini alır, sunucu durumunu kontrol eder ve OTA (Over-The-Air) güncellemeleri işler.
Not: OTA projeye dahil edilmemiştir.

Proje kaynak kodlarına GitHub üzerinden erişebilirsiniz.