elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


Tema destacado: Únete al Grupo Steam elhacker.NET


Páginas: 1 2 3 4 5 6 [7] 8 9 10
 61 
 en: 6 Junio 2026, 00:48 am 
Iniciado por El_Andaluz - Último mensaje por El_Andaluz


El presidente ruso, Vladímir Putin, agradeció este viernes a su homólogo estadounidense, Donald Trump, por "educar" al presidente ucraniano, Volodímir Zelenski, por sus modales y vestirse como Rambo.

"Todos vimos cómo Donald (Trump) ante los ojos de todo el mundo educó al autor de la carta (de Zelenski dirigida a Putin) y le llamó la atención por su código de vestimenta", señaló Putin en su comparecencia en el Foro Económico Internacional de San Petersburgo.

A lo que añadió que "proyectar constantemente Rambo: First Blood puede ser apropiado en algunos lugares, pero por su puesto no en todos". "En cuanto a los buenos modales, quisiera agradecer a Donald su labor. Sin duda es útil, pero aún queda mucho por hacer. Debemos continuar", subrayó en referencia al acalorado primer encuentro entre los líderes de EEUU y Ucrania en marzo del año pasado, donde Trump regañó a Zelenski por no acudir en traje, sino vestido de estética militar.

El mandatario ruso destacó también que sus relaciones con Trump "se basan en el respeto mutuo". A pesar de que Trump se halla alejado de ser intermediario para las negociaciones por una paz entre Rusia y Ucrania, Putin siguió alabando su personalidad y repitió algunas de las tesis del presidente de EEUU, como el supuesto fraude electoral que llevó a la derrota de Trump en 2020 y que si hubiera seguido al frente del país, no habría iniciado la guerra de Ucrania dos años después.

Sin embargo, el ministro de Exteriores ruso, Serguéi Lavrov, expresó otra opinión el jueves en el marco del mismo foro. El jefe de la diplomacia rusa defendió que la "guerra de (Joe) Biden se ha convertido en la guerra de Trump" en respuesta a las declaraciones del secretario de Estado de Estados Unidos, Marco Rubio, quien aseguró que su país es mediador en el conflicto, pero no es neutral y apoya a Ucrania.





 62 
 en: 5 Junio 2026, 22:16 pm 
Iniciado por Tachikomaia - Último mensaje por Tachikomaia
Ejemplos como este:

hay tantos que me han dejado así como chocado. Espec¡almente los pisos, ves uno detallado y otro y otro y te preguntas ¿pero por qué están tan bien hechos tantos? O al menos con baldosas bien marcadas, con colores al azar o siguiendo un patrón. Con las paredes y ventanas suelen hacer lo mismo.

Los personajes en general y muchos de los hechos me parecen aburridos e incluso extremadamnente estúpidos (como que una le levanta la pollera a una amiga a ver qué panties tiene, diría que ni siquiera es gracioso 1 vez, imaginate que se repita), lo bueno es:
- La música de los openings.
- Los openings (de hecho empecé a ver uno por el opening). Aquí 2 ejemplos:
https://www.youtube.com/watch?v=l4hlxvPh6ZQ
https://www.youtube.com/watch?v=Ie3natE3-DE
- El mundo, algunas de sus mecánicas, el misterio de qué pasará con ciertos personajes.

Si a alguien le interesa, creo que mejor lea un resumen, porque para colmo los 2 anime son como la misma historia con protagonistas distintos, al ver una te falta el contexto del otro protagonista (especialmente en Toaru Majutsu no Index, ahí hacen que Railgun parezca una tonta), al punto de que el orden recomendado de verlos es un lío.

Citar
Los suelos y calles en Toaru Majutsu no Index y A Certain Scientific Railgun son tan detallados porque están basados fielmente en localizaciones reales de la ciudad de Tachikawa, en Tokio.
El equipo de animación de J.C.Staff realiza un trabajo de documentación fotográfica milimétrico para replicar los pavimentos, baldosas, rejillas de ventilación y pasos de peatones del mundo real y trasladarlos al anime.
Aquí tienes los motivos principales de este nivel de detalle:
## Realismo Urbano

* Localización real: Ciudad de la Academia se construyó usando Tachikawa como modelo directo.
* Texturas fieles: Los adoquines entrelazados, las líneas de asfalto y el desgaste del suelo imitan la infraestructura pública japonesa.
* Inmersión: El contraste entre la tecnología de ciencia ficción y un suelo urbano hiperrealista hace que el mundo se sienta vivo y creíble.

## Identidad Visual de J.C.Staff

* Planos contrapicados: Las series usan muchos ángulos de cámara bajos durante las conversaciones y peleas.
* Fondos trabajados: Al enfocar desde el suelo, el pavimento se convierte en el lienzo principal de la escena.
* Consistencia: Mantener el diseño del suelo idéntico al de Tachikawa ayuda a los animadores a mantener la perspectiva geométrica correcta en los fondos.

## Turismo de Anime (Seichijunrei)

* Promoción local: La ciudad de Tachikawa colabora activamente con la franquicia.
* Fidelidad: Los fans viajan a la ciudad para hacer "peregrinación" y fotografiar los mismos adoquines y calles que pisan Mikoto Misaka o Touma Kamijo.

¿Te llama la atención el diseño de alguna zona o calle en específico de la serie? Si quieres, puedo contarte más sobre qué lugares reales de Tachikawa inspiraron los escenarios más famosos del anime.

Citar
La franquicia de Toaru es masiva y abarca más de una década de producción. La animación de estas series se dividió en varias etapas a lo largo de los años y, para lograr ese nivel de detalle urbano, combinaron software estándar de la industria con técnicas de mapeo avanzadas. [1, 2]
------------------------------
## 📅 Los Años de Producción
El estudio J.C.Staff ha estado produciendo ambas ramas de la franquicia en bloques intermitentes: [1, 2]

* A Certain Magical Index (Toaru Majutsu no Index)
* Temporada 1: 2008 – 2009
   * Temporada 2: 2010 – 2011
   * Película (Endymion no Kiseki): 2013
   * Temporada 3: 2018 – 2019 [2, 3]
* A Certain Scientific Railgun (Toaru Kagaku no Railgun)
* Temporada 1: 2009 – 2010
   * Temporada 2 (Railgun S): 2013
   * Temporada 3 (Railgun T): 2020 [3, 4]

------------------------------
## 💻 ¿Usaron algún software especial?
No usaron un programa "secreto", sino una combinación técnica muy inteligente de diseño digital asistido por software de arquitectura y pintura digital tradicional:
## 1. Photoshop y Corel Painter (Pintura de Fondos)
La gran mayoría de los adoquines detallados que ves en pantalla están pintados por directores de arte digital. Usan Adobe Photoshop y Corel Painter. Para acelerar el proceso de las baldosas de Tachikawa, los artistas crean "pinceles de textura personalizados" (custom texture brushes). En lugar de dibujar cada baldosa una por una, deslizan un pincel digital que ya tiene el patrón exacto del suelo real.
## 2. Modelado 3D para la Perspectiva (Autodesk 3ds Max / Maya)
Cuando las calles tienen un ángulo muy complejo o hay persecuciones y peleas (como los ataques eléctricos de Mikoto Misaka), dibujar el suelo en perspectiva a mano es una pesadilla geométrica.

* J.C.Staff modela la calle completa con software 3D como Autodesk 3ds Max.
* Crean un bloque de pavimento gris en 3D para calcular cómo se deforma la perspectiva con la cámara en movimiento.
* Luego, exportan esa guía geométrica y los artistas de fondos pintan encima en 2D. El resultado es un suelo perfectamente alineado y real, pero con apariencia de dibujo animado.

## 3. Proyección de Texturas Fotográficas (Camera Mapping)
En las temporadas más recientes (como Index III o Railgun T), el estudio perfeccionó una técnica en software de composición (como Adobe After Effects o Foundry Nuke). Toman las fotos reales que sacaron en la ciudad de Tachikawa, limpian los elementos del mundo real (como marcas de autos o basura), y "proyectan" esa fotografía texturizada sobre los planos del suelo en 3D. Esto hace que las texturas de las calles sean literalmente idénticas a las reales.
------------------------------
Si te interesa el apartado técnico del anime, ¿te gustaría saber cómo hacen los efectos de los poderes eléctricos de Misaka o cómo integran los vehículos futuristas en 3D con los fondos dibujados a mano?

Por cierto:

+

=

 :laugh:

 63 
 en: 5 Junio 2026, 21:15 pm 
Iniciado por El_Andaluz - Último mensaje por crazykenny
Eso sí, lo hará sin leer el contenido de las conversaciones ni enviar los mensajes a sus servidores, algo clave para mantener la privacidad.



Ya, y, bueno, estoy seguro que usaran IA para realizar dicha funcion y que dichas IA, al no leer el contenido, sufriran mas IA-lucinaciones que aumentaran el riesgo de falsos positivos o negativos.

Resulta "encantador" si lo miramos seriamente.  Sarcasmo off. :¬¬

Muchas gracias por vuestra atencion, y, bueno, saludos.

 64 
 en: 5 Junio 2026, 09:25 am 
Iniciado por Tachikomaia - Último mensaje por leprosyuncut

Es posible que las cajas parezcan muy pequeñas, pero podrás comprobar en un emulador que son muy similares a las que el juego arcade original debe emplear. De todos modos

 65 
 en: 4 Junio 2026, 23:14 pm 
Iniciado por El_Andaluz - Último mensaje por El_Andaluz


WhatsApp está trabajando en una nueva función para avisar al usuario cuando reciba un mensaje sospechoso de una persona desconocida. La herramienta se llama Scam Alert, algo que podemos traducir como "Alerta de estafas", y su objetivo es detectar posibles estafas antes de que el usuario caiga en ellas.

La plataforma mostrará una advertencia dentro del propio chat cuando detecte señales de fraude. Eso sí, lo hará sin leer el contenido de las conversaciones ni enviar los mensajes a sus servidores, algo clave para mantener la privacidad.



 66 
 en: 4 Junio 2026, 23:11 pm 
Iniciado por El_Andaluz - Último mensaje por El_Andaluz


Android va a incorporar una nueva protección pensada para frenar las estafas en las que, mediante una llamada, alguien suplanta el número de un contacto y utiliza inteligencia artificial para imitar su voz. Es decir, el móvil puede mostrar que te llama tu madre, tu jefe o un amigo, pero en realidad se trata de un estafador intentando colarte una urgencia falsa para pedirte dinero o datos personales.

La novedad se llama 'Fake call detection', que podemos traducir como 'Detector de llamadas fraudulentas', y funcionará de forma automática en los móviles compatibles. Google la plantea como una verificación silenciosa entre dispositivos, una especie de “apretón de manos digital” que permite comprobar si la llamada proviene realmente del teléfono de esa persona o si alguien está falsificando su número.



 67 
 en: 4 Junio 2026, 23:04 pm 
Iniciado por El_Andaluz - Último mensaje por El_Andaluz


El Mundial de Fútbol 2026 que se celebrará en México, Estados Unidos y Canadá afronta la cuenta atrás, con el partido inaugural entre México y Sudáfrica a la vuelta de la esquina el próximo 11 de junio. Para los aficionados al balompié el campeonato es una cita especial de la que solo pueden disfrutar una vez cada cuatro años y es por ello que garantizar la seguridad de aficionados y participantes se ha convertido en una prioridad.

En ese contexto, Meta, empresa matriz de Facebook, Instagram, WhatsApp, Messenger y Threads, ha presentado un amplio paquete de medidas para combatir las estafas relacionadas con la venta de entradas, dificultar la actividad de ciberdelincuentes y reforzar la protección de jugadores y figuras públicas frente al acoso en línea durante el torneo.

La iniciativa amplía las herramientas de seguridad que la compañía ya utiliza en sus plataformas y busca anticiparse al aumento de amenazas digitales que suelen acompañar a los grandes eventos deportivos. Porque nadie quiere que una mala experiencia a través de internet estropee la que está llamada a ser la cita principal en el ámbito deportivo del verano.



 68 
 en: 4 Junio 2026, 21:19 pm 
Iniciado por Eleкtro - Último mensaje por Eleкtro
Comparto aquí el nuevo método ListBucketKeysAsync, que no cabe en el post de aquí arriba por el condenado límite de caracteres que tiene el foro...

Código
  1. Public NotInheritable Class UtilKvdb
  2.  
  3.    ''' <summary>
  4.    ''' Asynchronously retrieves a list of keys (and optionally their values) from the specified bucket storage
  5.    ''' on the remote <c>kvdb.io</c> service.
  6.    ''' </summary>
  7.    '''
  8.    ''' <param name="bucketId">
  9.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  10.    ''' <para></para>
  11.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  12.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  13.    ''' </param>
  14.    '''
  15.    ''' <param name="secretKey">
  16.    ''' Optional. The secret key required to authorize the request if the bucket is protected.
  17.    ''' <para></para>
  18.    ''' Default value is null.
  19.    ''' </param>
  20.    '''
  21.    ''' <param name="prefix">
  22.    ''' Optional. Only return keys matching the given prefix.
  23.    ''' <para></para>
  24.    ''' Default value is null.
  25.    ''' </param>
  26.    '''
  27.    ''' <param name="includeValues">
  28.    ''' Optional. If set to <see langword="True"/>, returns values in addition to keys.
  29.    ''' <para></para>
  30.    ''' Default value is <see langword="False"/>.
  31.    ''' </param>
  32.    '''
  33.    ''' <param name="limit">
  34.    ''' Optional. Maximum number of keys to return.
  35.    ''' <para></para>
  36.    ''' Default value is 10000.
  37.    ''' </param>
  38.    '''
  39.    ''' <param name="skip">
  40.    ''' Optional. Number of keys to skip before returning the first result.
  41.    ''' <para></para>
  42.    ''' Default value is zero (no keys are skipped).
  43.    ''' </param>
  44.    '''
  45.    ''' <param name="reverse">
  46.    ''' Optional. If set to <see langword="True"/>, reverses the lexicographic sort order.
  47.    ''' <para></para>
  48.    ''' Default value is <see langword="False"/>.
  49.    ''' </param>
  50.    '''
  51.    ''' <param name="outputFormat">
  52.    ''' Optional. The preferred output format.
  53.    ''' <para></para>
  54.    ''' Default value is <see cref="KvdbOutputFormat.Text"/>.
  55.    ''' </param>
  56.    '''
  57.    ''' <returns>
  58.    ''' A <see cref="Task(Of String)"/> representing the asynchronous operation, containing the raw server response.
  59.    ''' </returns>
  60.    <DebuggerStepThrough>
  61.    Public Shared Async Function ListBucketKeysAsync(bucketId As String,
  62.                                            Optional secretKey As String = Nothing,
  63.                                            Optional prefix As String = Nothing,
  64.                                            Optional includeValues As Boolean = False,
  65.                                            Optional limit As Integer = 10000,
  66.                                            Optional skip As Integer = 0,
  67.                                            Optional reverse As Boolean = False,
  68.                                            Optional outputFormat As KvdbOutputFormat = KvdbOutputFormat.Text) As Task(Of String)
  69.  
  70. #If NETCOREAPP Then
  71.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  72. #Else
  73.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  74. #End If
  75.        If limit <= 0 Then
  76.            Throw New ArgumentOutOfRangeException(paramName:=NameOf(limit),
  77.                "The maximum number of keys to return must be greather than zero.")
  78.        End If
  79.  
  80.        If skip < 0 Then
  81.            Throw New ArgumentOutOfRangeException(paramName:=NameOf(skip),
  82.                "The number of keys to skip cannot be smaller than zero.")
  83.        End If
  84.  
  85.        If outputFormat <> KvdbOutputFormat.Text AndAlso
  86.           outputFormat <> KvdbOutputFormat.Json AndAlso
  87.           outputFormat <> KvdbOutputFormat.Jsonl Then
  88.  
  89.            Throw New InvalidEnumArgumentException(NameOf(outputFormat), outputFormat, GetType(KvdbOutputFormat))
  90.        End If
  91.  
  92.        Dim queryParams As New List(Of String)(capacity:=6) From {
  93.            $"format={outputFormat.ToString().ToLowerInvariant()}",
  94.            $"limit={limit}",
  95.            $"skip={skip}",
  96.            $"values={includeValues.ToString().ToLowerInvariant()}",
  97.            $"reverse={reverse.ToString().ToLowerInvariant()}"
  98.        }
  99.  
  100.        If Not String.IsNullOrWhiteSpace(prefix) Then
  101.            queryParams.Add($"prefix={Uri.EscapeDataString(prefix)}")
  102.        End If
  103.  
  104.        Dim queryString As String = String.Join("&", queryParams)
  105.        Dim relativeUrl As String = $"{bucketId}/?{queryString}"
  106.  
  107.        Dim policy As New KvdbBucketPolicy With {
  108.            .SecretKey = secretKey
  109.        }
  110.  
  111.        Using client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  112.              response As HttpResponseMessage = Await client.GetAsync(relativeUrl).
  113.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  114.            If response.IsSuccessStatusCode Then
  115.                Dim rawResult As String = Await response.Content.ReadAsStringAsync().
  116.                                                                 ConfigureAwait(continueOnCapturedContext:=False)
  117.                Return rawResult
  118.            Else
  119.                Throw New HttpRequestException(
  120.                    $"Failed to list bucket keys from the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  121.            End If
  122.        End Using
  123.    End Function
  124.  
  125. End Class

Enum KvdbOutputFormat:
Código
  1. ''' <summary>
  2. ''' Specifies the data serialization formats supported by the <c>kvdb.io</c> API for listing keys and values.
  3. ''' </summary>
  4. Public Enum KvdbOutputFormat
  5.  
  6.    ' DO NOT CHANGE VALUE NAMES (Text, Json, JSonl).
  7.    ' They are converted to string for writing the list query.
  8.  
  9.    ''' <summary>
  10.    ''' Plain text representation.
  11.    ''' <para></para>
  12.    ''' Returns newline-delimited (<c>\n</c>) keys,
  13.    ''' or <c>key=value</c> pairs if values are requested.
  14.    ''' </summary>
  15.    Text
  16.  
  17.    ''' <summary>
  18.    ''' Standard JSON format.
  19.    ''' <para></para>
  20.    ''' Returns a valid JSON array of strings <c>["key"]</c>,
  21.    ''' or an array of arrays <c>[["key", value]]</c> if values are requested.
  22.    ''' </summary>
  23.    Json
  24.  
  25.    ''' <summary>
  26.    ''' JSON Lines.
  27.    ''' <para></para>
  28.    ''' Returns newline-delimited JSON, a format easy to process with
  29.    ''' command-line and other tools expecting one JSON object per line.
  30.    ''' </summary>
  31.    Jsonl
  32.  
  33. End Enum
  34.  



La clase BucketPolicy para definir las políticas de acceso a los buckets en el código que he compartido un post más arriba. ☝️

EDITADO CON LIGERAS MICRO-OPTIMIZACIONES, E IMPLEMENTACIÓN DE EQUIDAD.

Código
  1. ''' <summary>
  2. ''' Represents the access policy for a bucket storage on the <c>kvdb.io</c> service.
  3. ''' </summary>
  4. Public Class KvdbBucketPolicy : Implements IEquatable(Of KvdbBucketPolicy)
  5.  
  6. #Region " Properties "
  7.  
  8.    ''' <summary>
  9.    ''' Gets or sets the key used to manage policy and other keys in a bucket storage.
  10.    ''' </summary>
  11.    '''
  12.    ''' <exception cref="ArgumentException">
  13.    ''' </exception>
  14.    <Browsable(True)>
  15.    <DisplayName("Secret Key")>
  16.    <Description("The key used to manage policy and other keys in a bucket storage.")>
  17.    <Category("Master Key")>
  18.    Public Overridable Property SecretKey As String
  19.        Get
  20.            Return Me.secretKey_
  21.        End Get
  22.        Set(value As String)
  23.            UtilKvdb.ValidateTokenSize(value, NameOf(KvdbBucketPolicy.SecretKey))
  24.            Me.secretKey_ = value
  25.        End Set
  26.    End Property
  27.    ''' <summary>
  28.    ''' Backing field for <see cref="KvdbBucketPolicy.SecretKey"/>.
  29.    ''' <para></para>
  30.    ''' The key used to manage policy and other keys in a bucket storage.
  31.    ''' </summary>
  32.    Protected secretKey_ As String
  33.  
  34.    ''' <summary>
  35.    ''' Gets or sets the key used to authorize read access to keys in a bucket storage.
  36.    ''' </summary>
  37.    '''
  38.    ''' <exception cref="ArgumentException">
  39.    ''' </exception>
  40.    <Browsable(True)>
  41.    <DisplayName("Read-Access Key")>
  42.    <Description("The key used to authorize read access to keys in a bucket storage.")>
  43.    <Category("Access Keys")>
  44.    Public Overridable Property ReadKey As String
  45.        Get
  46.            Return Me.readKey_
  47.        End Get
  48.        Set(value As String)
  49.            UtilKvdb.ValidateTokenSize(value, NameOf(KvdbBucketPolicy.ReadKey))
  50.            Me.readKey_ = value
  51.        End Set
  52.    End Property
  53.    ''' <summary>
  54.    ''' Backing field for <see cref="KvdbBucketPolicy.ReadKey"/>.
  55.    ''' <para></para>
  56.    ''' The key used to authorize read access to keys in a bucket storage.
  57.    ''' </summary>
  58.    Protected readKey_ As String
  59.  
  60.    ''' <summary>
  61.    ''' Gets or sets the key used to authorize write access to keys in a bucket storage.
  62.    ''' </summary>
  63.    '''
  64.    ''' <exception cref="ArgumentException">
  65.    ''' </exception>
  66.    <Browsable(True)>
  67.    <DisplayName("Write-Access Key")>
  68.    <Description("The key used to authorize write access to keys in a bucket storage.")>
  69.    <Category("Access Keys")>
  70.    Public Overridable Property WriteKey As String
  71.        Get
  72.            Return Me.writeKey_
  73.        End Get
  74.        Set(value As String)
  75.            UtilKvdb.ValidateTokenSize(value, NameOf(KvdbBucketPolicy.WriteKey))
  76.            Me.writeKey_ = value
  77.        End Set
  78.    End Property
  79.    ''' <summary>
  80.    ''' Backing field for <see cref="KvdbBucketPolicy.WriteKey"/>.
  81.    ''' <para></para>
  82.    ''' The key used to authorize write access to keys in a bucket storage.
  83.    ''' </summary>
  84.    Protected writeKey_ As String
  85.  
  86.    ''' <summary>
  87.    ''' Gets or sets the key used to generate cryptographically signed access tokens.
  88.    ''' <para></para>
  89.    ''' <i>Changing the signing_key in a HTTP request will immediately invalidate all previously issued access tokens.</i>
  90.    ''' </summary>
  91.    '''
  92.    ''' <exception cref="ArgumentException">
  93.    ''' </exception>
  94.    <Browsable(True)>
  95.    <DisplayName("Token Signing Key")>
  96.    <Description("The key used to generate cryptographically signed access tokens.")>
  97.    <Category("Token Signing")>
  98.    Public Overridable Property SigningKey As String
  99.        Get
  100.            Return Me.signingKey_
  101.        End Get
  102.        Set(value As String)
  103.            UtilKvdb.ValidateTokenSize(value, NameOf(KvdbBucketPolicy.SigningKey))
  104.            Me.signingKey_ = value
  105.        End Set
  106.    End Property
  107.    ''' <summary>
  108.    ''' Backing field for <see cref="KvdbBucketPolicy.SigningKey"/>.
  109.    ''' <para></para>
  110.    ''' The key used to generate cryptographically signed access tokens.
  111.    ''' <para></para>
  112.    ''' <i>Changing the signing_key in a request will immediately invalidate all previously issued access tokens.</i>
  113.    ''' </summary>
  114.    Protected signingKey_ As String
  115.  
  116.    ''' <summary>
  117.    ''' Gets or sets the time duration after which the keys created in a bucket storage will expire and be automatically deleted.
  118.    ''' <para></para>
  119.    ''' Note than on free accounts, if you set this property to <see cref="TimeSpan.Zero"/>,
  120.    ''' the server will override it to 2,592,000 seconds (30 days).
  121.    ''' </summary>
  122.    '''
  123.    ''' <exception cref="ArgumentException">
  124.    ''' </exception>
  125.    <Browsable(True)>
  126.    <DisplayName("Key-Value Expiration")>
  127.    <Description("The time duration after which the keys created in a bucket storage will expire and be automatically deleted.")>
  128.    <Category("Expiration")>
  129.    Public Overridable Property KeyExpiration As TimeSpan
  130.        Get
  131.            Return Me.keyExpiration_
  132.        End Get
  133.        Set(value As TimeSpan)
  134.            If value <> TimeSpan.Zero AndAlso value.TotalSeconds <= 0 Then
  135.                Throw New ArgumentException(
  136.                        $"{NameOf(KvdbBucketPolicy.KeyExpiration)} must be greather than zero", NameOf(value))
  137.            End If
  138.            Me.keyExpiration_ = value
  139.        End Set
  140.    End Property
  141.    ''' <summary>
  142.    ''' Backing field for <see cref="KvdbBucketPolicy.KeyExpiration"/>.
  143.    ''' <para></para>
  144.    ''' The time duration after which the keys created in a bucket storage will expire and be automatically deleted.
  145.    ''' </summary>
  146.    Protected keyExpiration_ As TimeSpan
  147.  
  148. #End Region
  149.  
  150. #Region " Constructors "
  151.  
  152.    ''' <summary>
  153.    ''' Prevents a default instance of the <see cref="KvdbBucketPolicy"/> class from being created.
  154.    ''' </summary>
  155.    Private Sub New()
  156.    End Sub
  157.  
  158.    ''' <summary>
  159.    ''' Initializes a new instance of the <see cref="KvdbBucketPolicy"/> class.
  160.    ''' </summary>
  161.    '''
  162.    ''' <param name="secretKey">
  163.    ''' Optional. The key used to manage policy and other keys in a bucket storage.
  164.    ''' <para></para>
  165.    ''' Default value is null.
  166.    ''' </param>
  167.    '''
  168.    ''' <param name="readKey">
  169.    ''' Optional. The key used to authorize read access to keys in a bucket storage.
  170.    ''' <para></para>
  171.    ''' Default value is null.
  172.    ''' </param>
  173.    '''
  174.    ''' <param name="writeKey">
  175.    ''' Optional. The key used to authorize write access to keys in a bucket storage.
  176.    ''' <para></para>
  177.    ''' Default value is null.
  178.    ''' </param>
  179.    '''
  180.    ''' <param name="signingKey">
  181.    ''' Optional. The key used to generate cryptographically signed access tokens.
  182.    ''' <para></para>
  183.    ''' <i>Changing the signing_key in a HTTP request will immediately invalidate all previously issued access tokens.</i>
  184.    ''' <para></para>
  185.    ''' Default value is null.
  186.    ''' </param>
  187.    '''
  188.    ''' <param name="keyExpiration">
  189.    ''' Optional. The time duration after which the keys created in a bucket storage will expire and be automatically deleted.
  190.    ''' <para></para>
  191.    ''' Default value is <see cref="TimeSpan.Zero"/>, which indicates that the keys will never expire.
  192.    ''' <para></para>
  193.    ''' Note than on free accounts, if you set this value to <see cref="TimeSpan.Zero"/>,
  194.    ''' the server will override it to 2,592,000 seconds (30 days).
  195.    ''' </param>
  196.    Public Sub New(Optional secretKey As String = Nothing,
  197.                   Optional readKey As String = Nothing,
  198.                   Optional writeKey As String = Nothing,
  199.                   Optional signingKey As String = Nothing,
  200.                   Optional keyExpiration As TimeSpan = Nothing)
  201.  
  202.        Me.SecretKey = secretKey
  203.        Me.ReadKey = readKey
  204.        Me.WriteKey = writeKey
  205.        Me.SigningKey = signingKey
  206.        Me.KeyExpiration = keyExpiration
  207.    End Sub
  208.  
  209. #End Region
  210.  
  211. #Region " Public Methods "
  212.  
  213.    ''' <summary>
  214.    ''' Returns a string that represents the current object.
  215.    ''' </summary>
  216.    '''
  217.    ''' <returns>
  218.    ''' A <see cref="String" /> that represents this instance.
  219.    ''' </returns>
  220.    <EditorBrowsable(EditorBrowsableState.Advanced)>
  221.    Public Overrides Function ToString() As String
  222.  
  223.        Return MyBase.ToString()
  224.    End Function
  225.  
  226.    ''' <summary>
  227.    ''' Returns a string that represents the current policy configuration, formatted as a URL query string
  228.    ''' </summary>
  229.    '''
  230.    ''' <param name="escaped">
  231.    ''' A value indicating whether the resulting string should be URL-encoded.
  232.    ''' <para></para>
  233.    ''' If set to <see langword="True"/>, all value components will be encoded using
  234.    ''' <see cref="Uri.EscapeDataString"/> to ensure the resulting string is safe for use in URLs.
  235.    ''' <para></para>
  236.    ''' If set to <see langword="False"/>, raw values will be returned without any URL encoding,
  237.    ''' for internal use or debugging scenarios where encoding is not required.
  238.    ''' </param>
  239.    '''
  240.    ''' <returns>
  241.    ''' A <see cref="String" /> that represents this instance.
  242.    ''' </returns>
  243.    <EditorBrowsable(EditorBrowsableState.Always)>
  244.    Public Overridable Overloads Function ToString(escaped As Boolean) As String
  245.  
  246.        ' We call property getters rather than comparing fields to support any overriden properties.
  247.  
  248.        Dim secretKeyValue As String = If(Me.SecretKey, String.Empty)
  249.        Dim readKeyValue As String = If(Me.ReadKey, String.Empty)
  250.        Dim writeKeyValue As String = If(Me.WriteKey, String.Empty)
  251.        Dim signingKeyValue As String = If(Me.SigningKey, String.Empty)
  252.        Dim ttlValue As String = CInt(Me.KeyExpiration.TotalSeconds).ToString()
  253.  
  254.        Dim parts As New List(Of String)(capacity:=5) From {
  255.            $"secret_key={If(escaped, Uri.EscapeDataString(secretKeyValue), secretKeyValue)}",
  256.            $"read_key={If(escaped, Uri.EscapeDataString(readKeyValue), readKeyValue)}",
  257.            $"write_key={If(escaped, Uri.EscapeDataString(writeKeyValue), writeKeyValue)}",
  258.            $"signing_key={If(escaped, Uri.EscapeDataString(signingKeyValue), signingKeyValue)}",
  259.            $"default_ttl={ttlValue}"
  260.        }
  261.  
  262.        Return String.Join("&", parts)
  263.    End Function
  264.  
  265. #End Region
  266.  
  267. #Region " IEquatable implementation and equality operators "
  268.  
  269.    ''' <summary>
  270.    ''' Determines whether the current <see cref="KvdbBucketPolicy"/> is equal to another <see cref="KvdbBucketPolicy"/>.
  271.    ''' </summary>
  272.    '''
  273.    ''' <param name="other">
  274.    ''' A <see cref="KvdbBucketPolicy"/> to compare with this <see cref="KvdbBucketPolicy"/>.
  275.    ''' </param>
  276.    '''
  277.    ''' <returns>
  278.    ''' <see langword="True" /> if the current <see cref="KvdbBucketPolicy"/> is equal to the <paramref name="other" /> parameter;
  279.    ''' Otherwise, <see langword="False" />.
  280.    ''' </returns>
  281.    Public Overridable Overloads Function Equals(other As KvdbBucketPolicy) As Boolean _
  282.    Implements IEquatable(Of KvdbBucketPolicy).Equals
  283.  
  284.        ' We call property getters rather than comparing fields to support any overriden properties.
  285.  
  286.        Return (other IsNot Nothing) AndAlso (
  287.                  System.Object.ReferenceEquals(Me, other) OrElse
  288.                  (
  289.                    String.Equals(Me.SecretKey, other.SecretKey, StringComparison.Ordinal) AndAlso
  290.                    String.Equals(Me.ReadKey, other.ReadKey, StringComparison.Ordinal) AndAlso
  291.                    String.Equals(Me.WriteKey, other.WriteKey, StringComparison.Ordinal) AndAlso
  292.                    String.Equals(Me.SigningKey, other.SigningKey, StringComparison.Ordinal) AndAlso
  293.                    Me.KeyExpiration = other.KeyExpiration
  294.                  )
  295.               )
  296.  
  297.        ' Alternatively, we could compare the string representations of both instances using
  298.        ' the KvdbBucketPolicy.ToString() method instead of comparing each field individually,
  299.        ' which is simpler but it will be less efficient in memory and performance,
  300.        ' especially in scenarios involving indexed collections or frequent equality checks.
  301.    End Function
  302.  
  303.    ''' <summary>
  304.    ''' Determines whether the specified <see cref="Object" />, is equal to this instance.
  305.    ''' </summary>
  306.    '''
  307.    ''' <param name="obj">
  308.    ''' The <see cref="Object" /> to compare with this instance.
  309.    ''' </param>
  310.    '''
  311.    ''' <returns>
  312.    ''' <see langword="True"/> if the specified <see cref="Object" /> is equal to this instance;
  313.    ''' Otherwise, <see langword="False"/>.
  314.    ''' </returns>
  315.    Public Overrides Function Equals(obj As Object) As Boolean
  316.  
  317.        Dim other As KvdbBucketPolicy = TryCast(obj, KvdbBucketPolicy)
  318.  
  319.        Return Me.Equals(other)
  320.    End Function
  321.  
  322.    ''' <summary>
  323.    ''' Returns the hash code for the current <see cref="KvdbBucketPolicy"/>.
  324.    ''' </summary>
  325.    '''
  326.    ''' <returns>
  327.    ''' The hash code for the current <see cref="KvdbBucketPolicy"/>,
  328.    ''' suitable for use in hashing algorithms and data structures like a hash table.
  329.    ''' </returns>
  330.    Public Overrides Function GetHashCode() As Integer
  331.  
  332.        ' We call property getters rather than comparing fields to support any overriden properties.
  333.  
  334. #If NETCOREAPP Then
  335.        Dim hash As New HashCode()
  336.  
  337.        hash.Add(Me.SecretKey)
  338.        hash.Add(Me.ReadKey)
  339.        hash.Add(Me.WriteKey)
  340.        hash.Add(Me.SigningKey)
  341.        hash.Add(Me.KeyExpiration)
  342.  
  343.        Return hash.ToHashCode()
  344. #Else
  345.        ' We avoid the complexity of manually combining hash codes and potential issues with overflow
  346.        ' by leveraging the built-in functionality of Tuple to generate a combined hash code from all relevant fields.
  347.        Return Tuple.Create(
  348.            Me.SecretKey,
  349.            Me.ReadKey,
  350.            Me.WriteKey,
  351.            Me.SigningKey,
  352.            Me.KeyExpiration
  353.        ).GetHashCode()
  354. #End If
  355.  
  356.    End Function
  357.  
  358.    ''' <summary>
  359.    ''' Compares two <see cref="KvdbBucketPolicy"/> instances for equality.
  360.    ''' </summary>
  361.    '''
  362.    ''' <param name="first">
  363.    ''' The first <see cref="KvdbBucketPolicy"/> object to compare.
  364.    ''' </param>
  365.    '''
  366.    ''' <param name="second">
  367.    ''' The second <see cref="KvdbBucketPolicy"/> object to compare.
  368.    ''' </param>
  369.    '''
  370.    ''' <returns>
  371.    ''' Returns <see langword="True"/> if the instances are equal; otherwise, <see langword="False"/>.
  372.    ''' </returns>
  373.    Public Shared Operator =(first As KvdbBucketPolicy, second As KvdbBucketPolicy) As Boolean
  374.  
  375.        Return first IsNot Nothing AndAlso
  376.               second IsNot Nothing AndAlso
  377.               first.Equals(second)
  378.    End Operator
  379.  
  380.    ''' <summary>
  381.    ''' Compares two <see cref="KvdbBucketPolicy"/> instances for inequality.
  382.    ''' </summary>
  383.    '''
  384.    ''' <param name="first">
  385.    ''' The first <see cref="KvdbBucketPolicy"/> object to compare.
  386.    ''' </param>
  387.    '''
  388.    ''' <param name="second">
  389.    ''' The second <see cref="KvdbBucketPolicy"/> object to compare.
  390.    ''' </param>
  391.    '''
  392.    ''' <returns>
  393.    ''' Returns <see langword="True"/> if the instances are not equal; otherwise, <see langword="False"/>.
  394.    ''' </returns>
  395.    Public Shared Operator <>(first As KvdbBucketPolicy, second As KvdbBucketPolicy) As Boolean
  396.  
  397.        Return Not (first = second)
  398.    End Operator
  399.  
  400. #End Region
  401.  
  402. End Class
  403.  

 69 
 en: 4 Junio 2026, 21:17 pm 
Iniciado por Eleкtro - Último mensaje por Eleкtro
Hoy les traigo una clase utilitaria para realizar interacciones con el servicio online de https://kvdb.io/, el cual sirve para almacenar pares de claves y valores (key-value) de forma remota.

Los métodos de esta clase permiten crear y eliminar buckets (contenedores de claves), así como crear, leer y eliminar claves dentro de un bucket específico. Permiten también aplicar políticas de acceso a los buckets, como claves de lectura, escritura y expiración de claves. (Faltaría por implementar: actualizar las políticas, y listar claves)

Sin más, aquí les dejo la clase UtilKvdb con sus métodos, y la clase BucketPolicy para definir las políticas de acceso a los buckets.

Nótese los ejemplos de uso en los comentarios de cada método.

EDITADO CON LIGERAS MICRO-OPTIMIZACIONES Y UN NUEVO MÉTODO 'UpdateBucketPolicyAsync'

REEDITADO CON UN NUEVO MÉTODO 'ListBucketKeysAsync' Y ENUM 'KvdbOutputFormat'

Código:
🧠 Methods
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
UtilKvdb.CreateBucketAsync(String, KvdbBucketPolicy) As Task(Of String)
UtilKvdb.CreateKeyAsync(String, String, String, KvdbBucketPolicy) As Task
UtilKvdb.CreateKeyAsync(String, String, StringContent, KvdbBucketPolicy) As Task
UtilKvdb.DeleteBucketAsync(String, KvdbBucketPolicy) As Task
UtilKvdb.DeleteKeyAsync(String, String, KvdbBucketPolicy) As Task
UtilKvdb.ListKeysAsync(String, String, String, Boolean, Integer, Integer, Boolean, KvdbOutputFormat) As Task(Of String)
UtilKvdb.ReadKeyAsJsonAsync(String, String, KvdbBucketPolicy) As Task(Of JsonDocument)
UtilKvdb.ReadKeyAsStringAsync(String, String, KvdbBucketPolicy) As Task(Of String)
UtilKvdb.UpdateBucketPolicyAsync(String, KvdbBucketPolicy) As Task

Clase UtilKvdb:
Código
  1. Imports System.Net
  2. Imports System.Net.Http
  3. Imports System.Net.Http.Headers
  4. Imports System.Security.Authentication
  5. Imports System.Text.Json
  6.  
  7. ''' <summary>
  8. ''' Provides utility methods for interacting with the <c>kvdb.io</c> key-value storage service.
  9. ''' </summary>
  10. Public NotInheritable Class UtilKvdb
  11.  
  12. #Region " Constructors "
  13.  
  14.    ''' <summary>
  15.    ''' Prevents a default instance of the <see cref="UtilKvdb"/> class from being created.
  16.    ''' </summary>
  17.    Private Sub New()
  18.    End Sub
  19.  
  20. #End Region
  21.  
  22. #Region " Public Methods "
  23.  
  24.    ''' <summary>
  25.    ''' Asynchronously creates and retrieves a newly server-generated bucket storage
  26.    ''' with random ID on the remote <c>kvdb.io</c> service.
  27.    ''' </summary>
  28.    '''
  29.    ''' <example> This is a code example.
  30.    ''' <code language="VB">
  31.    ''' Dim mailAddress As String = "your_address@gmail.com"
  32.    '''
  33.    ''' Dim policy As New KvdbBucketPolicy With {
  34.    '''     .SecretKey = Nothing,
  35.    '''     .ReadKey = Nothing,
  36.    '''     .WriteKey = Nothing,
  37.    '''     .SigningKey = Nothing,
  38.    '''     .KeyExpiration = TimeSpan.FromHours(1)
  39.    ''' }
  40.    '''
  41.    ''' Dim newlyBucketId As String = Await GenerateBucketAsync(mailAddress, policy)
  42.    '''
  43.    ''' Console.WriteLine($"Server-generated bucket with random ID : {newlyBucketId}")
  44.    ''' Console.WriteLine($"Access URL to keys collection in bucket: https://kvdb.io/{newlyBucketId}")
  45.    ''' </code>
  46.    ''' </example>
  47.    '''
  48.    ''' <param name="associatedEmailAddress">
  49.    ''' The associated email address required by <c>kvdb.io</c> to generate a new bucket.
  50.    ''' <para></para>
  51.    ''' If the email address is not registered on the server, you will receive
  52.    ''' a notification email from <c>kvdb.io</c> with a confirmation link
  53.    ''' to validate the email address and activate your (free)account.
  54.    ''' </param>
  55.    '''
  56.    ''' <param name="policy">
  57.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  58.    ''' and key expiration time to be applied to the newly server-generated bucket.
  59.    ''' <para></para>
  60.    ''' If no policy is provided or does not contain access keys,
  61.    ''' the server-generated bucket will have no configured access keys.
  62.    ''' </param>
  63.    '''
  64.    ''' <returns>
  65.    ''' A <see cref="Task(Of String)"/> containing the unique identifier for the newly server-generated bucket.
  66.    ''' <para></para>
  67.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  68.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  69.    ''' </returns>
  70.    '''
  71.    ''' <exception cref="HttpRequestException">
  72.    ''' </exception>
  73.    <DebuggerStepThrough>
  74.    Public Shared Async Function GenerateBucketAsync(associatedEmailAddress As String,
  75.                                            Optional policy As KvdbBucketPolicy = Nothing) As Task(Of String)
  76.  
  77. #If NETCOREAPP Then
  78.        ArgumentNullException.ThrowIfNullOrWhiteSpace(associatedEmailAddress, paramName:=NameOf(associatedEmailAddress))
  79. #Else
  80.        If String.IsNullOrWhiteSpace(associatedEmailAddress) Then : Throw New ArgumentNullException(paramName:=NameOf(associatedEmailAddress)) : End If
  81. #End If
  82.  
  83.        Dim query As String = $"email={Uri.EscapeDataString(associatedEmailAddress)}"
  84.        If policy IsNot Nothing Then
  85.            query &= $"&{policy.ToString(escaped:=True)}"
  86.        End If
  87.  
  88.        Using content As New StringContent(query, Encoding.UTF8, "application/x-www-form-urlencoded"),
  89.              client As HttpClient = UtilKvdb.BuildHttpClient(policy:=Nothing),
  90.              response As HttpResponseMessage = Await client.PostAsync(requestUri:=String.Empty, content).
  91.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  92.            If response.IsSuccessStatusCode Then
  93.                Dim rawId As String = Await response.Content.ReadAsStringAsync().
  94.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  95.                Return rawId?.Trim()
  96.            Else
  97.                Throw New HttpRequestException(
  98.                    $"Failed to create bucket on the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  99.            End If
  100.        End Using
  101.    End Function
  102.  
  103.    ''' <summary>
  104.    ''' Asynchronously updates the policy for an existing bucket storage
  105.    ''' on the remote <c>kvdb.io</c> service using the provided <see cref="KvdbBucketPolicy"/>.
  106.    ''' </summary>
  107.    '''
  108.    ''' <remarks>
  109.    ''' <b>Important Limitation:</b>
  110.    ''' Once a <c>secret key</c>, <c>read key</c>, <c>write key</c> or <c>signing key</c> has been established for a bucket,
  111.    ''' <para></para>
  112.    ''' the <c>kvdb.io</c> API does not seem to support clearing any of these keys to null (public state).
  113.    ''' </remarks>
  114.    '''
  115.    ''' <example> This is a code example.
  116.    ''' <code language="VB">
  117.    ''' Dim mailAddress As String = "your_address@gmail.com"
  118.    '''
  119.    ''' Dim defaultPolicy As New KvdbBucketPolicy()
  120.    '''
  121.    ''' Dim newlyBucketId As String = Await GenerateBucketAsync(mailAddress, defaultPolicy)
  122.    '''
  123.    ''' Console.WriteLine($"Server-generated bucket with random ID: {newlyBucketId}")
  124.    '''
  125.    ''' Dim newPolicy As New KvdbBucketPolicy With {
  126.    '''     .SecretKey = "My Secret Key",
  127.    '''     .ReadKey = "My Read Key",
  128.    '''     .WriteKey = "My Write Key",
  129.    '''     .SigningKey = Nothing,
  130.    '''     .KeyExpiration = TimeSpan.FromHours(1)
  131.    ''' }
  132.    '''
  133.    ''' Await UpdateBucketPolicyAsync(newlyBucketId, newPolicy)
  134.    '''
  135.    ''' Console.WriteLine($"Policty updated for bucket: https://kvdb.io/{newlyBucketId}")
  136.    ''' </code>
  137.    ''' </example>
  138.    '''
  139.    ''' <param name="policy">
  140.    ''' The <see cref="KvdbBucketPolicy"/> object containing the access keys
  141.    ''' and key expiration time to be applied to the specified bucket.
  142.    ''' </param>
  143.    '''
  144.    ''' <returns>
  145.    ''' A <see cref="Task(Of String)"/> representing the asynchronous operation.
  146.    ''' <para></para>
  147.    ''' If the task completes successfully, it indicates that
  148.    ''' the bucket policy was successfully updated on the server.
  149.    ''' </returns>
  150.    '''
  151.    ''' <exception cref="HttpRequestException">
  152.    ''' </exception>
  153.    <DebuggerStepThrough>
  154.    Public Shared Async Function UpdateBucketPolicyAsync(bucketId As String, policy As KvdbBucketPolicy) As Task
  155.  
  156. #If NETCOREAPP Then
  157.    ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  158.    ArgumentNullException.ThrowIfNull(policy, paramName:=NameOf(policy))
  159. #Else
  160.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  161.        If policy Is Nothing Then : Throw New ArgumentNullException(paramName:=NameOf(policy)) : End If
  162. #End If
  163.  
  164.        Dim policyQuery As String = policy.ToString(escaped:=True)
  165.        Dim relativeUrl As String = $"{bucketId}?{policyQuery}"
  166.  
  167.        Using content As New StringContent(policyQuery, Encoding.UTF8, "application/x-www-form-urlencoded"),
  168.              client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  169.              request As New HttpRequestMessage(New HttpMethod("PATCH"), relativeUrl) With {.Content = content},
  170.              response As HttpResponseMessage = Await client.SendAsync(request).
  171.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  172.            If response.IsSuccessStatusCode Then
  173.                Return
  174.            Else
  175.                Throw New HttpRequestException(
  176.                    $"Failed to update bucket policy on the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  177.            End If
  178.        End Using
  179.    End Function
  180.  
  181.    ''' <summary>
  182.    ''' Asynchronously deletes an existing bucket storage and its associated collection of keys
  183.    ''' from the remote <c>kvdb.io</c> service.
  184.    ''' </summary>
  185.    '''
  186.    ''' <example> This is a code example.
  187.    ''' <code language="VB">
  188.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  189.    '''
  190.    ''' Await DeleteBucketAsync(bucketId)
  191.    ''' </code>
  192.    ''' </example>
  193.    '''
  194.    ''' <param name="bucketId">
  195.    ''' The unique identifier pointing to the bucket storage to delete. The bucket storage must exist on the server.
  196.    ''' <para></para>
  197.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  198.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  199.    ''' </param>
  200.    '''
  201.    ''' <param name="policy">
  202.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  203.    ''' to authorize the deletion request for the specified bucket.
  204.    ''' <para></para>
  205.    ''' If no policy is provided or does not contain access keys,
  206.    ''' the deletion request will be sent without authentication.
  207.    ''' </param>
  208.    '''
  209.    ''' <returns>
  210.    ''' A <see cref="Task"/> representing the asynchronous operation.
  211.    ''' <para></para>
  212.    ''' If the task completes successfully, it indicates that the server accepted the deletion request
  213.    ''' (<see cref="HttpStatusCode.Accepted"/>) to remove the bucket storage.
  214.    ''' </returns>
  215.    '''
  216.    ''' <exception cref="HttpRequestException">
  217.    ''' </exception>
  218.    <DebuggerStepThrough>
  219.    Public Shared Async Function DeleteBucketAsync(bucketId As String,
  220.                                          Optional policy As KvdbBucketPolicy = Nothing) As Task
  221.  
  222. #If NETCOREAPP Then
  223.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  224. #Else
  225.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  226. #End If
  227.  
  228.        Using client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  229.              response As HttpResponseMessage = Await client.DeleteAsync(bucketId).
  230.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  231.            If response.IsSuccessStatusCode Then
  232.                Return
  233.            Else
  234.                Throw New HttpRequestException(
  235.                    $"Failed to delete bucket from the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  236.            End If
  237.        End Using
  238.    End Function
  239.  
  240.    ''' <summary>
  241.    ''' Asynchronously creates or overwrites a key-value slot inside the specified bucket storage
  242.    ''' from the remote <c>kvdb.io</c> service using the provided <see cref="StringContent"/>.
  243.    ''' </summary>
  244.    '''
  245.    ''' <example> This is a code example.
  246.    ''' <code language="VB">
  247.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  248.    ''' Dim key As String = "Name of the key to create"
  249.    ''' Dim value As String = "Value of the key"
  250.    '''
  251.    ''' Dim payloadData As New Dictionary(Of String, Object)(StringComparer.Ordinal) From {
  252.    '''     {"Key", key},
  253.    '''     {"Value", value}
  254.    ''' }
  255.    '''
  256.    ''' Dim jsonString As String = JsonSerializer.Serialize(payloadData)
  257.    '''
  258.    ''' Using content As New StringContent(jsonString, Encoding.UTF8, "application/json")
  259.    '''
  260.    '''     Await CreateKeyAsync(bucketId, key, content)
  261.    ''' End Using
  262.    ''' </code>
  263.    ''' </example>
  264.    '''
  265.    ''' <param name="bucketId">
  266.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  267.    ''' <para></para>
  268.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  269.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  270.    ''' </param>
  271.    '''
  272.    ''' <param name="key">
  273.    ''' The name of the key to create or overwrite.
  274.    ''' </param>
  275.    '''
  276.    ''' <param name="content">
  277.    ''' The <see cref="StringContent"/> object containing the data to be stored in the key.
  278.    ''' <para></para>
  279.    ''' For example, to store JSON data, the <see cref="StringContent"/> can be initialized as follows:
  280.    ''' <code>
  281.    '''     Dim jsonString As String = JsonSerializer.Serialize(myObject)
  282.    '''     Dim content As New StringContent(jsonString, Encoding.UTF8, "application/json")
  283.    ''' </code>
  284.    ''' </param>
  285.    '''
  286.    ''' <param name="policy">
  287.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  288.    ''' to authorize the creation or overwriting request for the specified key,
  289.    ''' and the expiration time to be applied to the key.
  290.    ''' <para></para>
  291.    ''' If no policy is provided or does not contain access keys,
  292.    ''' the creation request will be sent without authentication.
  293.    ''' </param>
  294.    '''
  295.    ''' <returns>
  296.    ''' A <see cref="Task"/> representing the asynchronous operation.
  297.    ''' <para></para>
  298.    ''' If the task completes successfully, it indicates that
  299.    ''' the key was successfully created or overwritten on the server.
  300.    ''' </returns>
  301.    '''
  302.    ''' <exception cref="HttpRequestException">
  303.    ''' </exception>
  304.    <DebuggerStepThrough>
  305.    Public Shared Async Function CreateKeyAsync(bucketId As String, key As String, content As StringContent,
  306.                                       Optional policy As KvdbBucketPolicy = Nothing) As Task
  307.  
  308. #If NETCOREAPP Then
  309.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  310.        ArgumentNullException.ThrowIfNullOrWhiteSpace(key, paramName:=NameOf(key))
  311.        ArgumentNullException.ThrowIfNull(content, paramName:=NameOf(content))
  312. #Else
  313.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  314.        If String.IsNullOrWhiteSpace(key) Then : Throw New ArgumentNullException(paramName:=NameOf(key)) : End If
  315.        If content Is Nothing Then : Throw New ArgumentNullException(paramName:=NameOf(content)) : End If
  316. #End If
  317.        UtilKvdb.ValidateTokenSize(key, NameOf(key))
  318.  
  319.        Dim relativeUrl As String = UtilKvdb.BuildRelativeKeyUrl(bucketId, key, isWriteOperation:=True, policy)
  320.  
  321.        Using client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  322.              response As HttpResponseMessage = Await client.PostAsync(relativeUrl, content).
  323.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  324.            If response.IsSuccessStatusCode Then
  325.                Return
  326.            Else
  327.                Throw New HttpRequestException(
  328.                    $"Failed to create or overwrite key on the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  329.            End If
  330.        End Using
  331.    End Function
  332.  
  333.    ''' <summary>
  334.    ''' Asynchronously creates or overwrites a key-value slot inside the specified bucket storage
  335.    ''' from the remote <c>kvdb.io</c> service using the provided <see cref="String"/> value.
  336.    ''' </summary>
  337.    '''
  338.    ''' <example> This is a code example.
  339.    ''' <code language="VB">
  340.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  341.    ''' Dim key As String = "Name of the key to create"
  342.    ''' Dim value As String = "Value of the key"
  343.    '''
  344.    ''' Await CreateKeyAsync(bucketId, key, value)
  345.    ''' </code>
  346.    ''' </example>
  347.    '''
  348.    ''' <param name="bucketId">
  349.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  350.    ''' <para></para>
  351.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  352.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  353.    ''' </param>
  354.    '''
  355.    ''' <param name="key">
  356.    ''' The name of the key to create or overwrite.
  357.    ''' </param>
  358.    '''
  359.    ''' <param name="value">
  360.    ''' The value to be stored in the key.
  361.    ''' <para></para>
  362.    ''' This value will be sent to the server as UTF-8 encoded plain text with media type <c>"text/plain"</c>.
  363.    ''' </param>
  364.    '''
  365.    ''' <param name="policy">
  366.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  367.    ''' to authorize the creation or overwriting request for the specified key,
  368.    ''' and the expiration time to be applied to the key.
  369.    ''' <para></para>
  370.    ''' If no policy is provided or does not contain access keys,
  371.    ''' the creation request will be sent without authentication.
  372.    ''' </param>
  373.    '''
  374.    ''' <returns>
  375.    ''' A <see cref="Task"/> representing the asynchronous operation.
  376.    ''' <para></para>
  377.    ''' If the task completes successfully, it indicates that
  378.    ''' the key was successfully created or overwritten on the server.
  379.    ''' </returns>
  380.    '''
  381.    ''' <exception cref="HttpRequestException">
  382.    ''' </exception>
  383.    <DebuggerStepThrough>
  384.    Public Shared Async Function CreateKeyAsync(bucketId As String, key As String, value As String,
  385.                                       Optional policy As KvdbBucketPolicy = Nothing) As Task
  386.  
  387. #If NETCOREAPP Then
  388.        ArgumentNullException.ThrowIfNullOrEmpty(value, paramName:=NameOf(value))
  389. #Else
  390.        If String.IsNullOrEmpty(value) Then : Throw New ArgumentNullException(paramName:=NameOf(value)) : End If
  391. #End If
  392.  
  393.        Using content As New StringContent(value, Encoding.UTF8, "text/plain")
  394.  
  395.            Await UtilKvdb.CreateKeyAsync(bucketId, key, content, policy).
  396.                           ConfigureAwait(continueOnCapturedContext:=False)
  397.        End Using
  398.    End Function
  399.  
  400.    ''' <summary>
  401.    ''' Asynchronously retrieves the value stored in a existing key-value slot from the specified bucket storage
  402.    ''' on the remote <c>kvdb.io</c> service as a raw plain text.
  403.    ''' </summary>
  404.    '''
  405.    ''' <example> This is a code example.
  406.    ''' <code language="VB">
  407.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  408.    ''' Dim key As String = "Name of the key to create"
  409.    '''
  410.    ''' Dim value As String = Await GetKeyValueAsync(bucketId, key)
  411.    '''
  412.    ''' Console.WriteLine($"Raw value retrieved from key: {value}")
  413.    ''' </code>
  414.    ''' </example>
  415.    '''
  416.    ''' <param name="bucketId">
  417.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  418.    ''' <para></para>
  419.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  420.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  421.    ''' </param>
  422.    '''
  423.    ''' <param name="key">
  424.    ''' The name of the key from which to retrieve its value.
  425.    ''' </param>
  426.    '''
  427.    ''' <param name="policy">
  428.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  429.    ''' to authorize read-access for the specified key.
  430.    ''' <para></para>
  431.    ''' If no policy is provided or does not contain access keys,
  432.    ''' the read-access request will be sent without authentication.
  433.    ''' </param>
  434.    '''
  435.    ''' <returns>
  436.    ''' A <see cref="Task(Of String)"/> containing the value stored in the specified key as a raw plain text string.
  437.    ''' </returns>
  438.    '''
  439.    ''' <exception cref="HttpRequestException">
  440.    ''' </exception>
  441.    <DebuggerStepThrough>
  442.    Public Shared Async Function GetKeyValueAsync(bucketId As String, key As String,
  443.                                         Optional policy As KvdbBucketPolicy = Nothing) As Task(Of String)
  444.  
  445. #If NETCOREAPP Then
  446.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  447.        ArgumentNullException.ThrowIfNullOrWhiteSpace(key, paramName:=NameOf(key))
  448. #Else
  449.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  450.        If String.IsNullOrWhiteSpace(key) Then : Throw New ArgumentNullException(paramName:=NameOf(key)) : End If
  451. #End If
  452.  
  453.        Dim relativeUrl As String = UtilKvdb.BuildRelativeKeyUrl(bucketId, key, isWriteOperation:=False, policy)
  454.  
  455.        Using client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  456.              response As HttpResponseMessage = Await client.GetAsync(relativeUrl).
  457.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  458.            If response.StatusCode = HttpStatusCode.OK Then
  459.                Dim extractedContent As String = Await response.Content.ReadAsStringAsync().
  460.                                                                        ConfigureAwait(continueOnCapturedContext:=False)
  461.                Return extractedContent
  462.            Else
  463.                Throw New HttpRequestException(
  464.                    $"Failed to read key from the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  465.            End If
  466.        End Using
  467.    End Function
  468.  
  469.    ''' <summary>
  470.    ''' Asynchronously retrieves the value stored in a existing key-value slot from the specified bucket storage
  471.    ''' on the remote <c>kvdb.io</c> service as a <see cref="JsonDocument"/>.
  472.    ''' </summary>
  473.    '''
  474.    ''' <example> This is a code example.
  475.    ''' <code language="VB">
  476.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  477.    ''' Dim key As String = "Name of the key to create"
  478.    '''
  479.    ''' Dim value As JsonDocument = Await GetKeyValueJsonAsync(bucketId, key)
  480.    '''
  481.    ''' Console.WriteLine($"JSON value retrieved from key: {value.RootElement}")
  482.    ''' </code>
  483.    ''' </example>
  484.    '''
  485.    ''' <param name="bucketId">
  486.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  487.    ''' <para></para>
  488.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  489.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  490.    ''' </param>
  491.    '''
  492.    ''' <param name="key">
  493.    ''' The name of the key from which to retrieve its value.
  494.    ''' </param>
  495.    '''
  496.    ''' <param name="policy">
  497.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  498.    ''' to authorize read-access for the specified key.
  499.    ''' <para></para>
  500.    ''' If no policy is provided or does not contain access keys,
  501.    ''' the read-access request will be sent without authentication.
  502.    ''' </param>
  503.    '''
  504.    ''' <returns>
  505.    ''' A <see cref="Task(Of JsonDocument)"/> containing the value stored in the specified key as a <see cref="JsonDocument"/>.
  506.    ''' </returns>
  507.    '''
  508.    ''' <exception cref="HttpRequestException">
  509.    ''' </exception>
  510.    '''
  511.    ''' <exception cref="JsonException">
  512.    ''' </exception>
  513.    <DebuggerStepThrough>
  514.    Public Shared Async Function GetKeyValueJsonAsync(bucketId As String, key As String,
  515.                                             Optional policy As KvdbBucketPolicy = Nothing) As Task(Of JsonDocument)
  516.  
  517.        Dim jsonString As String = Await UtilKvdb.GetKeyValueAsync(bucketId, key, policy).
  518.                                                  ConfigureAwait(continueOnCapturedContext:=False)
  519.  
  520.        ' Throws a JsonException if the remote payload content is invalid JSON.
  521.        Dim parsedDocument As JsonDocument = JsonDocument.Parse(jsonString)
  522.  
  523.        Return parsedDocument
  524.    End Function
  525.  
  526.    ''' <summary>
  527.    ''' Asynchronously deletes a existing key-value slot in the specified bucket storage
  528.    ''' from the remote <c>kvdb.io</c> service.
  529.    ''' </summary>
  530.    '''
  531.    ''' <example> This is a code example.
  532.    ''' <code language="VB">
  533.    ''' Dim bucketId As String = "JjSDG9KgFrBMbKgjRwmnAd"
  534.    ''' Dim key As String = "Name of the key to delete"
  535.    '''
  536.    ''' Await DeleteKeyAsync(bucketId, key)
  537.    ''' </code>
  538.    ''' </example>
  539.    '''
  540.    ''' <param name="bucketId">
  541.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  542.    ''' <para></para>
  543.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  544.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  545.    ''' </param>
  546.    '''
  547.    ''' <param name="key">
  548.    ''' The name of the key to delete.
  549.    ''' </param>
  550.    '''
  551.    ''' <param name="policy">
  552.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the access keys
  553.    ''' to authorize the deletion for the specified key.
  554.    ''' <para></para>
  555.    ''' If no policy is provided or does not contain access keys,
  556.    ''' the deletion request will be sent without authentication.
  557.    ''' </param>
  558.    '''
  559.    ''' <returns>
  560.    ''' A <see cref="Task"/> representing the asynchronous operation.
  561.    ''' <para></para>
  562.    ''' If the task completes successfully, it indicates that the server accepted the deletion request
  563.    ''' (<see cref="HttpStatusCode.Accepted"/>) to remove the key, regardless of whether the key exists or not.
  564.    ''' </returns>
  565.    '''
  566.    ''' <exception cref="HttpRequestException">
  567.    ''' </exception>
  568.    <DebuggerStepThrough>
  569.    Public Shared Async Function DeleteKeyAsync(bucketId As String, key As String,
  570.                                       Optional policy As KvdbBucketPolicy = Nothing) As Task
  571.  
  572. #If NETCOREAPP Then
  573.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  574.        ArgumentNullException.ThrowIfNullOrWhiteSpace(key, paramName:=NameOf(key))
  575. #Else
  576.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  577.        If String.IsNullOrWhiteSpace(key) Then : Throw New ArgumentNullException(paramName:=NameOf(key)) : End If
  578. #End If
  579.  
  580.        Dim relativeUrl As String = UtilKvdb.BuildRelativeKeyUrl(bucketId, key, isWriteOperation:=True, policy)
  581.  
  582.        Using client As HttpClient = UtilKvdb.BuildHttpClient(policy),
  583.              response As HttpResponseMessage = Await client.DeleteAsync(relativeUrl).
  584.                                                             ConfigureAwait(continueOnCapturedContext:=False)
  585.            If response.IsSuccessStatusCode Then
  586.                Return
  587.            Else
  588.                Throw New HttpRequestException(
  589.                    $"Failed to delete key from the server. Status Code: {CInt(response.StatusCode)} ({response.StatusCode})")
  590.            End If
  591.        End Using
  592.    End Function
  593.  
  594. #End Region
  595.  
  596. #Region " Private Methods "
  597.  
  598.    ''' <summary>
  599.    ''' Gets a new instance of <see cref="HttpClient"/> configured for use with the <c>kvdb.io</c> service,
  600.    ''' applying the appropriate authentication header if a <see cref="KvdbBucketPolicy.SecretKey"/> is provided.
  601.    ''' </summary>
  602.    '''
  603.    ''' <param name="policy">
  604.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing the <c>SecretKey</c> to be applied for
  605.    ''' authentication in the HTTP requests sent by the returned <see cref="HttpClient"/> instance.
  606.    ''' <para></para>
  607.    ''' If no policy is provided or does not contain a <c>SecretKey</c>,
  608.    ''' the returned <see cref="HttpClient"/> will not have any authentication header applied.
  609.    ''' </param>
  610.    '''
  611.    ''' <returns>
  612.    ''' Returns the resulting <see cref="HttpClient"/> instance configured for use with the <c>kvdb.io</c> service,
  613.    ''' and with the appropriate authentication header if applicable.
  614.    ''' </returns>
  615.    <DebuggerStepThrough>
  616.    Private Shared Function BuildHttpClient(Optional policy As KvdbBucketPolicy = Nothing) As HttpClient
  617.  
  618.        ' clientHandler is automatically disposed by HttpClient object on disposal.
  619.        Dim clientHandler As New HttpClientHandler() With {
  620.            .AllowAutoRedirect = False,
  621.            .UseCookies = False,
  622.            .UseProxy = False,
  623.            .DefaultProxyCredentials = Nothing,
  624.            .UseDefaultCredentials = False,
  625.            .Credentials = Nothing,
  626.            .PreAuthenticate = False,
  627.            .CheckCertificateRevocationList = True,
  628.            .ClientCertificateOptions = ClientCertificateOption.Automatic,
  629.            .SslProtocols = SslProtocols.Tls12 Or SslProtocols.Tls13,
  630.            .AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
  631.        }
  632.  
  633.        ' The BaseAddress for the <c>kvdb.io</c> service API,
  634.        ' so that we can use relative URLs for the requests.
  635.        Const kvdbBaseAddress As String = "https://kvdb.io"
  636.  
  637.        Dim client As New HttpClient(clientHandler, disposeHandler:=True) With {
  638.            .BaseAddress = New Uri(kvdbBaseAddress),
  639.            .Timeout = TimeSpan.FromSeconds(60)
  640.        }
  641.  
  642.        If (policy IsNot Nothing) AndAlso Not String.IsNullOrEmpty(policy.SecretKey) Then
  643.            client.DefaultRequestHeaders.Authorization = New AuthenticationHeaderValue("Bearer", policy.SecretKey)
  644.        End If
  645.  
  646.        Return client
  647.    End Function
  648.  
  649.    ''' <summary>
  650.    ''' Builds the relative URL for accessing a specific key in a bucket storage on the remote <c>kvdb.io</c> service,
  651.    ''' including the appropriate query string parameters for authentication and key expiration based on
  652.    ''' the provided <see cref="KvdbBucketPolicy"/> and operation context (create / read / write / delete).
  653.    ''' </summary>
  654.    '''
  655.    ''' <param name="bucketId">
  656.    ''' The unique identifier pointing to the bucket storage. The bucket storage must exist on the server.
  657.    ''' <para></para>
  658.    ''' <i>Typically, the bucket identifier is an alphanumeric string with 22 character length,
  659.    ''' such as: <c>"JjSDG9KgFrBMbKgjRwmnAd"</c></i>
  660.    ''' </param>
  661.    '''
  662.    ''' <param name="key">
  663.    ''' The name of the key for which to build the access URL.
  664.    ''' </param>
  665.    '''
  666.    ''' <param name="isWriteOperation">
  667.    ''' A value indicating whether the URL is being built for a write or a read operation.
  668.    ''' <para></para>
  669.    ''' This parameter is used to determine which access key to attach in the query string,
  670.    ''' and whether to include the TTL parameter for key expiration.
  671.    ''' </param>
  672.    '''
  673.    ''' <param name="policy">
  674.    ''' Optional. The <see cref="KvdbBucketPolicy"/> object containing
  675.    ''' the access keys to authorize the request for the specified key,
  676.    ''' and the expiration time to be applied to the key if applicable.
  677.    ''' <para></para>
  678.    ''' If no policy is provided or does not contain access keys,
  679.    ''' the URL will be built without authentication parameters.
  680.    ''' </param>
  681.    '''
  682.    ''' <returns>
  683.    ''' Returns the resulting complete URL for accessing the specified key in the bucket storage.
  684.    ''' </returns>
  685.    <DebuggerStepThrough>
  686.    Private Shared Function BuildRelativeKeyUrl(bucketId As String, key As String,
  687.                                                isWriteOperation As Boolean,
  688.                                       Optional policy As KvdbBucketPolicy = Nothing) As String
  689.  
  690. #If NETCOREAPP Then
  691.        ArgumentNullException.ThrowIfNullOrWhiteSpace(bucketId, paramName:=NameOf(bucketId))
  692.        ArgumentNullException.ThrowIfNullOrWhiteSpace(key, paramName:=NameOf(key))
  693. #Else
  694.        If String.IsNullOrWhiteSpace(bucketId) Then : Throw New ArgumentNullException(paramName:=NameOf(bucketId)) : End If
  695.        If String.IsNullOrWhiteSpace(key) Then : Throw New ArgumentNullException(paramName:=NameOf(key)) : End If
  696. #End If
  697.  
  698.        Dim escapedKey As String = Uri.EscapeDataString(key)
  699.        Dim relativePath As String = $"{bucketId}/{escapedKey}"
  700.        Dim queryParameters As New List(Of String)(capacity:=2)
  701.  
  702.        If policy IsNot Nothing Then
  703.            ' Determine whether to attach the WriteKey or ReadKey based on the execution context.
  704.  
  705.            If isWriteOperation Then
  706.                If Not String.IsNullOrEmpty(policy.WriteKey) Then
  707.                    Dim escapedWriteKey As String = Uri.EscapeDataString(policy.WriteKey)
  708.                    queryParameters.Add($"write_key={escapedWriteKey}")
  709.                End If
  710.  
  711.                ' The TTL parameter is only applicable during write operations.
  712.                If policy.KeyExpiration <> TimeSpan.Zero Then
  713.                    Dim ttlValue As String = CInt(policy.KeyExpiration.TotalSeconds).ToString()
  714.                    queryParameters.Add($"ttl={ttlValue}")
  715.                End If
  716.            Else
  717.                If Not String.IsNullOrEmpty(policy.ReadKey) Then
  718.                    Dim escapedReadKey As String = Uri.EscapeDataString(policy.ReadKey)
  719.                    queryParameters.Add($"read_key={escapedReadKey}")
  720.                End If
  721.            End If
  722.  
  723.            ' If there are parameters, we append them manually to the relative path using a '?' character.
  724.            If queryParameters.Any() Then
  725.                Dim queryString As String = String.Join("&", queryParameters)
  726.                relativePath = $"{relativePath}?{queryString}"
  727.            End If
  728.        End If
  729.  
  730.        Return relativePath
  731.    End Function
  732.  
  733.    ''' <summary>
  734.    ''' Validates that the provided token value does not exceed the maximum allowed byte size limit when encoded in <c>UTF-8</c>,
  735.    ''' which is 128 bytes (at least for key names) according to the <c>kvdb.io</c> service specifications.
  736.    ''' <para></para>
  737.    ''' Throws an <see cref="ArgumentException"/> if the byte size of the token exceeds the maximum allowed limit.
  738.    ''' </summary>
  739.    '''
  740.    ''' <param name="token">
  741.    ''' The token value to validate. This value can be null.
  742.    ''' </param>
  743.    '''
  744.    ''' <param name="paramName">
  745.    ''' The name of the parameter being validated for error tracing.
  746.    ''' </param>
  747.    '''
  748.    ''' <exception cref="ArgumentException">
  749.    ''' </exception>
  750.    <DebuggerStepThrough>
  751.    Friend Shared Sub ValidateTokenSize(token As String, paramName As String)
  752.  
  753.        Const MaxKvdbTokenByteSize As Integer = 128 ' In bytes, as per kvdb.io specifications.
  754.  
  755. #If NETCOREAPP Then
  756.        ArgumentNullException.ThrowIfNullOrWhiteSpace(paramName, paramName:=NameOf(paramName))
  757. #Else
  758.        If String.IsNullOrWhiteSpace(paramName) Then : Throw New ArgumentNullException(paramName:=NameOf(paramName)) : End If
  759. #End If
  760.  
  761.        If token IsNot Nothing Then
  762.            Dim byteCount As Integer = Encoding.UTF8.GetByteCount(token)
  763.  
  764.            If byteCount > MaxKvdbTokenByteSize Then
  765.                Throw New ArgumentException(
  766.                    $"The token size ({byteCount} bytes) for parameter '{paramName}' exceeds the" &
  767.                    " maximum allowed limit of {MaxKvdbTokenByteSize} bytes when encoded in UTF-8.",
  768.                    paramName:=paramName)
  769.            End If
  770.        End If
  771.    End Sub
  772.  
  773. #End Region
  774.  
  775. End Class

 70 
 en: 4 Junio 2026, 13:18 pm 
Iniciado por Tachikomaia - Último mensaje por Tachikomaia
Al crear a un enemigo:
Código
  1. Name._x = Math.random()*320;
  2. // Para zigzaguear:
  3. Name.MinX = Name._x-Math.random()*EnemigosRate;
  4. Name.MaxX = Name._x+Math.random()*EnemigosRate;
  5. Name.SpeedX = Math.random()*MinEnemigoSpeed*(random(2)*2-1);

EnemigosRate es 10
MinEnemigoSpeed es 0.1

Ambas van aumentando.

Cuando los enemigos se mueven:
Código
  1. if (Name._x < Name.MinX or Name._x > Name.MaxX) {
  2. Name.SpeedX = Name.SpeedX*-1;
  3. }
  4.  

Simplemente cree un punto mínimo y máximo al que pueden estar.
Si queda a la izquierda del punto mínimo, empezará a moverse al lado contrario.
Si queda a la derecha del punto máximo, lo mismo.

Pero falla ¿cual es el problema?

Páginas: 1 2 3 4 5 6 [7] 8 9 10
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines