Часть 1

Почему?

Решил я попрактиковаться в написании приложений на шарпе с использованием EF. Пока разбирался заметил, что туториалов и примеров как написать бота на C# немного, поэтому решил поделиться своими наработками.

Что может бот:

Задача бота - устроить обмен сообщениями между главным и подчиненными так, чтобы главный(диспетчер) мог писать всем или только одному подчиненному(водителю), а подчиненные могли общаться только с главным. WebHook не нужен, т.к. бот сугубо для практики.

Модель БД

Итак, для начала решил описать все необходимые таблицы, для хранения информации. Однако по итогу модель немного изменилась.

Шаг 1:

Для хранения таблиц я использовал Postgre SQL 15, а для мониторинга PgAdmin 4. Создал бд Users и развернул локально.

скачать

Шаг 2:

Установим все необходимые пакеты в VS через NuGET:

  • Microsoft.EntityFrameworkCore.Tools

  • Newtonsoft.Json

  • Npgsql.EntityFrameworkCore.PostgreSQL

  • Telegram.Bot

  • Telegram.Bot.Extensions.Polling

Шаг 3:

Получаем токен https://telegram.me/BotFather и плавно переходим к коду.

Шаг 4:

Создадим пустой класс Program.cs и внесем в него все необходимые для работы Методы:

using Telegram.Bot;
using Telegram.Bot.Extensions.Polling;
using Telegram.Bot.Types.ReplyMarkups;
using CETmsgr.keyboards;
using CETmsgr.dbutils;
using Update = Telegram.Bot.Types.Update;
using Telegram.Bot.Types.Enums;

namespace CETmsgr
{
    class Program
    {
        static ITelegramBotClient bot = new TelegramBotClient("TOKEN");
        public static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
        {
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(update));
          if (update.Type == UpdateType.Message)
          {
          // Тут бот получает сообщения от пользователя
            // Дальше код отвечает за команду старт, которую можно добавить через botfather
            // Если все хорошо при запуске program.cs в консоль выведется, что бот запущен
            // а при отправке команды бот напишет Привет
            if (message.Text.ToLower() == "/start")
                {
                    await DataBaseMethods.ToggleInDialogStatus(update.Message.Chat.Id, 0);
                    await botClient.SendTextMessageAsync(
                        chatId: message.Chat,
                        text: "Привет");

                    return;
          }
          if (update.Type == UpdateType.CallbackQuery)
          {
          // Тут получает нажатия на inline кнопки
          }
          public static async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
        {
            // Данный Хендлер получает ошибки и выводит их в консоль в виде JSON
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(exception));
        }
        static void Main(string[] args)
        {

            Console.WriteLine("Запущен бот " + bot.GetMeAsync().Result.FirstName);
            var cts = new CancellationTokenSource();
            var cancellationToken = cts.Token;
            var receiverOptions = new ReceiverOptions
            {
                AllowedUpdates = { }, // разрешено получать все виды апдейтов
            };
            bot.StartReceiving(
                HandleUpdateAsync,
                HandleErrorAsync,
                receiverOptions,
                cancellationToken
            );
            Console.ReadLine();
            
        }
    }
}

Шаг 5:

Теперь приступим к созданию классов для таблиц, я создал отдельную папку dbutils для этих нужд.

UserRoles необходима для того, чтобы можно было сортировать id пользователей по ролям.

TrafficControllers и Drivers просто будут хранить информацию по юзерам.

DialogMembers будет хранить уникальные id диалогов и связывать их между id 2х юзеров

DialogMsgs эта таблица будет хранить сообщения, связанные с id диалога из DialogMembers

DialogStatus а вот эта таблица необходима для проверок при написании сообщений юзеров боту.

using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class UserRoles
    {
        [Key]
        public long TgId { get; set; }
        public string Role { get; set; }
        public string? TgUsername { get; set; }
        public long TgChatId { get; set; }
        public int StageReg { get; set; }

    }
}
using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class Drivers
    {
        [Key]
        public long IdDriver { get; set; }
        public string Name { get; set; }
        public string IdRoute { get; set; }
        public string VehichleRegNum { get; set; }
        public string DeviceSerialNum { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class TrafficControllers
    {
        [Key]
        public long IdTc { get; set; }
        public string Name { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class DialogMembers
    {
        [Key]
        public int IdThread { get; set; }
        public long IdTc { get; set; }
        public long IdDriver { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class DialogMsgs
    {
        [Key]
        public long IdUnique { get; set; }
        public int IdThread { get; set; }
        public string Created { get; set; }
        public string UrgentLevel { get; set; }
        public string Message { get; set; }
    }
}
using System.ComponentModel.DataAnnotations;

namespace CETmsgr.dbutils
{
    public partial class DialogStatus
    {
        [Key]
        public long TgId { get; set; }
        public int Status { get; set; }
        public long CurrentDialog { get; set; }

    }
}

Чтобы на основании этого добра появились таблицы в бд необходимо провести миграцию. Поэтому создаем модель данных для наших классов и задаем подключение к бд:

using Microsoft.EntityFrameworkCore;

namespace CETmsgr.dbutils
{
    public class ApplicationContext : DbContext
    {
        public DbSet<UserRoles> UserRoles { get; set; }
        public DbSet<Drivers> Drivers { get; set; }
        public DbSet<TrafficControllers> TrafficControllers { get; set; }
        public DbSet<DialogMsgs> DialogMsgs { get; set; }
        public DbSet<DialogMembers> DialogMembers { get; set; }
        public DbSet<DialogStatus> DialogStatus { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseNpgsql(
                "Host=localhost;" +
                "Port=5433;" +
                "Database=Users;" +
                "Username=postgres;" +
                "Password=zaqwsxzaq");
        }
    }
}
    

теперь выполняем миграцию:

для этого в консоль диспетчера пакетов вводим команды:

enable-migrations
Add-migration *Имя миграции*
update-database

теперь когда есть таблицы для хранения всех необходимых данных можем приступать к написанию логики.

Шаг 6:

Итак для работы с БД, нам необходимо написать методов для работы с ним. для этого в раннее созданную папку создаем файл DataBaseMethods.cs. Поскольку мой бот уже готов я выложу исходный файл, дабы не запутывать вас раздельным повествованием.

using Microsoft.EntityFrameworkCore;
using System.Data;

namespace CETmsgr.dbutils
{
    internal class DataBaseMethods
    {
        // метод для создания сообщения водителем. данные методы мне кажутся излишними, но я решил их оставить тк все работает
        public static async Task<long> MsgCreateByDriverToTc(int IdThread, string Msg, string Crtd, string UL)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                DialogMsgs newMSG = new DialogMsgs { IdThread = IdThread, Created = Crtd, UrgentLevel = UL, Message = Msg };
                await db.DialogMsgs.AddAsync(newMSG);
                await db.SaveChangesAsync();
                try
                {
                    var getMSG = await db.DialogMsgs.OrderBy(x => x.Created).LastOrDefaultAsync(x => x.IdThread == IdThread);
                    return getMSG.IdUnique;
                }
                catch(Exception ex)
                {
                    return 123123;
                }
                
            }
        }
        // метод для создания сообщения диспетчером
        public static async Task<long> MsgCreateByTcToDriver(int IdThread, string Msg, string Crtd, string UL)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                DialogMsgs newMSG = new DialogMsgs { IdThread = IdThread, Created = Crtd, UrgentLevel = UL, Message = Msg };
                await db.DialogMsgs.AddAsync(newMSG);
                await db.SaveChangesAsync();
                try
                {
                    var getMSG = await db.DialogMsgs.OrderBy(x => x.IdUnique).LastOrDefaultAsync(x => x.IdThread == IdThread);
                    return getMSG.IdUnique;
                }
                catch (Exception ex)
                {
                    return 123123;
                }

            }
        }
        // метод для получения сообщений диспетчером
        public static async Task<long> MsgRecievierTc(long IdUnique, int IdThread, long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var getMSG = await db.DialogMsgs.FirstOrDefaultAsync(x => x.IdUnique == IdUnique);
                var getId = await db.DialogMembers.FirstOrDefaultAsync(x => (x.IdThread == IdThread && x.IdDriver == IdDriver));
                if (getMSG.IdThread == getId.IdThread)
                {
                    return getId.IdTc;
                }
                else
                {
                    return 404;
                }
            }
        }
        // метод для получения сообщений водителем 
        public static async Task<long> MsgRecievierDriver(long IdUnique, int IdThread, long IdTc)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var getMSG = await db.DialogMsgs.FirstOrDefaultAsync(x => x.IdUnique == IdUnique);
                var getId = await db.DialogMembers.FirstOrDefaultAsync(x => (x.IdThread == IdThread && x.IdTc == IdTc));
                if (getMSG.IdThread == getId.IdThread)
                {
                    return getId.IdDriver;
                }
                else
                {
                    return 404;
                }
            }
        }
        // получение стаутса пользователя, чтобы бот мог разделять сообщения от пользователя по данному фильтру
        public static async Task<int> GetStatus(long TgId)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var status = await db.DialogStatus.FirstOrDefaultAsync(x => x.TgId == TgId);
                return status.Status;
            }
        }
        // получение id юзера для отправки сообщения через бота из другого юзера при условии нажатия кнопок в диалогах
        public static async Task<long> GetAddress(long TgId)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var status = await db.DialogStatus.FirstOrDefaultAsync(x => x.TgId == TgId);
                return status.CurrentDialog;
            }
        }
        // получение id диалога из бд
        public static async Task<int> GetThreadByDriver(long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var dialog = await db.DialogMembers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                return dialog.IdThread;
            }
        }
        // получение id диалога из бд
        public static async Task<int> GetThreadByTc(long IdTc, long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var dialog = await db.DialogMembers.FirstOrDefaultAsync(x => x.IdTc == IdTc && x.IdDriver == IdDriver);
                return dialog.IdThread;
            }
        }
        // получение роли юзера
        public static async Task<UserRoles> GetUserRole(long TgId)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var user = await db.UserRoles.FirstOrDefaultAsync(x => x.TgId == TgId);
                return user;
            }
        }
        // получение данных водителя
        public static async Task<Drivers> GetDriverData(long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var driver = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                return driver;
            }
        }
        // получение данных диспетчера
        public static async Task<TrafficControllers> GetTcData(long IdTc)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var tc = await db.TrafficControllers.FirstOrDefaultAsync(x => x.IdTc == IdTc);
                return tc;
            }
        }
        // обновление статуса юзера и id получателя сообщения
        public static async Task ToggleInDialogStatus(long TgId, int Status, long receivier)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var dialogStatus = await db.DialogStatus.FirstOrDefaultAsync(x => x.TgId == TgId );
                if (dialogStatus is null)
                {
                    DialogStatus StatusCreate = new DialogStatus { TgId = TgId, Status = Status, CurrentDialog = receivier};
                    var result = await db.DialogStatus.AddAsync(StatusCreate);
                    await db.SaveChangesAsync();
                }
                else
                {
                    dialogStatus.Status = Status;
                    dialogStatus.CurrentDialog = receivier;
                    db.DialogStatus.Update(dialogStatus);
                    await db.SaveChangesAsync();
                }
            }
        }
        // обновление статуса юзера
        public static async Task ToggleInDialogStatus(long TgId, int Status)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var dialogStatus = await db.DialogStatus.FirstOrDefaultAsync(x => x.TgId == TgId);
                if (dialogStatus is null)
                {
                    DialogStatus StatusCreate = new DialogStatus { TgId = TgId, Status = Status, CurrentDialog = 0 };
                    var result = await db.DialogStatus.AddAsync(StatusCreate);
                    await db.SaveChangesAsync();
                }
                else
                {
                    dialogStatus.Status = Status;
                    dialogStatus.CurrentDialog = 0;
                    db.DialogStatus.Update(dialogStatus);
                    await db.SaveChangesAsync();
                }
            }
        }
        // добавление или обновление юзера
        public static async Task AddOrUpdateUser(long tg_ID, string role, string tg_username, long tg_chat_id, int StageReg)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var user = await db.UserRoles.FirstOrDefaultAsync(x => x.TgId == tg_ID);
                if (user is null)
                {
                    if (tg_username == null)
                    {
                        tg_username = "Без ника";
                    }
                    UserRoles newuser = new UserRoles { TgId = tg_ID, Role = role, TgUsername = tg_username, TgChatId = tg_chat_id, StageReg = StageReg };
                    await db.UserRoles.AddAsync(newuser);
                    await db.SaveChangesAsync();
                }
                else
                {
                    user.Role = role;
                    user.TgUsername = tg_username;
                    db.UserRoles.Update(user);
                    await db.SaveChangesAsync();
                }
            }

        }
        // создание диалога
        public static async Task DialogCreate(long IdTc, long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var dialogWithDriver = await db.DialogMembers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver && x.IdTc == IdTc);
                if (dialogWithDriver is null)
                {
                    DialogMembers newDialog = new DialogMembers { IdDriver = IdDriver, IdTc = IdTc };
                    var result = await db.DialogMembers.AddAsync(newDialog);
                    await db.SaveChangesAsync();
                }
                else
                {

                }
            }
        }
        // изменение статуса пользователя
        public static async Task StageIncrement(long tg_ID, int StageReg)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var user = await db.UserRoles.FirstOrDefaultAsync(x => x.TgId == tg_ID);
                user.StageReg = StageReg;
                db.UserRoles.Update(user);
                await db.SaveChangesAsync();
            }
        }
        // добавление водителя до регистрации все строки по нулям, чтобы exception не словить
        public static async Task AddDriver(long IdDriver)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var user = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                if (user is null)
                {
                string Name = "0";
                string IdRoute = "0";
                string VehichleRegNum = "0";
                string DeviceSerialNum = "0";
                Drivers newDriver = new Drivers { IdDriver = IdDriver, Name = Name, IdRoute = IdRoute, VehichleRegNum = VehichleRegNum, DeviceSerialNum = DeviceSerialNum};
                var result = db.Drivers.AddAsync(newDriver);
                await db.SaveChangesAsync();
                }
                
            }
        }
        // добавление диспетчера
        public static async Task AddTc(long IdTc)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var user = await db.TrafficControllers.FirstOrDefaultAsync(x => x.IdTc == IdTc);
                if (user is null)
                {
                    string Name = "0";
                    TrafficControllers newTc = new TrafficControllers { IdTc = IdTc, Name = Name};
                    var result = await db.TrafficControllers.AddAsync(newTc);
                    await db.SaveChangesAsync();
                }

            }
        }
        // добавление данных в бд водителя
        public static async Task AddDataDriverName(long IdDriver, string Name)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var Driver = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                Driver.Name = Name;
                db.Drivers.Update(Driver);
                await db.SaveChangesAsync();
            }
        }
        public static async Task AddDataDriverIdRoute(long IdDriver, string IdRoute)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var Driver = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                Driver.IdRoute = IdRoute;
                db.Drivers.Update(Driver);
                await db.SaveChangesAsync();
            }
        }
        public static async Task AddDataDriverVehichleRegNum(long IdDriver, string VehichleRegNum)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var Driver = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                Driver.VehichleRegNum = VehichleRegNum;
                db.Drivers.Update(Driver);
                await db.SaveChangesAsync();
            }
        }
        public static async Task AddDataDriverDeviceSerialNum(long IdDriver, string DeviceSerialNum)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var Driver = await db.Drivers.FirstOrDefaultAsync(x => x.IdDriver == IdDriver);
                Driver.DeviceSerialNum = DeviceSerialNum;
                db.Drivers.Update(Driver);
                await db.SaveChangesAsync();
            }
        }
        // добавление данных в бд диспетчера
        public static async Task AddDataTcName(long IdDriver, string Name)
        {
            using (ApplicationContext db = new ApplicationContext())
            {
                var tc = await db.TrafficControllers.FirstOrDefaultAsync(x => x.IdTc == IdDriver);
                tc.Name = Name;
                db.TrafficControllers.Update(tc);
                await db.SaveChangesAsync();
            }
        }
        // вывод списка водителей
        public static List<long> GetAllDriversId(string role)
        {
            //Dictionary<long, string> driversId_Name = new Dictionary<long, string>();
            using (ApplicationContext db = new ApplicationContext())
            {
                var AllDrivers = db.UserRoles.Where(x => x.Role == role).ToList();
                List<long> driversIDs = new List<long>();
                foreach (var u in AllDrivers)
                    driversIDs.Add(u.TgId);
                return driversIDs;
            }

        }
    }
}

Финал:

Теперь можно рассмотреть логику реагирования бота на нажатия и сообщения. Поэтому опишем все необходимые кнопки в классе kb.

namespace CETmsgr.keyboards
{
    public static class kb
    {
        public static ReplyKeyboardMarkup Register = new(new[]
        {
            new KeyboardButton[] { "/reg" },
        })
        { ResizeKeyboard = true };
        public static InlineKeyboardMarkup Role = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Водитель", callbackData: "driver"),
                InlineKeyboardButton.WithCallbackData(text: "Диспетчер", callbackData: "controller"),
            },
        });
        public static InlineKeyboardMarkup Menu = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Диалоги", callbackData: "dialogs"),
                InlineKeyboardButton.WithCallbackData(text: "Профиль", callbackData: "profile"),
            },
            //new []
            //{
            //    InlineKeyboardButton.WithCallbackData(text: "Регистрация", callbackData: "register"),
            //},
        });
        public static InlineKeyboardMarkup ToMenu = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Меню", callbackData: "menu"),
            },
        }); 
        public static InlineKeyboardMarkup StartRegDriver = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Ваше ФИО", callbackData: "DriverName"),
            },
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Идентификатор маршрута", callbackData: "IdRoute"),
            },            
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Регистрационный номер тс", callbackData: "VehichleRegNum"),
            },          
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Серийный номер устройства", callbackData: "DeviceSerialNum"),
            },
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Окончить Регистрацию", callbackData: "FinReg"),
            },
        });
        public static InlineKeyboardMarkup StartRegTC = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Ваше ФИО", callbackData: "TcName"),
            },
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Окончить Регистрацию", callbackData: "FinReg"),
            },
        });
        public static InlineKeyboardMarkup MsgToDriver = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Водитель", callbackData: "driver"),
                InlineKeyboardButton.WithCallbackData(text: "Диспетчер", callbackData: "controller"),
            },
        });
        public static InlineKeyboardMarkup MsgDispetcher = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Диспетчер", callbackData: "callTC"),
            },
        });
        public static InlineKeyboardMarkup TextAll = new(new[]
        {
            new []
            {
                InlineKeyboardButton.WithCallbackData(text: "Написать всем", callbackData: "textall"),
            },
        });
    }
}

Вернемся в класс program

using Telegram.Bot;
using Telegram.Bot.Extensions.Polling;
using Telegram.Bot.Types.ReplyMarkups;
using CETmsgr.keyboards;
using CETmsgr.dbutils;
using Update = Telegram.Bot.Types.Update;
using Telegram.Bot.Types.Enums;

namespace CETmsgr
{


    class Program
    {
        static ITelegramBotClient bot = new TelegramBotClient("TOKEN");
        public static async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
        {
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(update));
            
            if (update.Type == UpdateType.Message)
            {
                string Time = DateTime.Now.ToShortTimeString();
                var message = update.Message;

                if (message.Text.ToLower() == "/reg")
                {
                    await DataBaseMethods.ToggleInDialogStatus(update.Message.Chat.Id, 0);
                    await botClient.SendTextMessageAsync(
                        message.Chat.Id,
                        "загрузка...",
                        replyMarkup: new ReplyKeyboardRemove());

                    var userData = await DataBaseMethods.GetUserRole(message.From.Id);
                    if (userData != null)
                    {
                        await botClient.SendTextMessageAsync(
                            message.Chat.Id,
                            text: "Вы уже зарегистрировались",
                            replyMarkup: kb.ToMenu);
                    }
                    else
                    {

                        await botClient.SendTextMessageAsync(
                            message.Chat.Id,
                            text: "Выберите свою должность",
                            replyMarkup: kb.Role);
                    }
                    return;
                }
                if (message.Text.ToLower() == "/start")
                {
                    await DataBaseMethods.ToggleInDialogStatus(update.Message.Chat.Id, 0);
                    await botClient.SendTextMessageAsync(
                        chatId: message.Chat,
                        text: "Главное меню",
                        replyMarkup: kb.Menu);

                    return;
                }
// это условие для того, чтобы команды не попадали туда куда не надо
                if (!message.Text.StartsWith("/"))
                {
                    int dialogStatus = await DataBaseMethods.GetStatus(message.Chat.Id);
                    var userDataReg = await DataBaseMethods.GetUserRole(message.Chat.Id);
                    string role = userDataReg.Role;
                    int countReg = userDataReg.StageReg;
// так выглядит регистрациЯ ДИСПЕТЧЕРА И ВОДИТЕЛЯ, собственно счетчики нужны для корректного отслеживания сообщений от пользователя                    
                    if (countReg == 1 && role == "driver" && dialogStatus == 0)
                    {
                        await DataBaseMethods.AddDataDriverName(message.From.Id, message.Text);
                    }
                    if (countReg == 1 && role == "controller" && dialogStatus == 0)
                    {
                        await DataBaseMethods.AddDataTcName(message.From.Id, message.Text);
                        await botClient.SendTextMessageAsync(
                            message.Chat.Id,
                            text: "Хотите вернуться в меню?",
                            replyMarkup: kb.ToMenu);
                    }
                    if (countReg == 2 && dialogStatus == 0)
                    {
                        await DataBaseMethods.AddDataDriverIdRoute(message.From.Id, message.Text);
                    }
                    if (countReg == 3 && dialogStatus == 0)
                    {
                        await DataBaseMethods.AddDataDriverVehichleRegNum(message.From.Id, message.Text);
                    }
                    if (countReg == 4 && dialogStatus == 0)
                    {
                        await DataBaseMethods.AddDataDriverDeviceSerialNum(message.From.Id, message.Text);
                        await botClient.SendTextMessageAsync(
                            message.Chat.Id,
                            text: "Хотите вернуться в меню?",
                            replyMarkup: kb.ToMenu);
                    }
// а вот пригодились статусы, чтобы оставаться в неком диалоге, внутри бота,
// пока юзер не вернется в меню 
                    if (role == "driver" && dialogStatus == 1)
                    {
                        var getDialog = await DataBaseMethods.GetThreadByDriver(
                            message.Chat.Id);
                        var msgID = await DataBaseMethods.MsgCreateByDriverToTc(
                            getDialog,
                            message.Text,
                            Time,
                            "3");
                        var reciever = await DataBaseMethods.MsgRecievierTc(msgID, getDialog, message.Chat.Id);
                        var msgFrom = await DataBaseMethods.GetDriverData(message.Chat.Id);
                        await botClient.SendTextMessageAsync(
                            reciever,
                            text: $"{msgFrom.Name}:" + "\n" +
                            $"Маршрут: {msgFrom.IdRoute}:" + "\n" +
                            $"{message.Text}");
                    }
                    if (role == "controller" && dialogStatus == 1)
                    {
                        var getAddress = await DataBaseMethods.GetAddress(message.Chat.Id);
                        var getDialog = await DataBaseMethods.GetThreadByTc(
                            message.Chat.Id,
                            IdDriver: getAddress);
                        var msgID = await DataBaseMethods.MsgCreateByTcToDriver(
                            getDialog,
                            message.Text,
                            Time,
                            "3");
                        var reciever = await DataBaseMethods.MsgRecievierDriver(msgID, getDialog, message.Chat.Id);
                        var msgFrom = await DataBaseMethods.GetTcData(message.Chat.Id);
                        await botClient.SendTextMessageAsync(
                            reciever,
                            text: $"{msgFrom.Name}:" + "\n" +
                            $"{message.Text}");
                    }
// это отдельная фича под рассылку всем В от Д в боте
                    if (role == "controller" && dialogStatus == 2)
                    {
                        var getAddress = DataBaseMethods.GetAllDriversId("driver");
                        foreach (var address in getAddress)
                        {
                            var getDialog = await DataBaseMethods.GetThreadByTc(
                                message.Chat.Id,
                                IdDriver: address);
                            var msgID = await DataBaseMethods.MsgCreateByTcToDriver(
                                getDialog,
                                message.Text,
                                Time,
                                "3");
                            var reciever = await DataBaseMethods.MsgRecievierDriver(msgID, getDialog, message.Chat.Id);
                            var msgFrom = await DataBaseMethods.GetTcData(message.Chat.Id);
                            await botClient.SendTextMessageAsync(
                                reciever,
                                text: $"{msgFrom.Name}:" + "\n" +
                                $"{message.Text}");
                        }
                    }
                    else
                        return;
                }
            }
            if (update.Type == UpdateType.CallbackQuery)
            {
// Тут идет обработка всех нажатий на кнопки, тут никаких особых доп условий не надо, тк у каждой кнопки своя ссылка
                var callbackQuery = update.CallbackQuery;
                var userRole = await DataBaseMethods.GetUserRole(callbackQuery.Message.Chat.Id);
                long userTgId;
                try
                {
                    userTgId = Convert.ToInt64(callbackQuery.Data);
                }
                catch
                {
                    userTgId = 0;
                }
                var checkUserCallback = await DataBaseMethods.GetUserRole(userTgId);
// тут единственнок место где условие чуть сложнее
// здесь по простому мы запоминаем ид пользвоателя в отд бд, откуда в дальнейем рлдгрузим данные
              if (checkUserCallback != null)
                {
                    if (callbackQuery.Data == checkUserCallback.TgId.ToString() != null && userRole.Role == "controller")
                    {
                        await DataBaseMethods.DialogCreate(userTgId, callbackQuery.Message.Chat.Id);
                        await DataBaseMethods.ToggleInDialogStatus(callbackQuery.Message.Chat.Id, 1, userTgId);
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: "Напишите сообщение водителю",
                            replyMarkup: kb.ToMenu);
                    }
                    if (callbackQuery.Data == checkUserCallback.TgId.ToString() && userRole.Role == "driver")
                    {
                        await DataBaseMethods.DialogCreate(userTgId, callbackQuery.Message.Chat.Id);
                        await DataBaseMethods.ToggleInDialogStatus(callbackQuery.Message.Chat.Id, 1, userTgId);
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: "Напишите сообщение диспетчеру",
                            replyMarkup: kb.ToMenu);
                    }
                }
                if (callbackQuery.Data == "menu")
                {
                    await DataBaseMethods.ToggleInDialogStatus(callbackQuery.Message.Chat.Id, 0);
                    await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Главное Меню",
                        replyMarkup: kb.Menu);
                }
                if (callbackQuery.Data == "register")
                {
                    await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            "/reg это способ пройти регистрацию");

                }
                if (callbackQuery.Data == "profile")
                {
                    await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                    var driverData = await DataBaseMethods.GetDriverData(callbackQuery.Message.Chat.Id);
                    var tcData = await DataBaseMethods.GetTcData(callbackQuery.Message.Chat.Id);
                    if (userRole != null && driverData != null)
                    {
                        var name = driverData.Name;
                        var route = driverData.IdRoute;
                        var vrn = driverData.VehichleRegNum;
                        var dsn = driverData.DeviceSerialNum;
                        var role = userRole.Role;
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: $"ваша должность: {role}");
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: $"Ваше ФИО: {name}" + "\n" +
                            $"Маршрут номер: {route}" + "\n" +
                            $"Номер тс: {vrn}" + "\n" +
                            $"Номер устройства: {dsn}");
                    }
                    if (userRole != null && tcData != null)
                    {
                        var name = tcData.Name;
                        var role = userRole.Role;
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: $"ваша должность: {role}");
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: $"Ваше ФИО: {name}");
                    }
                    else
                    {
                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            text: "для регистрации нажмите /reg",
                            replyMarkup: kb.Register);
                    }

                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Хотите вернуться в меню?",
                        replyMarkup: kb.ToMenu);
                }
                if (callbackQuery.Data == "dialogs")
                {
                    var role = userRole.Role;
                    if (role == "controller")
                    {
                        await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                        var driversList = DataBaseMethods.GetAllDriversId("driver");

                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            "Водители:",
                            replyMarkup: kb.TextAll);

                        foreach (var driver in driversList)
                        {
                            var driverName = await DataBaseMethods.GetDriverData(driver);
                            InlineKeyboardMarkup driverButton = new(new[]
                            {
                                    new []
                                    {
                                        InlineKeyboardButton.WithCallbackData(text: $"{driver}", callbackData: $"{driver}"),
                                    },
                                });
                            await botClient.SendTextMessageAsync(
                                callbackQuery.Message.Chat.Id,
                                $"<code>{driverName.Name}</code> ",
                                ParseMode.Html,
                                replyMarkup: driverButton);
                        }
                    }
                    else
                    {
                        await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                        var tcList = DataBaseMethods.GetAllDriversId("controller");

                        await botClient.SendTextMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            "Диспетчеры:");

                        foreach (var tc in tcList)
                        {
                            var tcName = await DataBaseMethods.GetTcData(tc);
                            InlineKeyboardMarkup tcButton = new(new[]
                            {
                                    new []
                                    {
                                        InlineKeyboardButton.WithCallbackData(text: $"{tc}", callbackData: $"{tc}"),
                                    },
                                });
                            await botClient.SendTextMessageAsync(
                                callbackQuery.Message.Chat.Id,
                                $"<code>{tcName.Name}</code> ",
                                ParseMode.Html,
                                replyMarkup: tcButton);
                        }
                    }
                }
                if (callbackQuery.Data == "textall")
                {
                    var allDrivers = DataBaseMethods.GetAllDriversId("driver");
                    foreach (long driver in allDrivers)
                    {
                        await DataBaseMethods.DialogCreate(callbackQuery.Message.Chat.Id, driver);
                        await DataBaseMethods.ToggleInDialogStatus(callbackQuery.Message.Chat.Id, 2, driver);
                    }
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Напишите сообщение для всех водителей",
                        replyMarkup: kb.ToMenu);
                }
                if (callbackQuery.Data == "driver")
                {
                    await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Нажмите на кнопку для ввода данных",
                        replyMarkup: kb.StartRegDriver);

                    int StageRegDriver = 1;
                    await DataBaseMethods.AddOrUpdateUser(
                        callbackQuery.Message.Chat.Id,
                        callbackQuery.Data.ToString(),
                        callbackQuery.From.Username,
                        callbackQuery.Message.From.Id,
                        StageRegDriver);

                    await DataBaseMethods.AddDriver(
                        callbackQuery.Message.Chat.Id);
                } // начало регистрации Водителя
                if (callbackQuery.Data == "DriverName")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 1);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Введите ФИО:");
                }
                if (callbackQuery.Data == "IdRoute")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 2);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Введите номер маршрута:");
                }
                if (callbackQuery.Data == "VehichleRegNum")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 3);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Введите номер тс:");
                }
                if (callbackQuery.Data == "DeviceSerialNum")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 4);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Введите номер устройства:");
                }
                if (callbackQuery.Data == "controller")
                {
                    await botClient.DeleteMessageAsync(
                            callbackQuery.Message.Chat.Id,
                            callbackQuery.Message.MessageId);

                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Нажмите на кнопку для ввода данных",
                        replyMarkup: kb.StartRegTC);

                    int StageRegTC = 1;
                    await DataBaseMethods.AddOrUpdateUser(
                        callbackQuery.Message.Chat.Id,
                        callbackQuery.Data.ToString(),
                        callbackQuery.From.Username,
                        callbackQuery.Message.From.Id,
                        StageRegTC);

                    await DataBaseMethods.AddTc(
                        callbackQuery.Message.Chat.Id);
                } // начало регистрации Диспетчера
                if (callbackQuery.Data == "TcName")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 1);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Введите ФИО:");
                }
                if (callbackQuery.Data == "FinReg")
                {
                    await DataBaseMethods.StageIncrement(callbackQuery.Message.Chat.Id, 5);
                    await botClient.SendTextMessageAsync(
                        callbackQuery.Message.Chat.Id,
                        text: "Регистрация окончена",
                        replyMarkup: kb.ToMenu);
                } // общее окончание Регистрации
            }
        }
        public static async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
        {
            // Некоторые действия
            Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(exception));
        }
        static void Main(string[] args)
        {

            Console.WriteLine("Запущен бот " + bot.GetMeAsync().Result.FirstName);
            var cts = new CancellationTokenSource();
            var cancellationToken = cts.Token;
            var receiverOptions = new ReceiverOptions
            {
                AllowedUpdates = { }, // receive all update types
            };
            bot.StartReceiving(
                HandleUpdateAsync,
                HandleErrorAsync,
                receiverOptions,
                cancellationToken
            );
            Console.ReadLine();
            
        }
    }
}

Все необходимые на мой взгляд комментарии я написал в коде, если будут вопросы или критика, буду рад.

Всем хорошего дня!