Cómo crear hilos (thread) en una aplicación de consola C# por código. Y cómo ejecutar un proceso repetidas veces usando un temporizador (también por código). Usaremos Visual Studio .Net Community 2019. A modo dec ejemplo, crearemos una aplicación completa que lee comandos de un chat de Telegram (Bot) y realiza operaciones en función del comando recibido. Además, ejecuta un hilo independiente, que se ejecuta cada 3 minutos, y envía un mensaje a un chat de Telegram, obteniendo la información del mensaje de una base de datos MySQL.
- Requisitos de la aplicación de ejemplo en C# para multi-hilo y repetición de proceso.
- Crear el proyecto C# de aplicación de consola en Visual Studio .Net Community 2019.
- Clase para escribir en fichero de log las acciones que se van realizando en la aplicación, también para mostrar en la ventana de consola.
- Clase para cifrar y descifrar texto.
- Clase para leer parámetros de configuración de un fichero XML.
- Clase para acceder a servidor de base de datos MySQL Server (válido para MariaDB).
- Clase para leer los comandos recibidos desde el Bot de Telegram.
- Programa principal Program Main.
- Ejecutando la aplicación de consola que abre varios hilos y lee y envía mensajes a Bot de Telegram.
- Descargar aplicación completa y código fuente en C#.
Requisitos de la aplicación de ejemplo en C# para multi-hilo y repetición de proceso
Como ejemplo, desarrollamos una aplicación de consola en C# que pretendemos que haga lo siguiente:
- Por un lado debe permanecer escuchando mensajes de un Bot de Telegram. Cuando recibe algún mensaje realiza una operación en función del mensaje (comando).
- Por otro lado, la misma aplicación, cada 3 minutos, queremos que ejecute otro proceso que lea en una base de datos MySQL envíe un mensaje a un chat de Telegram, con información obtenida del servidor de MySQL.
Como vemos, en una misma aplicación de consola queremos que se ejecuten dos procesos diferenciados, que no tienen nada que ver el uno con el otro. Para ello usaremos thread de C# y, queremos que uno de los procesos se ejecute cada 3 minutos, para ello usaremos un temporizador (Timer).
Realizaremos todo desde el código fuente C#, sin usar componentes visuales, en una aplicación de consola.
Para el desarrollo de la aplicación usaremos el IDE gratuito Visual Studio Community .Net 2019 y usaremos el lenguaje de programación C# (C Sharp).
La aplicación de ejemplo usará varias tablas de MySQL, que deben existir: usuario, incidencia y parámetros. A continuación se indican los campos mínimos de estas tablas MySQL, que se usan para hacer consultas SQL en la aplicación:
- La tabla usuario tendrá, al menos, los siguientes campos:
- idChatTelegram: de tipo long (entero largo), almacenará el ID del chat de Telegram al que se enviarían posibles mensajes.
- idTelegram: int (enterio), ID del usuario de Telegram.
- nombre: de tipo string (texto), nombre completo del usuario.
- email: de tipo string (texto), email del usuario.
- fecharegistro: de tipo datetime (fecha-hora), fecha y hora de registro del usuario.
- La tabla parametros tendrá, al menos, los campos:
- nombre: de tipo string (texto), nombre del parámetro.
- valor: de tipo string (texto), valor del parámetro.
- La tabla incidencia tendrá, al menos, los siguientes campos:
- resueltatecnico: de tipo string (texto), tendrá valor «S» si está resuelta la incidencia y «N» si está pendiente de resolver.
Crear el proyecto C# de aplicación de consola en Visual Studio .Net Community 2019
En primer lugar crearemos un proyecto de consola, de C#, en Visual Studio .Net Community:
Introduciremos el nombre para el proyecto, así como la carpeta donde se guardarán los ficheros, por ejemplo «ProyectoA_MultiHiloCSharp»:
No es objeto de este artículo explicar cómo enviar y recibir mensajes desde un chat/bot de Telegram. Para leer mensajes a un bot de Telegram seguiremos los pasos que indicamos en el siguiente artículo:
Tampoco es objeto de este artículo explicar cómo conectar desde una aplicación C# a una base de datos MySQL/MariaDB. Para leer de una base de datos MySQL desde C# seguiremos los pasos que indicamos en el siguiente artículo:
Necesitaremos agregar las referencias para la DLL de Telegram Telegram.Bot.dll, que debemos haber descargado y, en nuestro caso, la colocaremos en la carpeta bin de nuestro proyecto, donde tendremos el ejecutable. Para agregarla, pulsaremos en el menú «Proyecto» – «Agregar referencia…». En el Administrador de referencias, examinaremos y agregaremos la DLL Telegram.Bot.dll:
También agregaremos la referencia a la DLL de MySQL MySql.Data.dll, de la misma forma:
Otra referencia a DLL que necesitaremos es Newtonsoft.Json.dll:
Agregaremos otras referencias necesarias para este proyecto, como System.Configuration:
Todas las DLL anteriores se incluyen en la descarga de la aplicación de ejemplo y su código fuente.
Clase para escribir en fichero de log las acciones que se van realizando en la aplicación, también para mostrar en la ventana de consola
Crearemos una clase llamada EscribirLog en nuestro proyecto. Para ello pulsaremos con le botón derecho sobre la solución y en el menú emergente elegiremos «Agregar» – «Clase…»:
La llamamos EscribirLog:
Y le agregamos el siguiente código fuente C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
using System; using System.IO; namespace ProyectoA_MultiHiloCSharp { class EscribirLog { public string mensajeLog { get; set; } public Boolean mostrarConsola { get; set; } //Constructor si se pasa el mensaje por parámetro en la creación de la clase public EscribirLog(string mensajeEnviar, Boolean mostrarConsola) { mensajeLog = mensajeEnviar; if (mostrarConsola) monstrarMensajeConsola(); escribirLineaFichero(); } //Constructor si se pasa el mensaje por setter tras la creación de la clase public EscribirLog() { if (mostrarConsola) monstrarMensajeConsola(); escribirLineaFichero(); } public void monstrarMensajeConsola() { //Quitar posibles saltos de línea del mensaje if ((mensajeLog != null) && (mensajeLog != "")) { mensajeLog = mensajeLog.Replace(Environment.NewLine, " | "); mensajeLog = mensajeLog.Replace("\r\n", " | ").Replace("\n", " | ").Replace("\r", " | "); Console.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " " + mensajeLog); } } //Escribe el mensaje de la propiedad mensajeLog en un fichero en la carpeta del ejecutable public void escribirLineaFichero() { try { if ((mensajeLog != null) && (mensajeLog != "")) { FileStream fs = new FileStream(@AppDomain.CurrentDomain.BaseDirectory + "estado.log", FileMode.OpenOrCreate, FileAccess.Write); StreamWriter m_streamWriter = new StreamWriter(fs); m_streamWriter.BaseStream.Seek(0, SeekOrigin.End); //Quitar posibles saltos de línea del mensaje mensajeLog = mensajeLog.Replace(Environment.NewLine, " | "); mensajeLog = mensajeLog.Replace("\r\n", " | ").Replace("\n", " | ").Replace("\r", " | "); m_streamWriter.WriteLine(DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss") + " " + mensajeLog); m_streamWriter.Flush(); m_streamWriter.Close(); } } catch { //Silenciosa } } } } |
Esta clase escribirá el texto que se le pase como parámetro en un fichero de texto sin formato. También permitirá mostrar el texto por pantalla (en la ventana de consola de la aplicación).
Cuando realizamos una aplicación de consola o un servicio, es útil guardar lo que la aplicación va realizando, para posibles depuraciones posteriores y para recabar información de su funcionamiento.
Clase para cifrar y descifrar texto
Para guardar las contraseñas y demás datos sensibles en el fichero de configuración de la aplicación necesitaremos una clase que cifre/descifre un texto. La agregaremos al proyecto y la llamaremos CifrarDescifrarTexto. Dicha clase tendrá el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace ProyectoA_MultiHiloCSharp { class CifrarDescifrarTexto { public string cifrarTextoAES(string textoCifrar, string palabraPaso, string valorRGBSalt, string algoritmoEncriptacionHASH, int iteraciones, string vectorInicial, int tamanoClave) { try { byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(vectorInicial); byte[] saltValueBytes = Encoding.ASCII.GetBytes(valorRGBSalt); byte[] plainTextBytes = Encoding.UTF8.GetBytes(textoCifrar); PasswordDeriveBytes password = new PasswordDeriveBytes(palabraPaso, saltValueBytes, algoritmoEncriptacionHASH, iteraciones); byte[] keyBytes = password.GetBytes(tamanoClave / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, InitialVectorBytes); MemoryStream memoryStream = new MemoryStream(); CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); cryptoStream.FlushFinalBlock(); byte[] cipherTextBytes = memoryStream.ToArray(); memoryStream.Close(); cryptoStream.Close(); string textoCifradoFinal = Convert.ToBase64String(cipherTextBytes); return textoCifradoFinal; } catch { return null; } } public string descifrarTextoAES(string textoCifrado, string palabraPaso, string valorRGBSalt, string algoritmoEncriptacionHASH, int iteraciones, string vectorInicial, int tamanoClave) { try { byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(vectorInicial); byte[] saltValueBytes = Encoding.ASCII.GetBytes(valorRGBSalt); byte[] cipherTextBytes = Convert.FromBase64String(textoCifrado); PasswordDeriveBytes password = new PasswordDeriveBytes(palabraPaso, saltValueBytes, algoritmoEncriptacionHASH, iteraciones); byte[] keyBytes = password.GetBytes(tamanoClave / 8); RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.Mode = CipherMode.CBC; ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, InitialVectorBytes); MemoryStream memoryStream = new MemoryStream(cipherTextBytes); CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); byte[] plainTextBytes = new byte[cipherTextBytes.Length]; int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length); memoryStream.Close(); cryptoStream.Close(); string textoDescifradoFinal = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount); return textoDescifradoFinal; } catch { return null; } } } } |
Clase para leer parámetros de configuración de un fichero XML
Necesitaremos una clase para leer los parámetros de configuración de la aplicación. Dichos parámetros se guardarán en el fichero XML de configuración. Algunos de los parámetros que se guardarán serán el nombre (IP) del servidor de MySQL/MariaDB al que nos conectaremos y sus datos de conexión: puerto, usuario y contraseña. La contraseña irá cifrada empleando la clase anterior, de forma que si alguien accede al fichero de configuración de la aplicación, aunque vea sus datos, no podrá saber la contraseña de conexión con el servidor.
Agregaremos la clase para leer/escribir parámetros de configuración, que llamaremos LeerGuardarDatosConfiguracion y le añadiremos el siguiente código fuente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
using System; using System.Configuration; namespace ProyectoA_MultiHiloCSharp { class LeerGuardarDatosConfiguracion { public LeerGuardarDatosConfiguracion() { } public LeerGuardarDatosConfiguracion(string clave) { leerValorConfiguracion(clave); } //Constructor si se pasan los parámetros clave y valor //escribe en el fichero de configuración el valor de esa clave public LeerGuardarDatosConfiguracion(string clave, string valor) { //guardarValorConfiguracion(clave, valor); } //Lee un valor de una clave en el fichero de configuración XML de la aplicación public string leerValorConfiguracion(string clave) { try { string resultado = ConfigurationManager.AppSettings[clave].ToString(); return resultado; } catch (Exception error) { new EscribirLog("Error al leer valor de configuración: " + error.GetType().ToString() + " " + error.Message, false); return ""; } } //Guarda un valor en una clave en el fichero de configuración XML de la aplicación public void guardarValorConfiguracion(string clave, string valor) { try { Configuration ficheroConfXML = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); //Configuration ficheroConfXML = // ConfigurationManager.OpenExeConfiguration(AppDomain.CurrentDomain.BaseDirectory); //Eliminamos la clave actual (si existe), de lo contrario los valores //se irán acumulando separados por coma ficheroConfXML.AppSettings.Settings.Remove(clave); //Asignamos el valor en la clave indicada ficheroConfXML.AppSettings.Settings.Add(clave, valor); //Guardamos los cambios definitivamente en el fichero de configuración ficheroConfXML.Save(ConfigurationSaveMode.Modified); } catch (Exception error) { new EscribirLog("Error al guardar valor de configuración: " + error.GetType().ToString() + " " + error.Message, false); } } } } |
Clase para acceder a servidor de base de datos MySQL Server (válido para MariaDB)
Para trabajar con el servidor de MySQL/MariaDB y poder acceder a las tablas de este servidor, crearemos una clase en nuestro proyecto, como hemos hecho para la clase anterior, llamándola BD (BD.cs). Esta clase tendrá el siguiente código C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
using MySql.Data.MySqlClient; using System; namespace ProyectoA_MultiHiloCSharp { class BD { private MySqlConnection conexionBD; //Datos de conexión a BD MySQL MariaDB public string servidor { get; set; } public string puerto { get; set; } public string bd { get; set; } public string usuario { get; set; } public string contrasena { get; set; } //Datos de usuario de la BD de la tabla usuario public string mail { get; set; } public DateTime fechaRegistro { get; set; } public string nombre { get; set; } //Constructor, se le pasan los datos del servidor //MySQL MariaDB por parámetro en la creación de la clase public BD(string servidor, string puerto, string bd, string usuario, string contrasena) { this.servidor = servidor; this.puerto = puerto; this.bd = bd; this.usuario = usuario; this.contrasena = contrasena; } //Constructor si no se pasan los datos del servidor de BD, leer de fichero de configuración XML public BD() { //Leemos los datos que faltan (servidor, bd, usuario, contraseña) del fichero de configuración LeerGuardarDatosConfiguracion fConf = new LeerGuardarDatosConfiguracion(); this.servidor = fConf.leerValorConfiguracion("BD - Servidor"); this.puerto = fConf.leerValorConfiguracion("BD - Puerto"); this.bd = fConf.leerValorConfiguracion("BD - BD"); this.usuario = fConf.leerValorConfiguracion("BD - Usuario"); CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); this.contrasena = cTexto.descifrarTextoAES(fConf.leerValorConfiguracion("BD - Contraseña"), "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128); //Para depuración // new EscribirLog(this.servidor + " || " + this.puerto + " || " + // this.bd + " || " + this.usuario + " || " + this.contrasena, true); conectarBD(); } //Obtiene los datos de conexión con el servidor de //MySQL MariaDB de los atributos (ya cargados en el constructor) //Y conecta con dicho servidor public void conectarBD() { if (conexionBD != null) conexionBD.Close(); string connStr = String.Format("server={0};port={1};user id={2}; password={3}; " + "database={4}; pooling=true;" + "Allow Zero Datetime=False;Convert Zero Datetime=True", this.servidor, this.puerto, this.usuario, this.contrasena, this.bd); try { conexionBD = new MySqlConnection(connStr); //conexionBD.Open(); new EscribirLog("Conectado a servidor BD MySQL/MariaDB " + this.servidor + " [" + this.bd + "]", true); } catch (MySqlException ex) { new EscribirLog("Error al conectar al servidor de base de datos MySQL " + this.servidor + " [" + this.bd + "] " + ex.Message, true); } finally { conexionBD.Close(); } } //Ejemplo de ejecución de SQL select //Comprueba si existe un usuario en la tabla usuario de MySQL, a partir del campo id_telegram public bool existeIDTelegram(int idTelegram) { string sqlEjecutar = "Select codigo from usuario where id_telegram = @id_telegram"; try { conexionBD.Close(); MySqlCommand runSQL = new MySqlCommand(sqlEjecutar, conexionBD); runSQL.Parameters.Add("@id_telegram", MySqlDbType.Int32).Value = idTelegram; conexionBD.Open(); MySqlDataReader datosSQL = runSQL.ExecuteReader(); //Si devuelve un registro if (datosSQL.Read()) { return true; } else { return false; } } catch (MySqlException ex) { new EscribirLog("Error al comprobar si existe ID usuario de" + " Telegram en BD: " + ex.Message, true); return false; } finally { conexionBD.Close(); } } //Ejemplo de ejecución de SQL select //Obtiene datos de un usuario de la base de datos a partir del ID de Telegram public bool obtenerInfoUsuario(int idTelegram) { if (existeIDTelegram(idTelegram)) { //Obtenemos datos del usuario: mail, fecha alta, nombre de la tabla usuario de MySQL string sqlEjecutar = "select email, fechaa, nombre" + " from usuario where id_telegram = @id_telegram"; try { conexionBD.Close(); MySqlCommand runSQL = new MySqlCommand(sqlEjecutar, conexionBD); runSQL.Parameters.Add("@id_telegram", MySqlDbType.Int32).Value = idTelegram; conexionBD.Open(); MySqlDataReader datosSQL = runSQL.ExecuteReader(); //Si devuelve un registro if (datosSQL.Read()) { if (datosSQL["email"] != DBNull.Value) this.mail = datosSQL["email"].ToString(); else this.mail = ""; if (datosSQL["nombre"] != DBNull.Value) this.nombre = datosSQL["nombre"].ToString(); else this.nombre = ""; if (datosSQL["fechaa"] != DBNull.Value) this.fechaRegistro = Convert.ToDateTime(datosSQL["fechaa"]); else this.fechaRegistro = DateTime.Now; return true; } else { new EscribirLog("No se ha encontrado el usuario con " + "ID de Telegram [" + idTelegram.ToString() + "] en la BD de MySQL.", true); return false; } } catch (MySqlException ex) { new EscribirLog("Error obtener información del usuario de MySQL: " + ex.Message, true); return false; } finally { conexionBD.Close(); } } else { new EscribirLog("No existe el IDTelegram [" + idTelegram + "] en MySQL", true); return false; } } //Ejemplo de ejecución de SQL select //Obtener número de tareas pendientes de resolver public int obtenerTareasPendientes() { int numTareas = 0; //Select de SQL que obtendrá el número de tareas pendientes de resolver //La tabla de tareas/incidencias se llama "incidencia" y la de usuarios "usuario" string sqlEjecutar = "select count(*) NumTareas from incidencia t" + " where t.resueltatecnico is null or t.resueltatecnico = 'N'"; try { conexionBD.Close(); MySqlCommand runSQL = new MySqlCommand(sqlEjecutar, conexionBD); conexionBD.Open(); MySqlDataReader datosSQL = runSQL.ExecuteReader(); while (datosSQL.Read()) { //Comprobamos los posibles nulos de cada campo de la consulta SQL if (!datosSQL.IsDBNull(datosSQL.GetOrdinal("NumTareas"))) numTareas = Convert.ToInt32(datosSQL["NumTareas"].ToString()); else numTareas = 0; } return numTareas; } catch (MySqlException ex) { new EscribirLog("Error obtener tareas pendientes de resolver: " + ex.Message, true); return numTareas; } finally { conexionBD.Close(); } } //Obtiene el ID del chat de Telegram al que se enviarán los mensajes desde //la tabla "parametros" de la aplicación, campo a filtrar "nombre" = id_chat_telegram //Se coge el ID del campo "valor" public long obtenerIDChatTelegramEnvioMensajes() { long idChat = -1; //Select de SQL que obtendrá el valor del campo "valor" de la tabla "parametros" de MySQL //Filtrando por "nombre"="id_chat_telegram" string sqlEjecutar = "select p.valor from parametros p" + " where p.nombre='id_chat_telegram'"; try { conexionBD.Close(); MySqlCommand runSQL = new MySqlCommand(sqlEjecutar, conexionBD); conexionBD.Open(); MySqlDataReader datosSQL = runSQL.ExecuteReader(); while (datosSQL.Read()) { //Comprobamos los posibles nulos de cada campo de la consulta SQL if (!datosSQL.IsDBNull(datosSQL.GetOrdinal("valor"))) idChat = Convert.ToInt64(datosSQL["valor"].ToString()); else idChat = -1; } return idChat; } catch (MySqlException ex) { new EscribirLog("Error obtener ID del Chat de la tabla parámetros: " + ex.Message, true); return idChat; } finally { conexionBD.Close(); } } //Ejemplo para insergar registro en tabla MySQL/MariaDB desde C# //Inserta un usuario en la tabla usuario public bool insertarUsuario(long idChatTelegram, int idTelegram, string nombre, string email) { string sqlEjecutar = "insert into usuario (idChatTelegram, " + "idTelegram, nombre, email, fecharegistro) " + "values (@idChatTelegram, @idTelegram, @nombre, @email, @fecha_registro)"; try { MySqlCommand comandoSQL = new MySqlCommand(); conexionBD.Close(); comandoSQL.Connection = conexionBD; comandoSQL.CommandText = sqlEjecutar; comandoSQL.Parameters.Add("@idTelegram", MySqlDbType.Int64).Value = idChatTelegram; comandoSQL.Parameters.Add("@idTelegram", MySqlDbType.Int32).Value = idTelegram; comandoSQL.Parameters.Add("@nombre", MySqlDbType.VarChar).Value = nombre; comandoSQL.Parameters.Add("@email", MySqlDbType.VarChar).Value = email; comandoSQL.Parameters.Add("@fecha_registro", MySqlDbType.Timestamp).Value = DateTime.Now; conexionBD.Open(); comandoSQL.ExecuteNonQuery(); return true; } catch (MySqlException ex) { new EscribirLog("Error al insertar registro de alta " + "de usuario en tabla usuario de MySQL: " + ex.Message, true); return false; } finally { conexionBD.Close(); } } } } |
Clase para leer los comandos recibidos desde el Bot de Telegram
Agregaremos una clase a nuestro proyecto que contendrá los posibles comandos que la aplicación entenderá, para realizar la operación asignada al comando leído. Estableceremos el nombre de la clase, en nuestro caso Comandos.
La clase Comandos (Comandos.cs) tendrá el siguiente código fuente C#:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
using System; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using Telegram.Bot.Args; using Telegram.Bot.Types; using Telegram.Bot.Types.InlineQueryResults; namespace ProyectoA_MultiHiloCSharp { class Comandos { //Comandos del BOT de Telegram que entenderá el programa public string[] hola = new string[] { "HOLA", "AYUDA", "HELP" }; public string[] infoUsuario = new string[] { "USUARIO", "USER", "USU", "INFO" }; public string[] encriptar = new string[] { "ENCRIPTAR", "CIFRAR" }; public string[] desencriptar = new string[] { "DESENCRIPTAR", "DESCIFRAR" }; //Para quitar el comando del texto original public int posPunto; public Comandos() { } public async Task IniciarEscuchaMensajes() { int offset = 0; string mensajeOriginal; while (true) { try { //Preparamos la aplicación para escuchar mensajes del chat del Bot de Telegram System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12; var me = Program.miBot.GetMeAsync().Result; Update[] updates = Program.miBot.GetUpdatesAsync(offset).Result; //Creamos los eventos, que estarán en Program.cs, que se ejecutarán cuando se reciban //determinados elementos del chat del Bot de Telegram Program.miBot.OnCallbackQuery += BotOnCallbackQueryReceived; Program.miBot.OnInlineQuery += BotOnInlineQueryReceived; Program.miBot.OnInlineResultChosen += BotOnChosenInlineResultReceived; Program.miBot.OnReceiveError += BotOnReceiveError; //new EscribirLog("Escuchando mensajes...", true); foreach (var update in updates) { offset = update.Id + 1; if (update.Message == null) continue; //Comprobamos si se recibe algo que no sea texto desde el chat del Bot de Telegram if (update.Message.Type != Telegram.Bot.Types.Enums.MessageType.Text) { new EscribirLog("Se ha enviado un documento: " + @Path.Combine(Directory.GetCurrentDirectory(), "descargados", update.Message.Document.FileName), true); /* ***SIRVE*** PARA DESCARGAR FICHERO new EscribirLog("Descargando...", true); //Lo descargamos para probar descarga de documentos enviados a a bot var file = await Program.miBot.GetFileAsync(update.Message.Document.FileId); FileStream fs = new FileStream(@Path.Combine(Directory.GetCurrentDirectory(), "descargados", update.Message.Document.FileName), FileMode.Create); await Program.miBot.DownloadFileAsync(file.FilePath, fs); fs.Close(); fs.Dispose(); new EscribirLog("Descargado", true); */ //De momento no hacer nada si se recibe un fichero continue; } //Se obtiene el mensaje original proveniente del chat del Bot de Telegram mensajeOriginal = update.Message.Text; //Lo mostramos en la consola de la aplicación para depuración new EscribirLog("Mensaje recibido en el chat " + update.Message.Chat.Id.ToString() + " de " + update.Message.Chat.Username + " sin formatear: " + mensajeOriginal, true); //Separamos el comando del parámetro (dato) que pueda incluir, por ejemplo: //CIFRAR TEXTO, el comando será CIFRAR y el parámetro será TEXTO string parametro = ""; string accion = ObtenerAccion(mensajeOriginal, ref parametro); //Obtenemos información del mensaje recibido (el ID del //usuario de Telegram que lo envía, el ID del chat, ...) int idTelegram = update.Message.From.Id; long idChat = update.Message.Chat.Id; string nickTelegram = update.Message.From.Username; string nombreTelegram = update.Message.From.FirstName; string apellidosTelegram = update.Message.From.LastName; //Instanciamos la clase BD para usar sus métodos de acceso a MYSQL BD conBD = new BD(); switch (accion) { //Mostrar ayuda, respuesta a comando "HOLA" en mensaje de Telegram case "hola": await Program.EnviarMensaje(idChat, "Hola ¿qué tal " + update.Message.From.Username + "?" + Environment.NewLine + Environment.NewLine + "No respondas mucho que soy poco hablador \U0001F64A" + Environment.NewLine + Environment.NewLine + "Estos son los comandos que entiendo: " + Environment.NewLine + "\U000025AB Ayuda/Hola" + Environment.NewLine + "\U000025AB Usuario" + Environment.NewLine + "\U000025AB Cifrar [Texto]" + Environment.NewLine + "\U000025AB Descifrar [Texto]" + Environment.NewLine + Environment.NewLine + "Escríbeme un comando y trataré de ayudarte \U0001F44D", Telegram.Bot.Types.Enums.ParseMode.Default, false); break; //Obtener información de un usuario de la BD MySQL y enviar a Telegram case "infousuario": if (conBD.obtenerInfoUsuario(idTelegram)) { await Program.EnviarMensaje(idChat, nombreTelegram + ", estos son los datos de tu cuenta de usuario en la BD:" + Environment.NewLine + "\U000025AB Nombre: " + conBD.nombre + Environment.NewLine + "\U000025AB Email: " + conBD.mail + Environment.NewLine + "\U000025AB Registro: " + conBD.fechaRegistro.ToShortDateString().ToString(), Telegram.Bot.Types.Enums.ParseMode.Html, false); } break; case "encriptar": //Si tenemos el texto a encriptar lo encriptamos y lo enviamos por Telegram if (parametro != "") { CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); string textoCifrado = cTexto.cifrarTextoAES(parametro, "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128); await Program.EnviarMensaje(idChat, textoCifrado, Telegram.Bot.Types.Enums.ParseMode.Default, false); } else { await Program.EnviarMensaje(idChat, nombreTelegram + ", no me has dicho el *texto* para cifrar. " + "Prueba otra vez escribiendo el comando completo \U0001F641", Telegram.Bot.Types.Enums.ParseMode.Markdown, false); } break; case "desencriptar": //Si tenemos el texto a desencriptar lo desencriptamos y lo enviamos por Telegram if (parametro != "") { CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); string textoDescifrado = cTexto.descifrarTextoAES(parametro, "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128); await Program.EnviarMensaje(idChat, textoDescifrado, Telegram.Bot.Types.Enums.ParseMode.Default, false); } else { await Program.EnviarMensaje(idChat, nombreTelegram + ", no me has dicho el *texto* para descifrar. " + "Prueba otra vez escribiendo el comando completo \U0001F641", Telegram.Bot.Types.Enums.ParseMode.Markdown, false); } break; //Ejemplo de enviar audio case "xxxx": await Program.EnviarMensaje(idChat, "Mensaje", Telegram.Bot.Types.Enums.ParseMode.Default, false); await Program.miBot.SendChatActionAsync(idChat, Telegram.Bot.Types.Enums.ChatAction.UploadAudio); string fichero = @Path.Combine(Directory.GetCurrentDirectory(), "recursos", "ordenador.ogg"); using (var fileStream = new FileStream(fichero, FileMode.Open, FileAccess.Read, FileShare.Read)) { await Program.miBot.SendAudioAsync(idChat, fileStream, ""); } break; } } } catch (TaskCanceledException error) { new EscribirLog("Proceso cancelado: " + error.GetType().ToString() + " " + error.Message, false); } catch (AggregateException e) { foreach (var ex in e.InnerExceptions) { Console.WriteLine(ex.Message); } } catch (Exception error) { new EscribirLog("Error al enviar/recibir mensajes al bot: " + error.GetType().ToString() + " " + error.Message, false); } } } public async void BotOnInlineQueryReceived(object sender, InlineQueryEventArgs inlineQueryEventArgs) { Console.WriteLine($"Received inline query from: " + $"{inlineQueryEventArgs.InlineQuery.From.Id}"); InlineQueryResultBase[] results = { new InlineQueryResultLocation( id: "1", latitude: 40.7058316f, longitude: -74.2581888f, title: "New York") // displayed result { InputMessageContent = new InputLocationMessageContent( latitude: 40.7058316f, longitude: -74.2581888f) // message if result is selected }, new InlineQueryResultLocation( id: "2", latitude: 13.1449577f, longitude: 52.507629f, title: "Berlin") // displayed result { InputMessageContent = new InputLocationMessageContent( latitude: 13.1449577f, longitude: 52.507629f) // message if result is selected } }; await Program.miBot.AnswerInlineQueryAsync( inlineQueryEventArgs.InlineQuery.Id, results, isPersonal: true, cacheTime: 0); } public async void BotOnCallbackQueryReceived(object sender, CallbackQueryEventArgs callbackQueryEventArgs) { var callbackQuery = callbackQueryEventArgs.CallbackQuery; await Program.miBot.AnswerCallbackQueryAsync( callbackQuery.Id, $"Received {callbackQuery.Data}"); await Program.EnviarMensaje( callbackQuery.Message.Chat.Id, $"Received {callbackQuery.Data}", Telegram.Bot.Types.Enums.ParseMode.Default, false); } public void BotOnChosenInlineResultReceived(object sender, ChosenInlineResultEventArgs chosenInlineResultEventArgs) { new EscribirLog($"Recibido resultado inline botón:" + $" {chosenInlineResultEventArgs.ChosenInlineResult.ResultId}", false); } public void BotOnReceiveError(object sender, ReceiveErrorEventArgs receiveErrorEventArgs) { new EscribirLog($"Error recibido: {0} - {1}", false); } //El mensaje puede ser un comando único, por ejemplo: hola //o bien un comando con el texto adicional (parámetro), por ejemplo: CIFRAR TEXTO //La función devolverá un string que será el comando, y por referencia el posible parámetro public string ObtenerAccion(string mensaje, ref string parametro) { //Separamos el comando del posible texto adicional (parámetro) //El comando será siempre la primera palabra sin espacios //Quitamos alguna posible coma u otro carácter var comandoF = Regex.Match(mensaje, @"^([\w\-]+)"); string comando = comandoF.Value; //Obtenemos el parámetro (texto a partir del espacio) int posEspacio = mensaje.IndexOf(" ", 0); if (posEspacio != -1) parametro = mensaje.Substring(posEspacio + 1, mensaje.Length - posEspacio - 1); else parametro = ""; //Quitamos del comando cualquier carácter especial string mensajeFormateado = FormatearTexto(comando); comando = ""; if (Array.Exists(hola, E => E == mensajeFormateado)) { comando = "hola"; } if (Array.Exists(infoUsuario, E => E == mensajeFormateado)) { comando = "infousuario"; } if (Array.Exists(encriptar, E => E == mensajeFormateado)) { comando = "encriptar"; } if (Array.Exists(desencriptar, E => E == mensajeFormateado)) { comando = "desencriptar"; } if (comando == "") comando = "desconocido"; return comando; } //Formatea el texto quitando tildes, espacios, / public string FormatearTexto(string texto) { string textoFormateado; //Quitamos las tildes textoFormateado = QuitarTildesTexto(texto); //Pasamos a mayúsculas textoFormateado = textoFormateado.ToUpper(); //Quitamos la / si la incluye el mensaje al principio //(si tenemos setprivacy del Bot de Telegram a ENABLED) if (textoFormateado.Substring(0, 1) == "/") { textoFormateado = textoFormateado.Substring(1, textoFormateado.Length - 1); } //Quitamos ¿?¡! textoFormateado = textoFormateado.Replace("?", ""); textoFormateado = textoFormateado.Replace("¿", ""); textoFormateado = textoFormateado.Replace("¡", ""); textoFormateado = textoFormateado.Replace("!", ""); //Quitamos los espacios textoFormateado = textoFormateado.Replace(" ", ""); return textoFormateado; } //Obtiene la primera palabra antes de un signo de puntuación //Por si pudiera ser un comando public string ObtenerComandoMensaje(string mensaje) { mensaje = mensaje.Replace(",", "."); mensaje = mensaje.Replace(",,", "."); mensaje = mensaje.Replace(":", "."); mensaje = mensaje.Replace(";", "."); mensaje = mensaje.Replace("..", "."); mensaje = mensaje.Replace(",,", "."); mensaje = mensaje.Replace("-", "."); mensaje = mensaje.Replace("_", "."); mensaje = mensaje.Replace("'", "."); mensaje = mensaje.Replace("#", "."); mensaje = mensaje.Replace("/", "."); mensaje = mensaje.Replace("\\", "."); mensaje = mensaje.Replace("&", "."); this.posPunto = mensaje.IndexOf("."); if (this.posPunto != -1) { string principioMensaje = mensaje.Substring(0, this.posPunto); return principioMensaje; } else return ""; } //Quita las tildes/acentos de un texto public string QuitarTildesTexto(string texto) { texto = texto.ToLower(); Regex a = new Regex("[á|à|ä|â]", RegexOptions.Compiled); Regex e = new Regex("[é|è|ë|ê]", RegexOptions.Compiled); Regex i = new Regex("[í|ì|ï|î]", RegexOptions.Compiled); Regex o = new Regex("[ó|ò|ö|ô]", RegexOptions.Compiled); Regex u = new Regex("[ú|ù|ü|û]", RegexOptions.Compiled); //Regex n = new Regex("[ñ|Ñ]", RegexOptions.Compiled); texto = a.Replace(texto, "a"); texto = e.Replace(texto, "e"); texto = i.Replace(texto, "i"); texto = o.Replace(texto, "o"); texto = u.Replace(texto, "u"); //texto = n.Replace(texto, "n"); return texto; } } } |
Programa principal Program Main
Una vez que hemos agregado todas las clases y referencias necesarias, como hemos indicando anteriormetne, agregaremos el código fuente al programa principal (Program), a la clase Main, que será la primera que se ejecute cuando se abra la aplicación de consola.
El el programa principal de nuestra aplicación, en el fichero Program.cs, tendrá el siguiente código, para que la aplicación de consola permanezca siempre abierta, escuchando mensajes de un bot de Telemgram y para que, también, cada 3 minutos lea en una base de datos MySQL y realice otras operaciones:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Timers; using Telegram.Bot; using Telegram.Bot.Types; namespace ProyectoA_MultiHiloCSharp { class Program { public static TelegramBotClient miBot; //Conexión con Bot de Telegram para enviar/recibir mensajes static Thread syncThread = null; //Para crear hilos de ejecución (thread, procesos separados e independientes) static System.Timers.Timer temporizador; //Temporizador para que un proceso se ejecute repetidamente //Instanciamos la clase Comandos para la escucha de mensajes del Bot de Telegram //Y para ejecutar el comando que corresponda al mensaje recibido public static Comandos miTelegram = new Comandos(); static void Main(string[] args) { //Guardamos valores de configuración en INI //De forma manual en tiempo de diseño GuardarValoresConfiguracion(); //Por un lado inciciamos el temporizador que se ejecutará en un nuevo hilo //Se ejecutará cada 3 minutos new EscribirLog("Iniciando hilo (thread) de temporizador de 3 minutos...", true); temporizador = new System.Timers.Timer(); temporizador.Interval = 180000; //3 minutos temporizador.Enabled = true; //Método/Evento que se ejecutará cuando transcurran los 3 minutos temporizador.Elapsed += new ElapsedEventHandler(temporizadorElapsed); temporizador.Start(); //Por otro lado, accederemos al Bot de Telegram para quedar a la //escucha de los mensajes que se envíen a su chat new EscribirLog("Obteniendo acceso al Bot ProyectoA...", true); //ID del Bot y token de seguridad //Cambiar ID y token por los del Bot al que queramos conectar miBot = new TelegramBotClient("2079:AUAjsvuBBKnBkMv"); new EscribirLog("Acceso al Bot concedido, ID del Bot ProyectoA: " + miBot.BotId.ToString(), true); //Iniciamos la escucha de mensajes del Bot de Telegram //Se creará un hilo (Theread) nuevo, independiente del anterior EscucharMensajes().Wait(); } //Dejar aplicación de consola a la escucha de mensajes nuevos en el chat del Bot de Telegram static async Task EscucharMensajes() { new EscribirLog("Iniciando escucha de mensajes...", true); await miTelegram.IniciarEscuchaMensajes(); } //Cuando se cumple el tiempo establecido para el //temporizador (Interval), se ejecuta este evento protected static void temporizadorElapsed(object sender, ElapsedEventArgs e) { //Se inicia un hilo para comprobar las tareas del usuario pendientes de resolver syncThread = new Thread(new ThreadStart(inicioHiloTemporizador)); syncThread.Start(); } //Hilo que se ejecuta cada intervalo establecido en el temporizador //El hilo ejecutará un método de la clase BD (que se instancia) y //envía un mensaje por Telegram a un ID de chat especificado protected static void inicioHiloTemporizador() { new EscribirLog("Iniciando hilo...", true); //Realizamos la operación que queramos que se ejecute cada 3 minutos //Para el ejemplo, el hilo accederá a MySQL y filtrará el número de tareas pendientes //de realizar de los técnicos BD conBD = new BD(); int numTareas = conBD.obtenerTareasPendientes(); //En este ejemplo, enviamos un mensaje a un chat de Telegram //Que obtenemos del campo "valor" de la tabla //"parametros" de MySQL cuyo "nombre" = "id_chat_telegram" long idChatTelegramEnvio = conBD.obtenerIDChatTelegramEnvioMensajes(); if (idChatTelegramEnvio > -1) { new EscribirLog("Enviando mensaje a ID de chat " + idChatTelegramEnvio.ToString() + " con número de tareas: " + numTareas.ToString(), true); EnviarMensaje(idChatTelegramEnvio, "Temporizador activado, nº tareas pendientes: " + numTareas.ToString(), Telegram.Bot.Types.Enums.ParseMode.Default, false); } else { new EscribirLog("No ha establecido el parámetro \"id_chat_telegram\" " + "en la tabla parametros", true); } } //Enviar mensaje a chat de Telegram public static async Task EnviarMensaje(ChatId idChat, string mensaje, Telegram.Bot.Types.Enums.ParseMode tipo, bool vistaPreviaPagina) { /* //Si es de tipo HTML formatear el texto (Telegram solo admite <b></b>, <i></i>, <a href=...></a>,<code></code>,<pre></pre> //El resto de etiquetas hay que suprimirlas para que no dé error: //Telegram.Bot.Exceptions.ApiRequestException Bad Request: can't parse entities: Unsupported start tag "x" at byte offset x if (tipo == Telegram.Bot.Types.Enums.ParseMode.Html) { mensaje = formatearHTMLTelegram(mensaje); } */ //Si el texto del mensaje supera los 4096 caracteres enviar //en varios mensajes separados, es el límite de Telegram //De lo contrario daría error: Telegram.Bot.Exceptions.ApiRequestException //Bad Request: message is too long if (mensaje != null) { if (mensaje.Length <= 4096) { await miBot.SendTextMessageAsync(idChat, mensaje, tipo, !vistaPreviaPagina); } else { char[] arrayCaracter; arrayCaracter = mensaje.ToCharArray(0, mensaje.Length); int recorridos = 0; int recorridosTotal = 0; string mensajeCortado = ""; foreach (char caracter in arrayCaracter) { mensajeCortado = mensajeCortado + caracter; if ((recorridos == 4095) || recorridosTotal == mensaje.Length - 1) { await miBot.SendTextMessageAsync(idChat, mensajeCortado, tipo, !vistaPreviaPagina); recorridos = 0; mensajeCortado = ""; } else { recorridos++; } recorridosTotal++; } } } } //Se usa para quitar los tag HTML que Telegram no soporta en mensajes de tipo HTML public static string formatearHTMLTelegram(string texto) { //Para que no elimine el texto contenido entre p b i //Quitamos posibles <p></p> texto = texto.Replace("<p style=\"text-align: center; \">", ""); texto = texto.Replace("<p style=\"text-align: right; \">", ""); texto = texto.Replace("<p style=\"text-align: left; \">", ""); texto = texto.Replace("<p>", ""); texto = texto.Replace("</p>", ""); //Quitamos posibles <b></b> texto = texto.Replace("<b>", ""); texto = texto.Replace("</b>", ""); //Quitamos posibles <i></i> texto = texto.Replace("<i>", ""); texto = texto.Replace("</i>", ""); texto = texto.Replace("[caption]", ""); texto = texto.Replace("[/caption]", ""); texto = texto.Replace("[box]", ""); texto = texto.Replace("[/box]", ""); texto = texto.Replace(" ", ""); texto = texto.Replace("[box type=\"download\"]", ""); texto = Regex.Replace(texto, "<.*?>", string.Empty); return texto; } //Método para guardar los valores de configuración en el XML en tiempo de diseño //Sirve para guardar, por ejemplo, la contraseña de acceso a MySQL cifrada correctamente public static void GuardarValoresConfiguracion() { new LeerGuardarDatosConfiguracion("BD - Servidor", ""); new LeerGuardarDatosConfiguracion("BD - Puerto", ""); new LeerGuardarDatosConfiguracion("BD - BD", ""); new LeerGuardarDatosConfiguracion("BD - Usuario", ""); //Instanciamos la clase CifrarDescrifrarTexto para cifrar //la contraseña antes de guardarla en el XML CifrarDescifrarTexto cTexto = new CifrarDescifrarTexto(); new LeerGuardarDatosConfiguracion("BD - Contraseña", cTexto.cifrarTextoAES("contraseña", "Encriptad0", "SalT", "MD5", 22, "1234567891234567", 128)); } } } |
Ejecutando la aplicación de consola que abre varios hilos y lee y envía mensajes a Bot de Telegram
En primer lugar generaremos el ejecutable de la aplicación. Desde Visual Studio .net, pulsaremos en el menú «Compilar» – «Volver a generar ProyectoA_MultiHiloCSharp»:
Nos generará un ejecutable en la carpeta …/bin de nuestro proyecto. Antes de ejecutar la aplicación, deberemos editar el fichero de configuración ProyectoA_MultiHiloCSharp.exe.config y establecer los parámetros de conexión a la base de datos MySQL/MariaDB:
Una vez establecidos los parámetros, únicamente tendremos que hacer doble clic sobre el ejecutable para abrir la aplicación:
La aplicación conectará con el API de Telegram, identificará el Bot con su ID y su token de seguridad y permanecerá a la escucha de mensajes. Si se escribe un mensaje al bot de Telegram, la aplicación lo leerá. Y si el mensaje recibido coincide con algún comando de los que conoce, realizará la operación asignada. Los posibles comandos:
- cifrar texto: cifrará el texto indicado «texto» y enviará un mensaje con el texto cifrado.
- descifrar texto: realiza la operación inversa a la anterior.
- usuario: accede a la base de datos MySQL y obtiene el email, el nombre y la fecha de registro del usuario. Lo filtra por el ID de Telegram (que deberá existir en la tabla de usuario).
- hola: devuelve un mensaje con los comandos que reconoce el programa para el bot de Telegram.
Además de reconocer comandos, la aplicación ejecuta un proceso, cada 3 minutos, que lee de la base de datos de MySQL las incidencias pendientes de resolver de un técnico (usuario), filtrando por su ID de Telegram y envía un mensaje al chat del bot de Telegram con el número de incidencias.
La aplicación de consola irá mostrando las acciones que va ejecutando, para depurar:
La aplicación de Telegram, una vez ejecutados algunos comandos y una vez recibido algún mensaje con las incidencias pendientes de un técnico, tendrá este aspecto:
Descargar aplicación completa y código fuente en C#
A continuación dejamos el enlace a la descarga de la aplicación de ejemplo completa con el código fuente y todas las DLL necesarias: