Implement notification stream rpc

This commit is contained in:
Alexander
2026-07-22 20:32:44 +02:00
parent e1d35f81bc
commit 9c2499a4a3
13 changed files with 2338 additions and 264 deletions
Generated
+2
View File
@@ -3200,12 +3200,14 @@ name = "tora"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"clap",
"colored",
"hyper-util",
"nix",
"serde_json",
"tokio",
"tokio-stream",
"tonic",
"tonic-health",
"tora-proto",
+656 -224
View File
@@ -159,6 +159,136 @@ impl ::prost::Name for ResumeResponse {
}
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct NotificationsRequest {
/// If non-empty, only events for these torrent ids (or id prefixes, as
/// accepted by `Status`/`Remove`) are delivered.
#[prost(string, repeated, tag = "1")]
pub ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// If non-empty, only these event kinds are delivered. An empty list means
/// all kinds.
#[prost(enumeration = "NotificationKind", repeated, tag = "2")]
pub kinds: ::prost::alloc::vec::Vec<i32>,
}
impl ::prost::Name for NotificationsRequest {
const NAME: &'static str = "NotificationsRequest";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.NotificationsRequest".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.NotificationsRequest".into()
}
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct Notification {
/// Wall-clock time the daemon observed the change, in milliseconds since the
/// Unix epoch.
#[prost(uint64, tag = "1")]
pub observed_at_unix_millis: u64,
/// What changed. Use this to dispatch; use the `oneof detail` for
/// kind-specific payload.
#[prost(enumeration = "NotificationKind", tag = "2")]
pub kind: i32,
/// Full snapshot of the torrent *after* the change. Always populated so
/// consumers don't need to fan out to `Status` for context.
#[prost(message, optional, tag = "3")]
pub torrent: ::core::option::Option<TorrentStatus>,
#[prost(oneof = "notification::Detail", tags = "10, 11, 12, 13")]
pub detail: ::core::option::Option<notification::Detail>,
}
/// Nested message and enum types in `Notification`.
pub mod notification {
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
pub enum Detail {
#[prost(message, tag = "10")]
StateChanged(super::StateChanged),
#[prost(message, tag = "11")]
Progress(super::ProgressMark),
#[prost(message, tag = "12")]
Added(super::AddedInfo),
#[prost(message, tag = "13")]
Removed(super::RemovedInfo),
}
}
impl ::prost::Name for Notification {
const NAME: &'static str = "Notification";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.Notification".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.Notification".into()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct StateChanged {
#[prost(enumeration = "State", tag = "1")]
pub previous: i32,
#[prost(enumeration = "State", tag = "2")]
pub current: i32,
}
impl ::prost::Name for StateChanged {
const NAME: &'static str = "StateChanged";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.StateChanged".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.StateChanged".into()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProgressMark {
/// Current integer percentage 0..=100.
#[prost(uint32, tag = "1")]
pub percent: u32,
/// Bytes downloaded since the previous PROGRESS event for this torrent.
#[prost(uint64, tag = "2")]
pub bytes_delta: u64,
}
impl ::prost::Name for ProgressMark {
const NAME: &'static str = "ProgressMark";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.ProgressMark".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.ProgressMark".into()
}
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct AddedInfo {
/// Source string as provided to `Add` (magnet, URL, or file path).
#[prost(string, tag = "1")]
pub source: ::prost::alloc::string::String,
}
impl ::prost::Name for AddedInfo {
const NAME: &'static str = "AddedInfo";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.AddedInfo".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.AddedInfo".into()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct RemovedInfo {
/// True if `Remove` was called with `delete_files = true`.
#[prost(bool, tag = "1")]
pub files_deleted: bool,
}
impl ::prost::Name for RemovedInfo {
const NAME: &'static str = "RemovedInfo";
const PACKAGE: &'static str = "torrent";
fn full_name() -> ::prost::alloc::string::String {
"torrent.RemovedInfo".into()
}
fn type_url() -> ::prost::alloc::string::String {
"/torrent.RemovedInfo".into()
}
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct TorrentStatus {
#[prost(string, tag = "1")]
pub id: ::prost::alloc::string::String,
@@ -203,6 +333,47 @@ impl ::prost::Name for TorrentStatus {
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum NotificationKind {
Unspecified = 0,
/// Emitted once when a torrent is added via `Add`.
TorrentAdded = 1,
/// Emitted once when a torrent is removed via `Remove`.
TorrentRemoved = 2,
/// Emitted on any `State` transition, including PAUSED <-> DOWNLOADING,
/// DOWNLOADING <-> STALE, * -> FINISHED, * -> ERROR.
StateChanged = 3,
/// Emitted when the integer download percentage changes (rate-limited).
/// No event is sent if the percentage did not advance since the last tick.
Progress = 4,
}
impl NotificationKind {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Unspecified => "NOTIFICATION_KIND_UNSPECIFIED",
Self::TorrentAdded => "TORRENT_ADDED",
Self::TorrentRemoved => "TORRENT_REMOVED",
Self::StateChanged => "STATE_CHANGED",
Self::Progress => "PROGRESS",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"NOTIFICATION_KIND_UNSPECIFIED" => Some(Self::Unspecified),
"TORRENT_ADDED" => Some(Self::TorrentAdded),
"TORRENT_REMOVED" => Some(Self::TorrentRemoved),
"STATE_CHANGED" => Some(Self::StateChanged),
"PROGRESS" => Some(Self::Progress),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum State {
Unspecified = 0,
Pending = 1,
@@ -210,6 +381,12 @@ pub enum State {
Paused = 3,
Finished = 4,
Error = 5,
/// Live per librqbit, but the daemon has detected a stall: no byte progress
/// for several ticks AND zero connected peers. The torrent is still
/// considered live by the underlying engine; this state is an application-
/// level overlay. Recovers to DOWNLOADING automatically when bytes advance
/// or a peer connects.
Stale = 6,
}
impl State {
/// String value of the enum field names used in the ProtoBuf definition.
@@ -224,6 +401,7 @@ impl State {
Self::Paused => "PAUSED",
Self::Finished => "FINISHED",
Self::Error => "ERROR",
Self::Stale => "STALE",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -235,13 +413,14 @@ impl State {
"PAUSED" => Some(Self::Paused),
"FINISHED" => Some(Self::Finished),
"ERROR" => Some(Self::Error),
"STALE" => Some(Self::Stale),
_ => None,
}
}
}
/// Encoded file descriptor set for the `torrent` package
pub const FILE_DESCRIPTOR_SET: &[u8] = &[
0x0a, 0x90, 0x1f, 0x0a, 0x0d, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f,
0x0a, 0xe0, 0x3e, 0x0a, 0x0d, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x07, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x22, 0x43, 0x0a, 0x0a, 0x41,
0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x67,
0x6e, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x61, 0x67, 0x6e, 0x65,
@@ -267,229 +446,482 @@ pub const FILE_DESCRIPTOR_SET: &[u8] = &[
0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0x0a, 0x0d, 0x52, 0x65,
0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x52,
0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xec, 0x03,
0x0a, 0x0d, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70,
0x75, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74,
0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f,
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07,
0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64,
0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53,
0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x12, 0x2c, 0x0a, 0x12, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65,
0x65, 0x64, 0x5f, 0x62, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x6f,
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73, 0x12, 0x1f,
0x0a, 0x0b, 0x65, 0x74, 0x61, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20,
0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x74, 0x61, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12,
0x25, 0x0a, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65,
0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64,
0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x62, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04,
0x52, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73,
0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x65, 0x65, 0x64, 0x73, 0x18,
0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x65, 0x65, 0x64, 0x73, 0x2a, 0x61, 0x0a, 0x05,
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07,
0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57,
0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x41,
0x55, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48,
0x45, 0x44, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x32,
0xd9, 0x02, 0x0a, 0x08, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x03,
0x41, 0x64, 0x64, 0x12, 0x13, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64,
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38,
0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74,
0x12, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a,
0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x50, 0x61, 0x75, 0x73,
0x65, 0x12, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x75, 0x73,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x39, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x74, 0x6f, 0x72,
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73,
0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4a, 0xad, 0x14, 0x0a, 0x06,
0x12, 0x04, 0x00, 0x00, 0x53, 0x01, 0x0a, 0x08, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x00, 0x00, 0x12,
0x0a, 0x08, 0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x10, 0x0a, 0x0a, 0x0a, 0x02, 0x06, 0x00,
0x12, 0x04, 0x04, 0x00, 0x11, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x04,
0x08, 0x10, 0x0a, 0x5a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x03, 0x06, 0x02, 0x2c, 0x1a,
0x4d, 0x20, 0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20,
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x20, 0x6c, 0x69,
0x6e, 0x6b, 0x20, 0x6f, 0x72, 0x20, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66,
0x69, 0x6c, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x64, 0x6f,
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x0c,
0x0a, 0x05, 0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x06, 0x06, 0x09, 0x0a, 0x0c, 0x0a, 0x05,
0x06, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x06, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00,
0x02, 0x00, 0x03, 0x12, 0x03, 0x06, 0x1f, 0x2a, 0x0a, 0x3a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01,
0x12, 0x03, 0x08, 0x02, 0x34, 0x1a, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20,
0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f,
0x66, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x08,
0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x08, 0x0d, 0x1a,
0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x08, 0x25, 0x32, 0x0a, 0x35,
0x0a, 0x04, 0x06, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x02, 0x2f, 0x1a, 0x28, 0x20, 0x4c, 0x69,
0x73, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x20,
0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65,
0x6d, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03,
0x0a, 0x06, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x0b,
0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0a, 0x21, 0x2d, 0x0a,
0x2e, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x03, 0x12, 0x03, 0x0c, 0x02, 0x35, 0x1a, 0x21, 0x20, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20,
0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x0a,
0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x0c, 0x06, 0x0c, 0x0a, 0x0c, 0x0a,
0x05, 0x06, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x0c, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06,
0x00, 0x02, 0x03, 0x03, 0x12, 0x03, 0x0c, 0x25, 0x33, 0x0a, 0x2b, 0x0a, 0x04, 0x06, 0x00, 0x02,
0x04, 0x12, 0x03, 0x0e, 0x02, 0x32, 0x1a, 0x1e, 0x20, 0x50, 0x61, 0x75, 0x73, 0x65, 0x20, 0x61,
0x20, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x72,
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x01, 0x12,
0x03, 0x0e, 0x06, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x0e,
0x0c, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x0e, 0x23, 0x30,
0x0a, 0x27, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x05, 0x12, 0x03, 0x10, 0x02, 0x35, 0x1a, 0x1a, 0x20,
0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x20, 0x61, 0x20, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x20,
0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02,
0x05, 0x01, 0x12, 0x03, 0x10, 0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x02,
0x12, 0x03, 0x10, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03,
0x10, 0x25, 0x33, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x13, 0x00, 0x17, 0x01, 0x0a,
0x0a, 0x0a, 0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x13, 0x08, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x00, 0x02, 0x00, 0x05, 0x12, 0x03, 0x14, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02,
0x00, 0x12, 0x03, 0x14, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12,
0x03, 0x14, 0x09, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x14,
0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x16, 0x02, 0x08,
0x0a, 0x53, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x16, 0x02, 0x18, 0x1a, 0x46, 0x20,
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20,
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69,
0x64, 0x65, 0x2e, 0x20, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20,
0x75, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x61, 0x64, 0x27, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61,
0x75, 0x6c, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03,
0x16, 0x09, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x16, 0x16,
0x17, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x19, 0x00, 0x1b, 0x01, 0x0a, 0x0a, 0x0a,
0x03, 0x04, 0x01, 0x01, 0x12, 0x03, 0x19, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02,
0x00, 0x05, 0x12, 0x03, 0x1a, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12,
0x03, 0x1a, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1a,
0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1a, 0x0e, 0x0f,
0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x1d, 0x00, 0x1f, 0x01, 0x0a, 0x0a, 0x0a, 0x03,
0x04, 0x02, 0x01, 0x12, 0x03, 0x1d, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00,
0x05, 0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03,
0x1e, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x09,
0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x0e, 0x0f, 0x0a,
0x09, 0x0a, 0x02, 0x04, 0x03, 0x12, 0x03, 0x21, 0x00, 0x16, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03,
0x01, 0x12, 0x03, 0x21, 0x08, 0x13, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x23, 0x00,
0x25, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x23, 0x08, 0x14, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x24, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04,
0x04, 0x04, 0x02, 0x00, 0x12, 0x03, 0x24, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02,
0x00, 0x06, 0x12, 0x03, 0x24, 0x0b, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01,
0x12, 0x03, 0x24, 0x19, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03,
0x24, 0x24, 0x25, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x27, 0x00, 0x2a, 0x01, 0x0a,
0x0a, 0x0a, 0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x27, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x05, 0x02, 0x00, 0x05, 0x12, 0x03, 0x28, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02,
0x00, 0x12, 0x03, 0x28, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12,
0x03, 0x28, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x28,
0x0e, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05, 0x12, 0x03, 0x29, 0x02, 0x06,
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x29, 0x02, 0x18, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x29, 0x07, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x05, 0x02, 0x01, 0x03, 0x12, 0x03, 0x29, 0x16, 0x17, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x06, 0x12,
0x03, 0x2c, 0x00, 0x19, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x2c, 0x08, 0x16,
0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x2e, 0x00, 0x30, 0x01, 0x0a, 0x0a, 0x0a, 0x03,
0x04, 0x07, 0x01, 0x12, 0x03, 0x2e, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00,
0x05, 0x12, 0x03, 0x2f, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03,
0x2f, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2f, 0x09,
0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2f, 0x0e, 0x0f, 0x0a,
0x09, 0x0a, 0x02, 0x04, 0x08, 0x12, 0x03, 0x32, 0x00, 0x18, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08,
0x01, 0x12, 0x03, 0x32, 0x08, 0x15, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x09, 0x12, 0x04, 0x34, 0x00,
0x36, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x34, 0x08, 0x15, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x05, 0x12, 0x03, 0x35, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04,
0x04, 0x09, 0x02, 0x00, 0x12, 0x03, 0x35, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02,
0x00, 0x01, 0x12, 0x03, 0x35, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03,
0x12, 0x03, 0x35, 0x0e, 0x0f, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x03, 0x38, 0x00, 0x19,
0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x38, 0x08, 0x16, 0x0a, 0x0a, 0x0a, 0x02,
0x05, 0x00, 0x12, 0x04, 0x3a, 0x00, 0x41, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12,
0x03, 0x3a, 0x05, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x3b,
0x02, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x3b, 0x02, 0x18, 0x0a,
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x02, 0x12, 0x03, 0x3b, 0x16, 0x17, 0x0a, 0x0c, 0x0a,
0x05, 0x05, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x3c, 0x02, 0x09, 0x0a, 0x0b, 0x0a, 0x04, 0x05,
0x00, 0x02, 0x01, 0x12, 0x03, 0x3c, 0x02, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01,
0x02, 0x12, 0x03, 0x3c, 0x0c, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12,
0x03, 0x3d, 0x02, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x3d, 0x02,
0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x3d, 0x10, 0x11, 0x0a,
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x3e, 0x02, 0x08, 0x0a, 0x0b, 0x0a,
0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x3e, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00,
0x02, 0x03, 0x02, 0x12, 0x03, 0x3e, 0x0b, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04,
0x01, 0x12, 0x03, 0x3f, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x04, 0x12, 0x03,
0x3f, 0x02, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x3f, 0x0d,
0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x05, 0x01, 0x12, 0x03, 0x40, 0x02, 0x07, 0x0a,
0x0b, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x05, 0x12, 0x03, 0x40, 0x02, 0x0c, 0x0a, 0x0c, 0x0a, 0x05,
0x05, 0x00, 0x02, 0x05, 0x02, 0x12, 0x03, 0x40, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0b,
0x12, 0x04, 0x43, 0x00, 0x53, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, 0x03, 0x43,
0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x05, 0x12, 0x03, 0x44, 0x02, 0x08,
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, 0x12, 0x03, 0x44, 0x02, 0x10, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x03, 0x44, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x0b, 0x02, 0x00, 0x03, 0x12, 0x03, 0x44, 0x0e, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
0x01, 0x05, 0x12, 0x03, 0x45, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x01, 0x12,
0x03, 0x45, 0x02, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x03, 0x45,
0x09, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x03, 0x12, 0x03, 0x45, 0x15, 0x16,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x02, 0x05, 0x12, 0x03, 0x46, 0x02, 0x08, 0x0a, 0x0b,
0x0a, 0x04, 0x04, 0x0b, 0x02, 0x02, 0x12, 0x03, 0x46, 0x02, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x0b, 0x02, 0x02, 0x01, 0x12, 0x03, 0x46, 0x09, 0x0d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
0x02, 0x03, 0x12, 0x03, 0x46, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x05,
0x12, 0x03, 0x47, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x03, 0x12, 0x03, 0x47,
0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x01, 0x12, 0x03, 0x47, 0x09, 0x0f,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x03, 0x03, 0x12, 0x03, 0x47, 0x12, 0x13, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x04, 0x05, 0x12, 0x03, 0x48, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04,
0x04, 0x0b, 0x02, 0x04, 0x12, 0x03, 0x48, 0x02, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02,
0x04, 0x01, 0x12, 0x03, 0x48, 0x09, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x04, 0x03,
0x12, 0x03, 0x48, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x05, 0x12, 0x03,
0x49, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x05, 0x12, 0x03, 0x49, 0x02, 0x19,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x01, 0x12, 0x03, 0x49, 0x09, 0x14, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x05, 0x03, 0x12, 0x03, 0x49, 0x17, 0x18, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x0b, 0x02, 0x06, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b,
0x02, 0x06, 0x12, 0x03, 0x4a, 0x02, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x06, 0x01,
0x12, 0x03, 0x4a, 0x09, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x06, 0x03, 0x12, 0x03,
0x4a, 0x1c, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x07, 0x06, 0x12, 0x03, 0x4b, 0x02,
0x07, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x07, 0x12, 0x03, 0x4b, 0x02, 0x12, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x07, 0x01, 0x12, 0x03, 0x4b, 0x08, 0x0d, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x0b, 0x02, 0x07, 0x03, 0x12, 0x03, 0x4b, 0x10, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
0x02, 0x08, 0x05, 0x12, 0x03, 0x4c, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x08,
0x12, 0x03, 0x4c, 0x02, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x08, 0x01, 0x12, 0x03,
0x4c, 0x09, 0x16, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x08, 0x03, 0x12, 0x03, 0x4c, 0x19,
0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x09, 0x05, 0x12, 0x03, 0x4d, 0x02, 0x08, 0x0a,
0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x09, 0x12, 0x03, 0x4d, 0x02, 0x21, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x0b, 0x02, 0x09, 0x01, 0x12, 0x03, 0x4d, 0x09, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
0x02, 0x09, 0x03, 0x12, 0x03, 0x4d, 0x1e, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a,
0x05, 0x12, 0x03, 0x4e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0a, 0x12, 0x03,
0x4e, 0x02, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a, 0x01, 0x12, 0x03, 0x4e, 0x09,
0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0a, 0x03, 0x12, 0x03, 0x4e, 0x17, 0x19, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0b, 0x05, 0x12, 0x03, 0x4f, 0x02, 0x08, 0x0a, 0x0b, 0x0a,
0x04, 0x04, 0x0b, 0x02, 0x0b, 0x12, 0x03, 0x4f, 0x02, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b,
0x02, 0x0b, 0x01, 0x12, 0x03, 0x4f, 0x09, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0b,
0x03, 0x12, 0x03, 0x4f, 0x1a, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x05, 0x12,
0x03, 0x50, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0c, 0x12, 0x03, 0x50, 0x02,
0x1f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x01, 0x12, 0x03, 0x50, 0x09, 0x19, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0c, 0x03, 0x12, 0x03, 0x50, 0x1c, 0x1e, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x0b, 0x02, 0x0d, 0x05, 0x12, 0x03, 0x51, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04,
0x0b, 0x02, 0x0d, 0x12, 0x03, 0x51, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0d,
0x01, 0x12, 0x03, 0x51, 0x09, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0d, 0x03, 0x12,
0x03, 0x51, 0x11, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0e, 0x05, 0x12, 0x03, 0x52,
0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x0e, 0x12, 0x03, 0x52, 0x02, 0x14, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x0e, 0x01, 0x12, 0x03, 0x52, 0x09, 0x0e, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x0b, 0x02, 0x0e, 0x03, 0x12, 0x03, 0x52, 0x11, 0x13, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x59, 0x0a,
0x14, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03,
0x28, 0x09, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e,
0x64, 0x52, 0x05, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x22, 0x81, 0x03, 0x0a, 0x0c, 0x4e, 0x6f, 0x74,
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x17, 0x6f, 0x62, 0x73,
0x65, 0x72, 0x76, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x6d, 0x69,
0x6c, 0x6c, 0x69, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x6f, 0x62, 0x73, 0x65,
0x72, 0x76, 0x65, 0x64, 0x41, 0x74, 0x55, 0x6e, 0x69, 0x78, 0x4d, 0x69, 0x6c, 0x6c, 0x69, 0x73,
0x12, 0x2d, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19,
0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12,
0x30, 0x0a, 0x07, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x48,
0x00, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12,
0x33, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x67,
0x72, 0x65, 0x73, 0x73, 0x4d, 0x61, 0x72, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67,
0x72, 0x65, 0x73, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x0c, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64,
0x64, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64,
0x12, 0x30, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76,
0x65, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x22, 0x64, 0x0a, 0x0c,
0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x08,
0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e,
0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08,
0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x07, 0x63, 0x75, 0x72, 0x72,
0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x74, 0x6f, 0x72, 0x72,
0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x07, 0x63, 0x75, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x22, 0x49, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4d, 0x61,
0x72, 0x6b, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b,
0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28,
0x04, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x44, 0x65, 0x6c, 0x74, 0x61, 0x22, 0x23, 0x0a,
0x09, 0x41, 0x64, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
0x63, 0x65, 0x22, 0x32, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x22, 0xec, 0x03, 0x0a, 0x0d, 0x54, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x66, 0x6f,
0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6e, 0x66,
0x6f, 0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x50, 0x61,
0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79,
0x74, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65,
0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x64,
0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x24,
0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e,
0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73,
0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x64, 0x6f, 0x77,
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f, 0x62, 0x70, 0x73, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x53,
0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x74, 0x61, 0x5f, 0x73,
0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x74,
0x61, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x75, 0x70, 0x6c, 0x6f,
0x61, 0x64, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04,
0x52, 0x0d, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12,
0x28, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x70, 0x65, 0x65, 0x64, 0x5f,
0x62, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x70, 0x6c, 0x6f, 0x61,
0x64, 0x53, 0x70, 0x65, 0x65, 0x64, 0x42, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x65, 0x65,
0x72, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12,
0x14, 0x0a, 0x05, 0x73, 0x65, 0x65, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05,
0x73, 0x65, 0x65, 0x64, 0x73, 0x2a, 0x7e, 0x0a, 0x10, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x1d, 0x4e, 0x4f, 0x54,
0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x49, 0x4e, 0x44, 0x5f, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d,
0x54, 0x4f, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x41, 0x44, 0x44, 0x45, 0x44, 0x10, 0x01, 0x12,
0x13, 0x0a, 0x0f, 0x54, 0x4f, 0x52, 0x52, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x56,
0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x48,
0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x4f, 0x47, 0x52,
0x45, 0x53, 0x53, 0x10, 0x04, 0x2a, 0x6c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x15,
0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46,
0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47,
0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x49, 0x4e,
0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12,
0x0c, 0x0a, 0x08, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x04, 0x12, 0x09, 0x0a,
0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x4c,
0x45, 0x10, 0x06, 0x32, 0xa2, 0x03, 0x0a, 0x08, 0x54, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73,
0x12, 0x30, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x13, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74,
0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x38, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x74,
0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x54,
0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x33, 0x0a, 0x04,
0x4c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x74, 0x6f, 0x72,
0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x39, 0x0a, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x16, 0x2e, 0x74, 0x6f,
0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65,
0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x05,
0x50, 0x61, 0x75, 0x73, 0x65, 0x12, 0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e,
0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x74,
0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x61, 0x75, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x12, 0x16,
0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x2e, 0x52, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x47, 0x0a, 0x0d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x12, 0x1d, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x15, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x01, 0x4a, 0xc0, 0x2c, 0x0a, 0x07, 0x12, 0x05, 0x00,
0x00, 0x9c, 0x01, 0x01, 0x0a, 0x08, 0x0a, 0x01, 0x0c, 0x12, 0x03, 0x00, 0x00, 0x12, 0x0a, 0x08,
0x0a, 0x01, 0x02, 0x12, 0x03, 0x02, 0x00, 0x10, 0x0a, 0x0a, 0x0a, 0x02, 0x06, 0x00, 0x12, 0x04,
0x04, 0x00, 0x15, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x06, 0x00, 0x01, 0x12, 0x03, 0x04, 0x08, 0x10,
0x0a, 0x5a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x00, 0x12, 0x03, 0x06, 0x02, 0x2c, 0x1a, 0x4d, 0x20,
0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72,
0x6f, 0x6d, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x65, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b,
0x20, 0x6f, 0x72, 0x20, 0x2e, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x69, 0x6c,
0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x20, 0x64, 0x6f, 0x77, 0x6e,
0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05,
0x06, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x06, 0x06, 0x09, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00,
0x02, 0x00, 0x02, 0x12, 0x03, 0x06, 0x0a, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x00,
0x03, 0x12, 0x03, 0x06, 0x1f, 0x2a, 0x0a, 0x3a, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x01, 0x12, 0x03,
0x08, 0x02, 0x34, 0x1a, 0x2d, 0x20, 0x47, 0x65, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x75,
0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x6f, 0x66, 0x20,
0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x08, 0x06, 0x0c,
0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x02, 0x12, 0x03, 0x08, 0x0d, 0x1a, 0x0a, 0x0c,
0x0a, 0x05, 0x06, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x08, 0x25, 0x32, 0x0a, 0x35, 0x0a, 0x04,
0x06, 0x00, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x02, 0x2f, 0x1a, 0x28, 0x20, 0x4c, 0x69, 0x73, 0x74,
0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6b, 0x6e,
0x6f, 0x77, 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x0a, 0x06,
0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x02, 0x12, 0x03, 0x0a, 0x0b, 0x16, 0x0a,
0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x02, 0x03, 0x12, 0x03, 0x0a, 0x21, 0x2d, 0x0a, 0x2e, 0x0a,
0x04, 0x06, 0x00, 0x02, 0x03, 0x12, 0x03, 0x0c, 0x02, 0x35, 0x1a, 0x21, 0x20, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72,
0x6f, 0x6d, 0x20, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a,
0x05, 0x06, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03, 0x0c, 0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06,
0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x0c, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02,
0x03, 0x03, 0x12, 0x03, 0x0c, 0x25, 0x33, 0x0a, 0x2b, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x04, 0x12,
0x03, 0x0e, 0x02, 0x32, 0x1a, 0x1e, 0x20, 0x50, 0x61, 0x75, 0x73, 0x65, 0x20, 0x61, 0x20, 0x64,
0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x01, 0x12, 0x03, 0x0e,
0x06, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x02, 0x12, 0x03, 0x0e, 0x0c, 0x18,
0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x04, 0x03, 0x12, 0x03, 0x0e, 0x23, 0x30, 0x0a, 0x27,
0x0a, 0x04, 0x06, 0x00, 0x02, 0x05, 0x12, 0x03, 0x10, 0x02, 0x35, 0x1a, 0x1a, 0x20, 0x52, 0x65,
0x73, 0x75, 0x6d, 0x65, 0x20, 0x61, 0x20, 0x70, 0x61, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f,
0x72, 0x72, 0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x01,
0x12, 0x03, 0x10, 0x06, 0x0c, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x02, 0x12, 0x03,
0x10, 0x0d, 0x1a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x05, 0x03, 0x12, 0x03, 0x10, 0x25,
0x33, 0x0a, 0xd5, 0x01, 0x0a, 0x04, 0x06, 0x00, 0x02, 0x06, 0x12, 0x03, 0x14, 0x02, 0x48, 0x1a,
0xc7, 0x01, 0x20, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x20, 0x74, 0x6f, 0x20,
0x61, 0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x20, 0x6f, 0x66,
0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x20, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x61,
0x72, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x66, 0x72,
0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
0x20, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x77, 0x61, 0x72, 0x64, 0x20, 0xe2, 0x80,
0x94, 0x20, 0x6e, 0x6f, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x69, 0x73, 0x20,
0x72, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x2e, 0x20, 0x4e, 0x65, 0x77, 0x20, 0x73, 0x75,
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x0a, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20,
0x73, 0x65, 0x65, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20,
0x66, 0x69, 0x72, 0x65, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20,
0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02,
0x06, 0x01, 0x12, 0x03, 0x14, 0x06, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x02,
0x12, 0x03, 0x14, 0x14, 0x28, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x06, 0x12, 0x03,
0x14, 0x33, 0x39, 0x0a, 0x0c, 0x0a, 0x05, 0x06, 0x00, 0x02, 0x06, 0x03, 0x12, 0x03, 0x14, 0x3a,
0x46, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x00, 0x12, 0x04, 0x17, 0x00, 0x1b, 0x01, 0x0a, 0x0a, 0x0a,
0x03, 0x04, 0x00, 0x01, 0x12, 0x03, 0x17, 0x08, 0x12, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02,
0x00, 0x05, 0x12, 0x03, 0x18, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x00, 0x02, 0x00, 0x12,
0x03, 0x18, 0x02, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x18,
0x09, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x00, 0x03, 0x12, 0x03, 0x18, 0x12, 0x13,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x05, 0x12, 0x03, 0x1a, 0x02, 0x08, 0x0a, 0x53,
0x0a, 0x04, 0x04, 0x00, 0x02, 0x01, 0x12, 0x03, 0x1a, 0x02, 0x18, 0x1a, 0x46, 0x20, 0x4f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65,
0x2e, 0x20, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x73, 0x20, 0x75, 0x73,
0x65, 0x20, 0x74, 0x6f, 0x72, 0x61, 0x64, 0x27, 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x01, 0x12, 0x03, 0x1a, 0x09,
0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x00, 0x02, 0x01, 0x03, 0x12, 0x03, 0x1a, 0x16, 0x17, 0x0a,
0x0a, 0x0a, 0x02, 0x04, 0x01, 0x12, 0x04, 0x1d, 0x00, 0x1f, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04,
0x01, 0x01, 0x12, 0x03, 0x1d, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x05,
0x12, 0x03, 0x1e, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x01, 0x02, 0x00, 0x12, 0x03, 0x1e,
0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x01, 0x12, 0x03, 0x1e, 0x09, 0x0b,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x01, 0x02, 0x00, 0x03, 0x12, 0x03, 0x1e, 0x0e, 0x0f, 0x0a, 0x0a,
0x0a, 0x02, 0x04, 0x02, 0x12, 0x04, 0x21, 0x00, 0x23, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x02,
0x01, 0x12, 0x03, 0x21, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x05, 0x12,
0x03, 0x22, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x02, 0x02, 0x00, 0x12, 0x03, 0x22, 0x02,
0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x01, 0x12, 0x03, 0x22, 0x09, 0x0b, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x02, 0x02, 0x00, 0x03, 0x12, 0x03, 0x22, 0x0e, 0x0f, 0x0a, 0x09, 0x0a,
0x02, 0x04, 0x03, 0x12, 0x03, 0x25, 0x00, 0x16, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x03, 0x01, 0x12,
0x03, 0x25, 0x08, 0x13, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x04, 0x12, 0x04, 0x27, 0x00, 0x29, 0x01,
0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x04, 0x01, 0x12, 0x03, 0x27, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x04, 0x02, 0x00, 0x04, 0x12, 0x03, 0x28, 0x02, 0x0a, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x04,
0x02, 0x00, 0x12, 0x03, 0x28, 0x02, 0x26, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x06,
0x12, 0x03, 0x28, 0x0b, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x01, 0x12, 0x03,
0x28, 0x19, 0x21, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x04, 0x02, 0x00, 0x03, 0x12, 0x03, 0x28, 0x24,
0x25, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x05, 0x12, 0x04, 0x2b, 0x00, 0x2e, 0x01, 0x0a, 0x0a, 0x0a,
0x03, 0x04, 0x05, 0x01, 0x12, 0x03, 0x2b, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02,
0x00, 0x05, 0x12, 0x03, 0x2c, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x05, 0x02, 0x00, 0x12,
0x03, 0x2c, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x01, 0x12, 0x03, 0x2c,
0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x00, 0x03, 0x12, 0x03, 0x2c, 0x0e, 0x0f,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02, 0x01, 0x05, 0x12, 0x03, 0x2d, 0x02, 0x06, 0x0a, 0x0b,
0x0a, 0x04, 0x04, 0x05, 0x02, 0x01, 0x12, 0x03, 0x2d, 0x02, 0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x05, 0x02, 0x01, 0x01, 0x12, 0x03, 0x2d, 0x07, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x05, 0x02,
0x01, 0x03, 0x12, 0x03, 0x2d, 0x16, 0x17, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x06, 0x12, 0x03, 0x30,
0x00, 0x19, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x06, 0x01, 0x12, 0x03, 0x30, 0x08, 0x16, 0x0a, 0x0a,
0x0a, 0x02, 0x04, 0x07, 0x12, 0x04, 0x32, 0x00, 0x34, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x07,
0x01, 0x12, 0x03, 0x32, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x05, 0x12,
0x03, 0x33, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x07, 0x02, 0x00, 0x12, 0x03, 0x33, 0x02,
0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x01, 0x12, 0x03, 0x33, 0x09, 0x0b, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x07, 0x02, 0x00, 0x03, 0x12, 0x03, 0x33, 0x0e, 0x0f, 0x0a, 0x09, 0x0a,
0x02, 0x04, 0x08, 0x12, 0x03, 0x36, 0x00, 0x18, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x08, 0x01, 0x12,
0x03, 0x36, 0x08, 0x15, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x09, 0x12, 0x04, 0x38, 0x00, 0x3a, 0x01,
0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x09, 0x01, 0x12, 0x03, 0x38, 0x08, 0x15, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x09, 0x02, 0x00, 0x05, 0x12, 0x03, 0x39, 0x02, 0x08, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x09,
0x02, 0x00, 0x12, 0x03, 0x39, 0x02, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x01,
0x12, 0x03, 0x39, 0x09, 0x0b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x09, 0x02, 0x00, 0x03, 0x12, 0x03,
0x39, 0x0e, 0x0f, 0x0a, 0x09, 0x0a, 0x02, 0x04, 0x0a, 0x12, 0x03, 0x3c, 0x00, 0x19, 0x0a, 0x0a,
0x0a, 0x03, 0x04, 0x0a, 0x01, 0x12, 0x03, 0x3c, 0x08, 0x16, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0b,
0x12, 0x04, 0x3e, 0x00, 0x45, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0b, 0x01, 0x12, 0x03, 0x3e,
0x08, 0x1c, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x04, 0x12, 0x03, 0x41, 0x02, 0x0a,
0x0a, 0x81, 0x01, 0x0a, 0x04, 0x04, 0x0b, 0x02, 0x00, 0x12, 0x03, 0x41, 0x02, 0x1a, 0x1a, 0x74,
0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, 0x6e, 0x2d, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c, 0x20, 0x6f,
0x6e, 0x6c, 0x79, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
0x68, 0x65, 0x73, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x64, 0x73,
0x20, 0x28, 0x6f, 0x72, 0x20, 0x69, 0x64, 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73,
0x2c, 0x20, 0x61, 0x73, 0x0a, 0x20, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x20, 0x62,
0x79, 0x20, 0x60, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x60, 0x2f, 0x60, 0x52, 0x65, 0x6d, 0x6f,
0x76, 0x65, 0x60, 0x29, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72,
0x65, 0x64, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x05, 0x12, 0x03, 0x41,
0x0b, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x01, 0x12, 0x03, 0x41, 0x12, 0x15,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x00, 0x03, 0x12, 0x03, 0x41, 0x18, 0x19, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x04, 0x12, 0x03, 0x44, 0x02, 0x0a, 0x0a, 0x62, 0x0a, 0x04,
0x04, 0x0b, 0x02, 0x01, 0x12, 0x03, 0x44, 0x02, 0x26, 0x1a, 0x55, 0x20, 0x49, 0x66, 0x20, 0x6e,
0x6f, 0x6e, 0x2d, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74,
0x68, 0x65, 0x73, 0x65, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x73,
0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x65, 0x64, 0x2e, 0x20,
0x41, 0x6e, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6d, 0x65,
0x61, 0x6e, 0x73, 0x0a, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x2e, 0x0a,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x06, 0x12, 0x03, 0x44, 0x0b, 0x1b, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0b, 0x02, 0x01, 0x01, 0x12, 0x03, 0x44, 0x1c, 0x21, 0x0a, 0x0c, 0x0a, 0x05,
0x04, 0x0b, 0x02, 0x01, 0x03, 0x12, 0x03, 0x44, 0x24, 0x25, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0c,
0x12, 0x04, 0x47, 0x00, 0x57, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0c, 0x01, 0x12, 0x03, 0x47,
0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x05, 0x12, 0x03, 0x4a, 0x02, 0x08,
0x0a, 0x65, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x00, 0x12, 0x03, 0x4a, 0x02, 0x25, 0x1a, 0x58, 0x20,
0x57, 0x61, 0x6c, 0x6c, 0x2d, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20,
0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x20, 0x6f, 0x62, 0x73, 0x65, 0x72,
0x76, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2c, 0x20,
0x69, 0x6e, 0x20, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x20,
0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x55, 0x6e, 0x69, 0x78, 0x20,
0x65, 0x70, 0x6f, 0x63, 0x68, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x01,
0x12, 0x03, 0x4a, 0x09, 0x20, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x00, 0x03, 0x12, 0x03,
0x4a, 0x23, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x06, 0x12, 0x03, 0x4d, 0x02,
0x12, 0x0a, 0x65, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x01, 0x12, 0x03, 0x4d, 0x02, 0x1c, 0x1a, 0x58,
0x20, 0x57, 0x68, 0x61, 0x74, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x20, 0x55,
0x73, 0x65, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x69, 0x73, 0x70, 0x61,
0x74, 0x63, 0x68, 0x3b, 0x20, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x6f, 0x6e,
0x65, 0x6f, 0x66, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x60, 0x20, 0x66, 0x6f, 0x72, 0x0a,
0x20, 0x6b, 0x69, 0x6e, 0x64, 0x2d, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x70,
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01,
0x01, 0x12, 0x03, 0x4d, 0x13, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x01, 0x03, 0x12,
0x03, 0x4d, 0x1a, 0x1b, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x02, 0x06, 0x12, 0x03, 0x50,
0x02, 0x0f, 0x0a, 0x8e, 0x01, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x02, 0x12, 0x03, 0x50, 0x02, 0x1c,
0x1a, 0x80, 0x01, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x20, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x20, 0x2a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x2a, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x2e, 0x20, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x70, 0x6f, 0x70, 0x75,
0x6c, 0x61, 0x74, 0x65, 0x64, 0x20, 0x73, 0x6f, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d,
0x65, 0x72, 0x73, 0x20, 0x64, 0x6f, 0x6e, 0x27, 0x74, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74,
0x6f, 0x20, 0x66, 0x61, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x60, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x60, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78,
0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x02, 0x01, 0x12, 0x03, 0x50, 0x10,
0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x02, 0x03, 0x12, 0x03, 0x50, 0x1a, 0x1b, 0x0a,
0x0c, 0x0a, 0x04, 0x04, 0x0c, 0x08, 0x00, 0x12, 0x04, 0x51, 0x02, 0x56, 0x03, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x0c, 0x08, 0x00, 0x01, 0x12, 0x03, 0x51, 0x08, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x0c, 0x02, 0x03, 0x06, 0x12, 0x03, 0x52, 0x04, 0x10, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0c, 0x02,
0x03, 0x12, 0x03, 0x52, 0x04, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x03, 0x01, 0x12,
0x03, 0x52, 0x11, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x03, 0x03, 0x12, 0x03, 0x52,
0x21, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x04, 0x06, 0x12, 0x03, 0x53, 0x04, 0x10,
0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x04, 0x12, 0x03, 0x53, 0x04, 0x1f, 0x0a, 0x0c, 0x0a,
0x05, 0x04, 0x0c, 0x02, 0x04, 0x01, 0x12, 0x03, 0x53, 0x11, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x0c, 0x02, 0x04, 0x03, 0x12, 0x03, 0x53, 0x1c, 0x1e, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02,
0x05, 0x06, 0x12, 0x03, 0x54, 0x04, 0x0d, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0c, 0x02, 0x05, 0x12,
0x03, 0x54, 0x04, 0x19, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x05, 0x01, 0x12, 0x03, 0x54,
0x0e, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x05, 0x03, 0x12, 0x03, 0x54, 0x16, 0x18,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02, 0x06, 0x06, 0x12, 0x03, 0x55, 0x04, 0x0f, 0x0a, 0x0b,
0x0a, 0x04, 0x04, 0x0c, 0x02, 0x06, 0x12, 0x03, 0x55, 0x04, 0x1d, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x0c, 0x02, 0x06, 0x01, 0x12, 0x03, 0x55, 0x10, 0x17, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0c, 0x02,
0x06, 0x03, 0x12, 0x03, 0x55, 0x1a, 0x1c, 0x0a, 0x0a, 0x0a, 0x02, 0x05, 0x00, 0x12, 0x04, 0x59,
0x00, 0x65, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x00, 0x01, 0x12, 0x03, 0x59, 0x05, 0x15, 0x0a,
0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x00, 0x01, 0x12, 0x03, 0x5a, 0x02, 0x1f, 0x0a, 0x0b, 0x0a,
0x04, 0x05, 0x00, 0x02, 0x00, 0x12, 0x03, 0x5a, 0x02, 0x24, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00,
0x02, 0x00, 0x02, 0x12, 0x03, 0x5a, 0x22, 0x23, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01,
0x01, 0x12, 0x03, 0x5c, 0x02, 0x0f, 0x0a, 0x3e, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x01, 0x12, 0x03,
0x5c, 0x02, 0x14, 0x1a, 0x31, 0x20, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e,
0x63, 0x65, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x64, 0x64, 0x65, 0x64, 0x20, 0x76, 0x69, 0x61, 0x20, 0x60,
0x41, 0x64, 0x64, 0x60, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x01, 0x02, 0x12,
0x03, 0x5c, 0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x01, 0x12, 0x03, 0x5e,
0x02, 0x11, 0x0a, 0x43, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x02, 0x12, 0x03, 0x5e, 0x02, 0x16, 0x1a,
0x36, 0x20, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x63, 0x65, 0x20, 0x77,
0x68, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73,
0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x76, 0x69, 0x61, 0x20, 0x60, 0x52, 0x65,
0x6d, 0x6f, 0x76, 0x65, 0x60, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x02, 0x02,
0x12, 0x03, 0x5e, 0x14, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x03, 0x01, 0x12, 0x03,
0x61, 0x02, 0x0f, 0x0a, 0x86, 0x01, 0x0a, 0x04, 0x05, 0x00, 0x02, 0x03, 0x12, 0x03, 0x61, 0x02,
0x14, 0x1a, 0x79, 0x20, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x61,
0x6e, 0x79, 0x20, 0x60, 0x53, 0x74, 0x61, 0x74, 0x65, 0x60, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e, 0x67,
0x20, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x20, 0x3c, 0x2d, 0x3e, 0x20, 0x44, 0x4f, 0x57, 0x4e,
0x4c, 0x4f, 0x41, 0x44, 0x49, 0x4e, 0x47, 0x2c, 0x0a, 0x20, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f,
0x41, 0x44, 0x49, 0x4e, 0x47, 0x20, 0x3c, 0x2d, 0x3e, 0x20, 0x53, 0x54, 0x41, 0x4c, 0x45, 0x2c,
0x20, 0x2a, 0x20, 0x2d, 0x3e, 0x20, 0x46, 0x49, 0x4e, 0x49, 0x53, 0x48, 0x45, 0x44, 0x2c, 0x20,
0x2a, 0x20, 0x2d, 0x3e, 0x20, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05,
0x05, 0x00, 0x02, 0x03, 0x02, 0x12, 0x03, 0x61, 0x12, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00,
0x02, 0x04, 0x01, 0x12, 0x03, 0x64, 0x02, 0x0a, 0x0a, 0x9d, 0x01, 0x0a, 0x04, 0x05, 0x00, 0x02,
0x04, 0x12, 0x03, 0x64, 0x02, 0x0f, 0x1a, 0x8f, 0x01, 0x20, 0x45, 0x6d, 0x69, 0x74, 0x74, 0x65,
0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67,
0x65, 0x72, 0x20, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x70, 0x65, 0x72, 0x63,
0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x20, 0x28,
0x72, 0x61, 0x74, 0x65, 0x2d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x2e, 0x0a, 0x20,
0x4e, 0x6f, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x73, 0x65, 0x6e, 0x74,
0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61,
0x67, 0x65, 0x20, 0x64, 0x69, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x64, 0x76, 0x61, 0x6e,
0x63, 0x65, 0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x73,
0x74, 0x20, 0x74, 0x69, 0x63, 0x6b, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x00, 0x02, 0x04,
0x02, 0x12, 0x03, 0x64, 0x0d, 0x0e, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0d, 0x12, 0x04, 0x67, 0x00,
0x6a, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0d, 0x01, 0x12, 0x03, 0x67, 0x08, 0x14, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x06, 0x12, 0x03, 0x68, 0x02, 0x07, 0x0a, 0x0b, 0x0a, 0x04,
0x04, 0x0d, 0x02, 0x00, 0x12, 0x03, 0x68, 0x02, 0x15, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02,
0x00, 0x01, 0x12, 0x03, 0x68, 0x08, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x00, 0x03,
0x12, 0x03, 0x68, 0x13, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, 0x06, 0x12, 0x03,
0x69, 0x02, 0x07, 0x0a, 0x0b, 0x0a, 0x04, 0x04, 0x0d, 0x02, 0x01, 0x12, 0x03, 0x69, 0x02, 0x14,
0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, 0x01, 0x12, 0x03, 0x69, 0x08, 0x0f, 0x0a, 0x0c,
0x0a, 0x05, 0x04, 0x0d, 0x02, 0x01, 0x03, 0x12, 0x03, 0x69, 0x12, 0x13, 0x0a, 0x0a, 0x0a, 0x02,
0x04, 0x0e, 0x12, 0x04, 0x6c, 0x00, 0x71, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x04, 0x0e, 0x01, 0x12,
0x03, 0x6c, 0x08, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x05, 0x12, 0x03, 0x6e,
0x02, 0x08, 0x0a, 0x32, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x00, 0x12, 0x03, 0x6e, 0x02, 0x15, 0x1a,
0x25, 0x20, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x65,
0x72, 0x20, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x20, 0x30, 0x2e, 0x2e,
0x3d, 0x31, 0x30, 0x30, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x01, 0x12,
0x03, 0x6e, 0x09, 0x10, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x00, 0x03, 0x12, 0x03, 0x6e,
0x13, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x05, 0x12, 0x03, 0x70, 0x02, 0x08,
0x0a, 0x53, 0x0a, 0x04, 0x04, 0x0e, 0x02, 0x01, 0x12, 0x03, 0x70, 0x02, 0x19, 0x1a, 0x46, 0x20,
0x42, 0x79, 0x74, 0x65, 0x73, 0x20, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64,
0x20, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69,
0x6f, 0x75, 0x73, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x20, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x74, 0x6f, 0x72, 0x72,
0x65, 0x6e, 0x74, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x01, 0x12, 0x03,
0x70, 0x09, 0x14, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0e, 0x02, 0x01, 0x03, 0x12, 0x03, 0x70, 0x17,
0x18, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x0f, 0x12, 0x04, 0x73, 0x00, 0x76, 0x01, 0x0a, 0x0a, 0x0a,
0x03, 0x04, 0x0f, 0x01, 0x12, 0x03, 0x73, 0x08, 0x11, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0f, 0x02,
0x00, 0x05, 0x12, 0x03, 0x75, 0x02, 0x08, 0x0a, 0x4e, 0x0a, 0x04, 0x04, 0x0f, 0x02, 0x00, 0x12,
0x03, 0x75, 0x02, 0x14, 0x1a, 0x41, 0x20, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x73, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64,
0x20, 0x74, 0x6f, 0x20, 0x60, 0x41, 0x64, 0x64, 0x60, 0x20, 0x28, 0x6d, 0x61, 0x67, 0x6e, 0x65,
0x74, 0x2c, 0x20, 0x55, 0x52, 0x4c, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
0x70, 0x61, 0x74, 0x68, 0x29, 0x2e, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x01,
0x12, 0x03, 0x75, 0x09, 0x0f, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x0f, 0x02, 0x00, 0x03, 0x12, 0x03,
0x75, 0x12, 0x13, 0x0a, 0x0a, 0x0a, 0x02, 0x04, 0x10, 0x12, 0x04, 0x78, 0x00, 0x7b, 0x01, 0x0a,
0x0a, 0x0a, 0x03, 0x04, 0x10, 0x01, 0x12, 0x03, 0x78, 0x08, 0x13, 0x0a, 0x0c, 0x0a, 0x05, 0x04,
0x10, 0x02, 0x00, 0x05, 0x12, 0x03, 0x7a, 0x02, 0x06, 0x0a, 0x46, 0x0a, 0x04, 0x04, 0x10, 0x02,
0x00, 0x12, 0x03, 0x7a, 0x02, 0x19, 0x1a, 0x39, 0x20, 0x54, 0x72, 0x75, 0x65, 0x20, 0x69, 0x66,
0x20, 0x60, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x60, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x61,
0x6c, 0x6c, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x60, 0x64, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x60, 0x2e,
0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x01, 0x12, 0x03, 0x7a, 0x07, 0x14, 0x0a,
0x0c, 0x0a, 0x05, 0x04, 0x10, 0x02, 0x00, 0x03, 0x12, 0x03, 0x7a, 0x17, 0x18, 0x0a, 0x0b, 0x0a,
0x02, 0x05, 0x01, 0x12, 0x05, 0x7d, 0x00, 0x8a, 0x01, 0x01, 0x0a, 0x0a, 0x0a, 0x03, 0x05, 0x01,
0x01, 0x12, 0x03, 0x7d, 0x05, 0x0a, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x00, 0x01, 0x12,
0x03, 0x7e, 0x02, 0x13, 0x0a, 0x0b, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x00, 0x12, 0x03, 0x7e, 0x02,
0x18, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x00, 0x02, 0x12, 0x03, 0x7e, 0x16, 0x17, 0x0a,
0x0c, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x01, 0x01, 0x12, 0x03, 0x7f, 0x02, 0x09, 0x0a, 0x0b, 0x0a,
0x04, 0x05, 0x01, 0x02, 0x01, 0x12, 0x03, 0x7f, 0x02, 0x0e, 0x0a, 0x0c, 0x0a, 0x05, 0x05, 0x01,
0x02, 0x01, 0x02, 0x12, 0x03, 0x7f, 0x0c, 0x0d, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02,
0x01, 0x12, 0x04, 0x80, 0x01, 0x02, 0x0d, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x02, 0x12,
0x04, 0x80, 0x01, 0x02, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x02, 0x02, 0x12, 0x04,
0x80, 0x01, 0x10, 0x11, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x01, 0x12, 0x04, 0x81,
0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x03, 0x12, 0x04, 0x81, 0x01, 0x02,
0x0d, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x03, 0x02, 0x12, 0x04, 0x81, 0x01, 0x0b, 0x0c,
0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x04, 0x01, 0x12, 0x04, 0x82, 0x01, 0x02, 0x0a, 0x0a,
0x0c, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x04, 0x12, 0x04, 0x82, 0x01, 0x02, 0x0f, 0x0a, 0x0d, 0x0a,
0x05, 0x05, 0x01, 0x02, 0x04, 0x02, 0x12, 0x04, 0x82, 0x01, 0x0d, 0x0e, 0x0a, 0x0d, 0x0a, 0x05,
0x05, 0x01, 0x02, 0x05, 0x01, 0x12, 0x04, 0x83, 0x01, 0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x04, 0x05,
0x01, 0x02, 0x05, 0x12, 0x04, 0x83, 0x01, 0x02, 0x0c, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02,
0x05, 0x02, 0x12, 0x04, 0x83, 0x01, 0x0a, 0x0b, 0x0a, 0x0d, 0x0a, 0x05, 0x05, 0x01, 0x02, 0x06,
0x01, 0x12, 0x04, 0x89, 0x01, 0x02, 0x07, 0x0a, 0xc2, 0x02, 0x0a, 0x04, 0x05, 0x01, 0x02, 0x06,
0x12, 0x04, 0x89, 0x01, 0x02, 0x0c, 0x1a, 0xb3, 0x02, 0x20, 0x4c, 0x69, 0x76, 0x65, 0x20, 0x70,
0x65, 0x72, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x71, 0x62, 0x69, 0x74, 0x2c, 0x20, 0x62, 0x75, 0x74,
0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x73, 0x20,
0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x61, 0x20, 0x73, 0x74, 0x61, 0x6c, 0x6c,
0x3a, 0x20, 0x6e, 0x6f, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65,
0x73, 0x73, 0x0a, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x76, 0x65, 0x72, 0x61, 0x6c, 0x20,
0x74, 0x69, 0x63, 0x6b, 0x73, 0x20, 0x41, 0x4e, 0x44, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x63,
0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x70, 0x65, 0x65, 0x72, 0x73, 0x2e, 0x20,
0x54, 0x68, 0x65, 0x20, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x73, 0x20, 0x73,
0x74, 0x69, 0x6c, 0x6c, 0x0a, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x65, 0x64,
0x20, 0x6c, 0x69, 0x76, 0x65, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x75, 0x6e, 0x64,
0x65, 0x72, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x3b, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e,
0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x0a, 0x20, 0x6c,
0x65, 0x76, 0x65, 0x6c, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x20, 0x52, 0x65,
0x63, 0x6f, 0x76, 0x65, 0x72, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f,
0x41, 0x44, 0x49, 0x4e, 0x47, 0x20, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x61,
0x6c, 0x6c, 0x79, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x61,
0x64, 0x76, 0x61, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x20, 0x70, 0x65, 0x65,
0x72, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x0a, 0x0a, 0x0d, 0x0a, 0x05,
0x05, 0x01, 0x02, 0x06, 0x02, 0x12, 0x04, 0x89, 0x01, 0x0a, 0x0b, 0x0a, 0x0c, 0x0a, 0x02, 0x04,
0x11, 0x12, 0x06, 0x8c, 0x01, 0x00, 0x9c, 0x01, 0x01, 0x0a, 0x0b, 0x0a, 0x03, 0x04, 0x11, 0x01,
0x12, 0x04, 0x8c, 0x01, 0x08, 0x15, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x05, 0x12,
0x04, 0x8d, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x00, 0x12, 0x04, 0x8d,
0x01, 0x02, 0x10, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x01, 0x12, 0x04, 0x8d, 0x01,
0x09, 0x0b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x00, 0x03, 0x12, 0x04, 0x8d, 0x01, 0x0e,
0x0f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x01, 0x05, 0x12, 0x04, 0x8e, 0x01, 0x02, 0x08,
0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x01, 0x12, 0x04, 0x8e, 0x01, 0x02, 0x17, 0x0a, 0x0d,
0x0a, 0x05, 0x04, 0x11, 0x02, 0x01, 0x01, 0x12, 0x04, 0x8e, 0x01, 0x09, 0x12, 0x0a, 0x0d, 0x0a,
0x05, 0x04, 0x11, 0x02, 0x01, 0x03, 0x12, 0x04, 0x8e, 0x01, 0x15, 0x16, 0x0a, 0x0d, 0x0a, 0x05,
0x04, 0x11, 0x02, 0x02, 0x05, 0x12, 0x04, 0x8f, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04,
0x11, 0x02, 0x02, 0x12, 0x04, 0x8f, 0x01, 0x02, 0x12, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02,
0x02, 0x01, 0x12, 0x04, 0x8f, 0x01, 0x09, 0x0d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x02,
0x03, 0x12, 0x04, 0x8f, 0x01, 0x10, 0x11, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x03, 0x05,
0x12, 0x04, 0x90, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x03, 0x12, 0x04,
0x90, 0x01, 0x02, 0x14, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x03, 0x01, 0x12, 0x04, 0x90,
0x01, 0x09, 0x0f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x03, 0x03, 0x12, 0x04, 0x90, 0x01,
0x12, 0x13, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x04, 0x05, 0x12, 0x04, 0x91, 0x01, 0x02,
0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x04, 0x12, 0x04, 0x91, 0x01, 0x02, 0x19, 0x0a,
0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x04, 0x01, 0x12, 0x04, 0x91, 0x01, 0x09, 0x14, 0x0a, 0x0d,
0x0a, 0x05, 0x04, 0x11, 0x02, 0x04, 0x03, 0x12, 0x04, 0x91, 0x01, 0x17, 0x18, 0x0a, 0x0d, 0x0a,
0x05, 0x04, 0x11, 0x02, 0x05, 0x05, 0x12, 0x04, 0x92, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04,
0x04, 0x11, 0x02, 0x05, 0x12, 0x04, 0x92, 0x01, 0x02, 0x19, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11,
0x02, 0x05, 0x01, 0x12, 0x04, 0x92, 0x01, 0x09, 0x14, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02,
0x05, 0x03, 0x12, 0x04, 0x92, 0x01, 0x17, 0x18, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x06,
0x05, 0x12, 0x04, 0x93, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x06, 0x12,
0x04, 0x93, 0x01, 0x02, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x06, 0x01, 0x12, 0x04,
0x93, 0x01, 0x09, 0x19, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x06, 0x03, 0x12, 0x04, 0x93,
0x01, 0x1c, 0x1d, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x07, 0x06, 0x12, 0x04, 0x94, 0x01,
0x02, 0x07, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x07, 0x12, 0x04, 0x94, 0x01, 0x02, 0x12,
0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x07, 0x01, 0x12, 0x04, 0x94, 0x01, 0x08, 0x0d, 0x0a,
0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x07, 0x03, 0x12, 0x04, 0x94, 0x01, 0x10, 0x11, 0x0a, 0x0d,
0x0a, 0x05, 0x04, 0x11, 0x02, 0x08, 0x05, 0x12, 0x04, 0x95, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a,
0x04, 0x04, 0x11, 0x02, 0x08, 0x12, 0x04, 0x95, 0x01, 0x02, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04,
0x11, 0x02, 0x08, 0x01, 0x12, 0x04, 0x95, 0x01, 0x09, 0x16, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11,
0x02, 0x08, 0x03, 0x12, 0x04, 0x95, 0x01, 0x19, 0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02,
0x09, 0x05, 0x12, 0x04, 0x96, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x09,
0x12, 0x04, 0x96, 0x01, 0x02, 0x21, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x09, 0x01, 0x12,
0x04, 0x96, 0x01, 0x09, 0x1b, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x09, 0x03, 0x12, 0x04,
0x96, 0x01, 0x1e, 0x20, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0a, 0x05, 0x12, 0x04, 0x97,
0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x0a, 0x12, 0x04, 0x97, 0x01, 0x02,
0x1a, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0a, 0x01, 0x12, 0x04, 0x97, 0x01, 0x09, 0x14,
0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0a, 0x03, 0x12, 0x04, 0x97, 0x01, 0x17, 0x19, 0x0a,
0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0b, 0x05, 0x12, 0x04, 0x98, 0x01, 0x02, 0x08, 0x0a, 0x0c,
0x0a, 0x04, 0x04, 0x11, 0x02, 0x0b, 0x12, 0x04, 0x98, 0x01, 0x02, 0x1d, 0x0a, 0x0d, 0x0a, 0x05,
0x04, 0x11, 0x02, 0x0b, 0x01, 0x12, 0x04, 0x98, 0x01, 0x09, 0x17, 0x0a, 0x0d, 0x0a, 0x05, 0x04,
0x11, 0x02, 0x0b, 0x03, 0x12, 0x04, 0x98, 0x01, 0x1a, 0x1c, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11,
0x02, 0x0c, 0x05, 0x12, 0x04, 0x99, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02,
0x0c, 0x12, 0x04, 0x99, 0x01, 0x02, 0x1f, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0c, 0x01,
0x12, 0x04, 0x99, 0x01, 0x09, 0x19, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0c, 0x03, 0x12,
0x04, 0x99, 0x01, 0x1c, 0x1e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0d, 0x05, 0x12, 0x04,
0x9a, 0x01, 0x02, 0x08, 0x0a, 0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x0d, 0x12, 0x04, 0x9a, 0x01,
0x02, 0x14, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0d, 0x01, 0x12, 0x04, 0x9a, 0x01, 0x09,
0x0e, 0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0d, 0x03, 0x12, 0x04, 0x9a, 0x01, 0x11, 0x13,
0x0a, 0x0d, 0x0a, 0x05, 0x04, 0x11, 0x02, 0x0e, 0x05, 0x12, 0x04, 0x9b, 0x01, 0x02, 0x08, 0x0a,
0x0c, 0x0a, 0x04, 0x04, 0x11, 0x02, 0x0e, 0x12, 0x04, 0x9b, 0x01, 0x02, 0x14, 0x0a, 0x0d, 0x0a,
0x05, 0x04, 0x11, 0x02, 0x0e, 0x01, 0x12, 0x04, 0x9b, 0x01, 0x09, 0x0e, 0x0a, 0x0d, 0x0a, 0x05,
0x04, 0x11, 0x02, 0x0e, 0x03, 0x12, 0x04, 0x9b, 0x01, 0x11, 0x13, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
];
include!("torrent.serde.rs");
@@ -199,6 +199,98 @@ impl<'de> serde::Deserialize<'de> for AddResponse {
deserializer.deserialize_struct("torrent.AddResponse", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for AddedInfo {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if !self.source.is_empty() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.AddedInfo", len)?;
if !self.source.is_empty() {
struct_ser.serialize_field("source", &self.source)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for AddedInfo {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &["source"];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Source,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"source" => Ok(GeneratedField::Source),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = AddedInfo;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.AddedInfo")
}
fn visit_map<V>(self, mut map_: V) -> std::result::Result<AddedInfo, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut source__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Source => {
if source__.is_some() {
return Err(serde::de::Error::duplicate_field("source"));
}
source__ = Some(map_.next_value()?);
}
}
}
Ok(AddedInfo {
source: source__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("torrent.AddedInfo", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for ListRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
@@ -362,6 +454,420 @@ impl<'de> serde::Deserialize<'de> for ListResponse {
deserializer.deserialize_struct("torrent.ListResponse", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for Notification {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.observed_at_unix_millis != 0 {
len += 1;
}
if self.kind != 0 {
len += 1;
}
if self.torrent.is_some() {
len += 1;
}
if self.detail.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.Notification", len)?;
if self.observed_at_unix_millis != 0 {
#[allow(clippy::needless_borrow)]
#[allow(clippy::needless_borrows_for_generic_args)]
struct_ser.serialize_field(
"observedAtUnixMillis",
ToString::to_string(&self.observed_at_unix_millis).as_str(),
)?;
}
if self.kind != 0 {
let v = NotificationKind::try_from(self.kind)
.map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", self.kind)))?;
struct_ser.serialize_field("kind", &v)?;
}
if let Some(v) = self.torrent.as_ref() {
struct_ser.serialize_field("torrent", v)?;
}
if let Some(v) = self.detail.as_ref() {
match v {
notification::Detail::StateChanged(v) => {
struct_ser.serialize_field("stateChanged", v)?;
}
notification::Detail::Progress(v) => {
struct_ser.serialize_field("progress", v)?;
}
notification::Detail::Added(v) => {
struct_ser.serialize_field("added", v)?;
}
notification::Detail::Removed(v) => {
struct_ser.serialize_field("removed", v)?;
}
}
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for Notification {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"observed_at_unix_millis",
"observedAtUnixMillis",
"kind",
"torrent",
"state_changed",
"stateChanged",
"progress",
"added",
"removed",
];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
ObservedAtUnixMillis,
Kind,
Torrent,
StateChanged,
Progress,
Added,
Removed,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"observedAtUnixMillis" | "observed_at_unix_millis" => {
Ok(GeneratedField::ObservedAtUnixMillis)
}
"kind" => Ok(GeneratedField::Kind),
"torrent" => Ok(GeneratedField::Torrent),
"stateChanged" | "state_changed" => Ok(GeneratedField::StateChanged),
"progress" => Ok(GeneratedField::Progress),
"added" => Ok(GeneratedField::Added),
"removed" => Ok(GeneratedField::Removed),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = Notification;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.Notification")
}
fn visit_map<V>(self, mut map_: V) -> std::result::Result<Notification, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut observed_at_unix_millis__ = None;
let mut kind__ = None;
let mut torrent__ = None;
let mut detail__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::ObservedAtUnixMillis => {
if observed_at_unix_millis__.is_some() {
return Err(serde::de::Error::duplicate_field(
"observedAtUnixMillis",
));
}
observed_at_unix_millis__ = Some(
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
.0,
);
}
GeneratedField::Kind => {
if kind__.is_some() {
return Err(serde::de::Error::duplicate_field("kind"));
}
kind__ = Some(map_.next_value::<NotificationKind>()? as i32);
}
GeneratedField::Torrent => {
if torrent__.is_some() {
return Err(serde::de::Error::duplicate_field("torrent"));
}
torrent__ = map_.next_value()?;
}
GeneratedField::StateChanged => {
if detail__.is_some() {
return Err(serde::de::Error::duplicate_field("stateChanged"));
}
detail__ = map_
.next_value::<::std::option::Option<_>>()?
.map(notification::Detail::StateChanged);
}
GeneratedField::Progress => {
if detail__.is_some() {
return Err(serde::de::Error::duplicate_field("progress"));
}
detail__ = map_
.next_value::<::std::option::Option<_>>()?
.map(notification::Detail::Progress);
}
GeneratedField::Added => {
if detail__.is_some() {
return Err(serde::de::Error::duplicate_field("added"));
}
detail__ = map_
.next_value::<::std::option::Option<_>>()?
.map(notification::Detail::Added);
}
GeneratedField::Removed => {
if detail__.is_some() {
return Err(serde::de::Error::duplicate_field("removed"));
}
detail__ = map_
.next_value::<::std::option::Option<_>>()?
.map(notification::Detail::Removed);
}
}
}
Ok(Notification {
observed_at_unix_millis: observed_at_unix_millis__.unwrap_or_default(),
kind: kind__.unwrap_or_default(),
torrent: torrent__,
detail: detail__,
})
}
}
deserializer.deserialize_struct("torrent.Notification", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for NotificationKind {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let variant = match self {
Self::Unspecified => "NOTIFICATION_KIND_UNSPECIFIED",
Self::TorrentAdded => "TORRENT_ADDED",
Self::TorrentRemoved => "TORRENT_REMOVED",
Self::StateChanged => "STATE_CHANGED",
Self::Progress => "PROGRESS",
};
serializer.serialize_str(variant)
}
}
impl<'de> serde::Deserialize<'de> for NotificationKind {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"NOTIFICATION_KIND_UNSPECIFIED",
"TORRENT_ADDED",
"TORRENT_REMOVED",
"STATE_CHANGED",
"PROGRESS",
];
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = NotificationKind;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
i32::try_from(v)
.ok()
.and_then(|x| x.try_into().ok())
.ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self)
})
}
fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
i32::try_from(v)
.ok()
.and_then(|x| x.try_into().ok())
.ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self)
})
}
fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
where
E: serde::de::Error,
{
match value {
"NOTIFICATION_KIND_UNSPECIFIED" => Ok(NotificationKind::Unspecified),
"TORRENT_ADDED" => Ok(NotificationKind::TorrentAdded),
"TORRENT_REMOVED" => Ok(NotificationKind::TorrentRemoved),
"STATE_CHANGED" => Ok(NotificationKind::StateChanged),
"PROGRESS" => Ok(NotificationKind::Progress),
_ => Err(serde::de::Error::unknown_variant(value, FIELDS)),
}
}
}
deserializer.deserialize_any(GeneratedVisitor)
}
}
impl serde::Serialize for NotificationsRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if !self.ids.is_empty() {
len += 1;
}
if !self.kinds.is_empty() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.NotificationsRequest", len)?;
if !self.ids.is_empty() {
struct_ser.serialize_field("ids", &self.ids)?;
}
if !self.kinds.is_empty() {
let v = self
.kinds
.iter()
.cloned()
.map(|v| {
NotificationKind::try_from(v)
.map_err(|_| serde::ser::Error::custom(format!("Invalid variant {}", v)))
})
.collect::<std::result::Result<Vec<_>, _>>()?;
struct_ser.serialize_field("kinds", &v)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for NotificationsRequest {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &["ids", "kinds"];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Ids,
Kinds,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"ids" => Ok(GeneratedField::Ids),
"kinds" => Ok(GeneratedField::Kinds),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = NotificationsRequest;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.NotificationsRequest")
}
fn visit_map<V>(
self,
mut map_: V,
) -> std::result::Result<NotificationsRequest, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut ids__ = None;
let mut kinds__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Ids => {
if ids__.is_some() {
return Err(serde::de::Error::duplicate_field("ids"));
}
ids__ = Some(map_.next_value()?);
}
GeneratedField::Kinds => {
if kinds__.is_some() {
return Err(serde::de::Error::duplicate_field("kinds"));
}
kinds__ = Some(
map_.next_value::<Vec<NotificationKind>>()?
.into_iter()
.map(|x| x as i32)
.collect(),
);
}
}
}
Ok(NotificationsRequest {
ids: ids__.unwrap_or_default(),
kinds: kinds__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("torrent.NotificationsRequest", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for PauseRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
@@ -525,6 +1031,125 @@ impl<'de> serde::Deserialize<'de> for PauseResponse {
deserializer.deserialize_struct("torrent.PauseResponse", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for ProgressMark {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.percent != 0 {
len += 1;
}
if self.bytes_delta != 0 {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.ProgressMark", len)?;
if self.percent != 0 {
struct_ser.serialize_field("percent", &self.percent)?;
}
if self.bytes_delta != 0 {
#[allow(clippy::needless_borrow)]
#[allow(clippy::needless_borrows_for_generic_args)]
struct_ser.serialize_field(
"bytesDelta",
ToString::to_string(&self.bytes_delta).as_str(),
)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for ProgressMark {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &["percent", "bytes_delta", "bytesDelta"];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Percent,
BytesDelta,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"percent" => Ok(GeneratedField::Percent),
"bytesDelta" | "bytes_delta" => Ok(GeneratedField::BytesDelta),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = ProgressMark;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.ProgressMark")
}
fn visit_map<V>(self, mut map_: V) -> std::result::Result<ProgressMark, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut percent__ = None;
let mut bytes_delta__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Percent => {
if percent__.is_some() {
return Err(serde::de::Error::duplicate_field("percent"));
}
percent__ = Some(
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
.0,
);
}
GeneratedField::BytesDelta => {
if bytes_delta__.is_some() {
return Err(serde::de::Error::duplicate_field("bytesDelta"));
}
bytes_delta__ = Some(
map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?
.0,
);
}
}
}
Ok(ProgressMark {
percent: percent__.unwrap_or_default(),
bytes_delta: bytes_delta__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("torrent.ProgressMark", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for RemoveRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
@@ -704,6 +1329,98 @@ impl<'de> serde::Deserialize<'de> for RemoveResponse {
deserializer.deserialize_struct("torrent.RemoveResponse", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for RemovedInfo {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.files_deleted {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.RemovedInfo", len)?;
if self.files_deleted {
struct_ser.serialize_field("filesDeleted", &self.files_deleted)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for RemovedInfo {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &["files_deleted", "filesDeleted"];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
FilesDeleted,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"filesDeleted" | "files_deleted" => Ok(GeneratedField::FilesDeleted),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = RemovedInfo;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.RemovedInfo")
}
fn visit_map<V>(self, mut map_: V) -> std::result::Result<RemovedInfo, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut files_deleted__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::FilesDeleted => {
if files_deleted__.is_some() {
return Err(serde::de::Error::duplicate_field("filesDeleted"));
}
files_deleted__ = Some(map_.next_value()?);
}
}
}
Ok(RemovedInfo {
files_deleted: files_deleted__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("torrent.RemovedInfo", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for ResumeRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
@@ -880,6 +1597,7 @@ impl serde::Serialize for State {
Self::Paused => "PAUSED",
Self::Finished => "FINISHED",
Self::Error => "ERROR",
Self::Stale => "STALE",
};
serializer.serialize_str(variant)
}
@@ -897,6 +1615,7 @@ impl<'de> serde::Deserialize<'de> for State {
"PAUSED",
"FINISHED",
"ERROR",
"STALE",
];
struct GeneratedVisitor;
@@ -943,6 +1662,7 @@ impl<'de> serde::Deserialize<'de> for State {
"PAUSED" => Ok(State::Paused),
"FINISHED" => Ok(State::Finished),
"ERROR" => Ok(State::Error),
"STALE" => Ok(State::Stale),
_ => Err(serde::de::Error::unknown_variant(value, FIELDS)),
}
}
@@ -950,6 +1670,120 @@ impl<'de> serde::Deserialize<'de> for State {
deserializer.deserialize_any(GeneratedVisitor)
}
}
impl serde::Serialize for StateChanged {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.previous != 0 {
len += 1;
}
if self.current != 0 {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("torrent.StateChanged", len)?;
if self.previous != 0 {
let v = State::try_from(self.previous).map_err(|_| {
serde::ser::Error::custom(format!("Invalid variant {}", self.previous))
})?;
struct_ser.serialize_field("previous", &v)?;
}
if self.current != 0 {
let v = State::try_from(self.current).map_err(|_| {
serde::ser::Error::custom(format!("Invalid variant {}", self.current))
})?;
struct_ser.serialize_field("current", &v)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for StateChanged {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &["previous", "current"];
#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Previous,
Current,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
where
D: serde::Deserializer<'de>,
{
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = GeneratedField;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(formatter, "expected one of: {:?}", &FIELDS)
}
#[allow(unused_variables)]
fn visit_str<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"previous" => Ok(GeneratedField::Previous),
"current" => Ok(GeneratedField::Current),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = StateChanged;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct torrent.StateChanged")
}
fn visit_map<V>(self, mut map_: V) -> std::result::Result<StateChanged, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut previous__ = None;
let mut current__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Previous => {
if previous__.is_some() {
return Err(serde::de::Error::duplicate_field("previous"));
}
previous__ = Some(map_.next_value::<State>()? as i32);
}
GeneratedField::Current => {
if current__.is_some() {
return Err(serde::de::Error::duplicate_field("current"));
}
current__ = Some(map_.next_value::<State>()? as i32);
}
}
}
Ok(StateChanged {
previous: previous__.unwrap_or_default(),
current: current__.unwrap_or_default(),
})
}
}
deserializer.deserialize_struct("torrent.StateChanged", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for StatusRequest {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
@@ -173,6 +173,23 @@ pub mod torrents_client {
.insert(GrpcMethod::new("torrent.Torrents", "Resume"));
self.inner.unary(req, path, codec).await
}
pub async fn notifications(
&mut self,
request: impl tonic::IntoRequest<super::NotificationsRequest>,
) -> std::result::Result<
tonic::Response<tonic::codec::Streaming<super::Notification>>,
tonic::Status,
> {
self.inner.ready().await.map_err(|e| {
tonic::Status::unknown(format!("Service was not ready: {}", e.into()))
})?;
let codec = tonic_prost::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static("/torrent.Torrents/Notifications");
let mut req = request.into_request();
req.extensions_mut()
.insert(GrpcMethod::new("torrent.Torrents", "Notifications"));
self.inner.server_streaming(req, path, codec).await
}
}
}
/// Generated server implementations.
@@ -212,6 +229,15 @@ pub mod torrents_server {
&self,
request: tonic::Request<super::ResumeRequest>,
) -> std::result::Result<tonic::Response<super::ResumeResponse>, tonic::Status>;
/// Server streaming response type for the Notifications method.
type NotificationsStream: tonic::codegen::tokio_stream::Stream<
Item = std::result::Result<super::Notification, tonic::Status>,
> + std::marker::Send
+ 'static;
async fn notifications(
&self,
request: tonic::Request<super::NotificationsRequest>,
) -> std::result::Result<tonic::Response<Self::NotificationsStream>, tonic::Status>;
}
#[derive(Debug)]
pub struct TorrentsServer<T> {
@@ -508,6 +534,50 @@ pub mod torrents_server {
};
Box::pin(fut)
}
"/torrent.Torrents/Notifications" => {
#[allow(non_camel_case_types)]
struct NotificationsSvc<T: Torrents>(pub Arc<T>);
impl<T: Torrents>
tonic::server::ServerStreamingService<super::NotificationsRequest>
for NotificationsSvc<T>
{
type Response = super::Notification;
type ResponseStream = T::NotificationsStream;
type Future =
BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
fn call(
&mut self,
request: tonic::Request<super::NotificationsRequest>,
) -> Self::Future {
let inner = Arc::clone(&self.0);
let fut = async move {
<T as Torrents>::notifications(&inner, request).await
};
Box::pin(fut)
}
}
let accept_compression_encodings = self.accept_compression_encodings;
let send_compression_encodings = self.send_compression_encodings;
let max_decoding_message_size = self.max_decoding_message_size;
let max_encoding_message_size = self.max_encoding_message_size;
let inner = self.inner.clone();
let fut = async move {
let method = NotificationsSvc(inner);
let codec = tonic_prost::ProstCodec::default();
let mut grpc = tonic::server::Grpc::new(codec)
.apply_compression_config(
accept_compression_encodings,
send_compression_encodings,
)
.apply_max_message_size_config(
max_decoding_message_size,
max_encoding_message_size,
);
let res = grpc.server_streaming(method, req).await;
Ok(res)
};
Box::pin(fut)
}
_ => Box::pin(async move {
let mut response = http::Response::new(tonic::body::Body::default());
let headers = response.headers_mut();
+4 -2
View File
@@ -2,9 +2,11 @@ pub mod torrent {
include!("generated/torrent/torrent.rs");
}
pub use torrent::notification as notification_detail;
pub use torrent::{
AddRequest, AddResponse, ListRequest, ListResponse, PauseRequest, PauseResponse, RemoveRequest,
RemoveResponse, ResumeRequest, ResumeResponse, State, StatusRequest, TorrentStatus,
AddRequest, AddResponse, AddedInfo, ListRequest, ListResponse, Notification, NotificationKind,
NotificationsRequest, PauseRequest, PauseResponse, ProgressMark, RemoveRequest, RemoveResponse,
RemovedInfo, ResumeRequest, ResumeResponse, State, StateChanged, StatusRequest, TorrentStatus,
torrents_client::TorrentsClient,
torrents_server::{Torrents, TorrentsServer},
};
+3 -1
View File
@@ -5,12 +5,14 @@ edition = "2024"
[dependencies]
anyhow = "1.0.103"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
clap = { version = "4.6.1", features = ["derive", "env"] }
colored = "3"
hyper-util = { version = "0.1.20", features = ["tokio"] }
nix = { version = "0.31", features = ["signal"] }
serde_json = "1.0.140"
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net"] }
tokio = { version = "1.52.3", features = ["rt-multi-thread", "macros", "net", "signal"] }
tokio-stream = "0.1.18"
tonic = "0.14.6"
tonic-health = "0.14.6"
tora-proto = { version = "0.1.0", path = "../proto" }
+37
View File
@@ -1,6 +1,7 @@
use std::path::PathBuf;
use clap::{Parser, Subcommand, ValueEnum};
use tora_proto::NotificationKind;
#[derive(Parser)]
#[command(
@@ -43,6 +44,31 @@ pub enum SortKey {
Added,
}
/// CLI-facing mirror of `NotificationKind` so `clap`'s `ValueEnum` derive can
/// enumerate the values. Mapped back to the proto enum via `to_proto`.
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum KindFilter {
/// `TORRENT_ADDED`
Added,
/// `TORRENT_REMOVED`
Removed,
/// `STATE_CHANGED` (includes DOWNLOADING <-> STALE, * -> FINISHED, etc.)
StateChanged,
/// `PROGRESS` (one event per integer percent advance)
Progress,
}
impl KindFilter {
pub fn to_proto(self) -> i32 {
match self {
KindFilter::Added => NotificationKind::TorrentAdded as i32,
KindFilter::Removed => NotificationKind::TorrentRemoved as i32,
KindFilter::StateChanged => NotificationKind::StateChanged as i32,
KindFilter::Progress => NotificationKind::Progress as i32,
}
}
}
#[derive(Subcommand)]
pub enum Command {
/// Check whether torad is reachable and healthy.
@@ -95,4 +121,15 @@ pub enum Command {
},
/// Stop the torad daemon.
Stop,
/// Stream torrent notifications live from torad. Runs until interrupted
/// (Ctrl-C) or the daemon disconnects.
Watch {
/// Filter to these torrent ids (UUIDs or unique prefixes). Repeatable.
/// Omit to observe all torrents.
#[arg(long = "id", value_name = "ID")]
ids: Vec<String>,
/// Only emit these event kinds. Repeatable. Omit for all kinds.
#[arg(long = "kind", value_enum)]
kinds: Vec<KindFilter>,
},
}
+46 -3
View File
@@ -6,17 +6,18 @@ use std::time::Duration;
use anyhow::{Context, Result};
use clap::Parser;
use cli::{Cli, Command, SortKey};
use cli::{Cli, Command, KindFilter, SortKey};
use hyper_util::rt::TokioIo;
use output::Formatter;
use tokio::net::UnixStream;
use tokio_stream::StreamExt;
use tonic::transport::{Channel, Endpoint, Uri};
use tonic_health::pb::HealthCheckRequest;
use tonic_health::pb::health_check_response::ServingStatus;
use tonic_health::pb::health_client::HealthClient;
use tora_proto::{
AddRequest, ListRequest, PauseRequest, RemoveRequest, ResumeRequest, StatusRequest,
TorrentsClient,
AddRequest, ListRequest, NotificationsRequest, PauseRequest, RemoveRequest, ResumeRequest,
StatusRequest, TorrentsClient,
};
use tower::service_fn;
@@ -51,6 +52,7 @@ async fn main() -> Result<()> {
download_dir,
} => start_daemon(&socket, &pid_file, &database_url, download_dir, f).await,
Command::Stop => stop_daemon(&socket, &pid_file, f).await,
Command::Watch { ids, kinds } => watch(&socket, ids, kinds, f).await,
}
}
@@ -269,3 +271,44 @@ async fn stop_daemon(socket: &Path, pid_file: &Path, f: &dyn Formatter) -> Resul
Err(err) => Err(err).context("failed to send SIGTERM to torad"),
}
}
async fn watch(
socket: &Path,
ids: Vec<String>,
kinds: Vec<KindFilter>,
f: &dyn Formatter,
) -> Result<()> {
let channel = connect(socket).await?;
let mut client = TorrentsClient::new(channel);
let request = NotificationsRequest {
ids,
kinds: kinds.iter().map(|k| k.to_proto()).collect(),
};
let stream = client
.notifications(request)
.await
.context("Notifications RPC failed")?
.into_inner();
tokio::pin!(stream);
let stdout = std::io::stdout();
while let Some(message) = stream.next().await {
match message {
Ok(notification) => {
let line = f.watch(&notification)?;
let mut guard = stdout.lock();
use std::io::Write;
writeln!(guard, "{line}")?;
guard.flush()?;
}
Err(status) => {
anyhow::bail!("stream error: {status}");
}
}
}
Ok(())
}
+109 -1
View File
@@ -1,8 +1,12 @@
use std::fmt::Write;
use anyhow::Result;
use chrono::{Local, TimeZone};
use colored::Colorize;
use tora_proto::{State, TorrentStatus};
use tora_proto::{
Notification, NotificationKind, ProgressMark, State, StateChanged, TorrentStatus,
notification_detail::Detail,
};
use crate::cli::OutputFormat;
@@ -18,6 +22,7 @@ pub trait Formatter {
fn resume(&self) -> Result<String>;
fn start(&self) -> Result<String>;
fn stop(&self) -> Result<String>;
fn watch(&self, event: &Notification) -> Result<String>;
}
pub fn from_format(format: OutputFormat) -> Box<dyn Formatter> {
@@ -77,6 +82,10 @@ impl Formatter for LongFormatter {
fn stop(&self) -> Result<String> {
Ok(format!(" {} torad stopped", "".yellow()))
}
fn watch(&self, event: &Notification) -> Result<String> {
Ok(format_event_long(event))
}
}
struct JsonFormatter;
@@ -130,6 +139,10 @@ impl Formatter for JsonFormatter {
&serde_json::json!({ "stopped": true }),
)?)
}
fn watch(&self, event: &Notification) -> Result<String> {
Ok(serde_json::to_string(event)?)
}
}
fn format_status_long(status: &TorrentStatus) -> String {
@@ -216,6 +229,99 @@ fn format_status_long(status: &TorrentStatus) -> String {
out
}
fn format_event_long(event: &Notification) -> String {
let time = Local
.timestamp_millis_opt(event.observed_at_unix_millis as i64)
.single()
.map(|t| t.format("%H:%M:%S").to_string())
.unwrap_or_else(|| "??".to_string());
let kind = NotificationKind::try_from(event.kind).unwrap_or(NotificationKind::Unspecified);
let torrent = event.torrent.as_ref();
let id = torrent.map(|t| short_id(&t.id)).unwrap_or_default();
let name = torrent
.map(|t| display_name(&t.name))
.unwrap_or_else(|| "(unknown)".to_string());
let state = torrent
.and_then(|t| State::try_from(t.state).ok())
.unwrap_or(State::Unspecified);
let id_w = 8usize;
let name_w = 30usize;
let (icon, suffix) = match (&kind, event.detail.as_ref()) {
(NotificationKind::TorrentAdded, _) => ("+".green(), String::new()),
(NotificationKind::TorrentRemoved, Some(Detail::Removed(r))) => {
let note = if r.files_deleted {
" (files deleted)"
} else {
""
};
("".red(), format!("{}{}", colored_state(state), note.red()))
}
(NotificationKind::TorrentRemoved, _) => ("".red(), colored_state(state).to_string()),
(
NotificationKind::StateChanged,
Some(Detail::StateChanged(StateChanged { current, .. })),
) => {
let cur = State::try_from(*current).unwrap_or(State::Unspecified);
let icon = state_icon(cur);
(icon, colored_state(cur).to_string())
}
(
NotificationKind::Progress,
Some(Detail::Progress(ProgressMark {
percent,
bytes_delta,
})),
) => {
let delta = humanize_bytes(*bytes_delta);
let pct_bar = format_pct_inline(*percent);
(
"".yellow(),
format!(
"{} {} {}",
pct_bar.yellow(),
format!("{}%", percent).bold(),
format!("{delta})").dimmed()
),
)
}
(_, _) => ("·".dimmed(), colored_state(state).to_string()),
};
format!(
" {} {} {} {} {}",
time.dimmed(),
icon,
pad(&id, id_w).dimmed(),
pad(&name, name_w),
suffix,
)
}
fn state_icon(state: State) -> colored::ColoredString {
match state {
State::Finished => "".green(),
State::Downloading => "".yellow(),
State::Paused => "".cyan(),
State::Error => "".red(),
State::Stale => "".red(),
State::Pending | State::Unspecified => "·".dimmed(),
}
}
fn format_pct_inline(percent: u32) -> String {
let filled = (percent as usize * BAR_WIDTH) / 100;
let empty = BAR_WIDTH - filled;
format!(
"{}{} {:>3}%",
"".repeat(filled),
"".repeat(empty),
percent
)
}
fn format_list_long(torrents: &[TorrentStatus]) -> String {
if torrents.is_empty() {
return " no torrents".dimmed().to_string();
@@ -301,6 +407,7 @@ fn colored_state(state: State) -> colored::ColoredString {
State::Downloading => s.yellow(),
State::Paused => s.cyan(),
State::Error => s.red().bold(),
State::Stale => s.red(),
State::Pending | State::Unspecified => s.dimmed(),
}
}
@@ -312,6 +419,7 @@ fn pad_colored(s: &str, width: usize, state: State) -> colored::ColoredString {
State::Downloading => padded.yellow(),
State::Paused => padded.cyan(),
State::Error => padded.red().bold(),
State::Stale => padded.red(),
State::Pending | State::Unspecified => padded.dimmed(),
}
}
+11 -5
View File
@@ -23,6 +23,7 @@ pub enum TorrentState {
Paused,
Finished,
Error,
Stale,
}
#[derive(Debug, Clone, sqlx::FromRow)]
@@ -126,8 +127,12 @@ pub struct Progress<'a> {
pub error_message: Option<&'a str>,
}
pub async fn update_progress(pool: &PgPool, id: Uuid, progress: Progress<'_>) -> Result<()> {
sqlx::query(
pub async fn update_progress(
pool: &PgPool,
id: Uuid,
progress: Progress<'_>,
) -> Result<TorrentRow> {
let row = sqlx::query_as::<_, TorrentRow>(
"UPDATE torrents
SET name = COALESCE($2, name),
total_bytes = $3,
@@ -136,7 +141,8 @@ pub async fn update_progress(pool: &PgPool, id: Uuid, progress: Progress<'_>) ->
error_message = $6,
updated_at = now(),
completed_at = CASE WHEN $5 = 'finished' THEN now() ELSE completed_at END
WHERE id = $1",
WHERE id = $1
RETURNING id, info_hash, name, source, output_path, total_bytes, downloaded_bytes, state, error_message",
)
.bind(id)
.bind(progress.name)
@@ -144,7 +150,7 @@ pub async fn update_progress(pool: &PgPool, id: Uuid, progress: Progress<'_>) ->
.bind(progress.downloaded_bytes)
.bind(progress.state)
.bind(progress.error_message)
.execute(pool)
.fetch_one(pool)
.await?;
Ok(())
Ok(row)
}
+490 -26
View File
@@ -1,19 +1,20 @@
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use anyhow::{Context, Result};
use librqbit::api::TorrentIdOrHash;
use librqbit::{AddTorrent, AddTorrentOptions, ManagedTorrent, Session, TorrentStatsState};
use librqbit_core::Id20;
use sqlx::PgPool;
use tokio::sync::Mutex;
use tokio::sync::{Mutex, broadcast};
use tonic::{Request, Response, Status};
use tora_proto::{
AddRequest, AddResponse, ListRequest, ListResponse, PauseRequest, PauseResponse, RemoveRequest,
RemoveResponse, ResumeRequest, ResumeResponse, State as ProtoState, StatusRequest,
TorrentStatus, Torrents,
AddRequest, AddResponse, AddedInfo, ListRequest, ListResponse, Notification, NotificationKind,
NotificationsRequest, PauseRequest, PauseResponse, ProgressMark, RemoveRequest, RemoveResponse,
ResumeRequest, ResumeResponse, State as ProtoState, StateChanged, StatusRequest, TorrentStatus,
Torrents,
};
use tracing::{error, info, warn};
use uuid::Uuid;
@@ -24,6 +25,14 @@ use crate::source::SourceResolver;
const POLL_INTERVAL: Duration = Duration::from_secs(2);
const ADD_TIMEOUT: Duration = Duration::from_secs(60);
const BYTES_PER_MIB: f64 = 1_048_576.0;
/// `broadcast` channel capacity. Subscribers that fall more than this many
/// events behind receive `Lagged` errors and continue. Picked for ~5 s of
/// slack at the maximum realistic emit rate (~50 events/sec).
const EVENT_CHANNEL_CAPACITY: usize = 256;
/// Consecutive poller ticks a `Live && !finished` torrent must show no byte
/// progress AND zero connected peers before being flipped to `Stale`.
/// At a 2 s `POLL_INTERVAL`, this is 6 s of inactivity.
const STALE_TICKS_THRESHOLD: u32 = 3;
pub enum ResolveError {
NotFound,
@@ -38,6 +47,8 @@ pub struct TorrentManager {
source: SourceResolver,
tracked: Mutex<HashMap<Uuid, Arc<ManagedTorrent>>>,
adding: Mutex<HashSet<Uuid>>,
events: broadcast::Sender<Notification>,
poll_cache: Mutex<HashMap<Uuid, PollCache>>,
}
impl TorrentManager {
@@ -52,6 +63,7 @@ impl TorrentManager {
.await
.context("failed to create librqbit session")?;
let source = SourceResolver::new().context("failed to build source resolver")?;
let (events, _) = broadcast::channel(EVENT_CHANNEL_CAPACITY);
Ok(Arc::new(Self {
pool,
session,
@@ -59,9 +71,22 @@ impl TorrentManager {
source,
tracked: Mutex::new(HashMap::new()),
adding: Mutex::new(HashSet::new()),
events,
poll_cache: Mutex::new(HashMap::new()),
}))
}
pub fn subscribe(&self) -> broadcast::Receiver<Notification> {
self.events.subscribe()
}
pub(crate) fn notify(&self, mut notification: Notification) {
if notification.observed_at_unix_millis == 0 {
notification.observed_at_unix_millis = now_millis();
}
let _ = self.events.send(notification);
}
pub async fn add(&self, source: &str, output_dir: Option<&str>) -> Result<TorrentRow> {
let resolved = self.source.resolve(source).await?;
let stored_source = resolved.rewritten_source.as_deref().unwrap_or(source);
@@ -263,11 +288,151 @@ impl TorrentManager {
}
async fn report_progress(&self) {
// Snapshot stats under a brief lock; do all DB work and emission outside.
let snapshots: Vec<(Uuid, librqbit::TorrentStats, Option<String>)> = {
let tracked = self.tracked.lock().await;
for (id, handle) in tracked.iter() {
let stats = handle.stats();
let name = handle.name();
let state = if stats.finished {
tracked
.iter()
.map(|(id, handle)| (*id, handle.stats(), handle.name()))
.collect()
};
for (id, stats, name) in snapshots {
let raw_state = raw_state_from_stats(&stats);
let live = live_stats_from_stats(&stats);
let (final_state, prev_state_proto, percent, bytes_delta, state_changed) = self
.diff_and_update_cache(
id,
raw_state,
stats.progress_bytes,
live.peers,
stats.total_bytes,
)
.await;
let progress = db::Progress {
name: name.as_deref(),
total_bytes: i64::try_from(stats.total_bytes).unwrap_or(i64::MAX),
downloaded_bytes: i64::try_from(stats.progress_bytes).unwrap_or(i64::MAX),
state: final_state,
error_message: stats.error.as_deref(),
};
let row = match db::update_progress(&self.pool, id, progress).await {
Ok(row) => row,
Err(err) => {
error!(id = %id, error = %err, "failed to persist torrent progress");
continue;
}
};
let status = row_to_status(row, live);
if state_changed {
self.notify(Notification {
observed_at_unix_millis: 0,
kind: NotificationKind::StateChanged as i32,
torrent: Some(status.clone()),
detail: Some(tora_proto::notification_detail::Detail::StateChanged(
StateChanged {
previous: prev_state_proto as i32,
current: status.state,
},
)),
});
}
if let Some(delta) = bytes_delta {
self.notify(Notification {
observed_at_unix_millis: 0,
kind: NotificationKind::Progress as i32,
torrent: Some(status),
detail: Some(tora_proto::notification_detail::Detail::Progress(
ProgressMark {
percent,
bytes_delta: delta,
},
)),
});
}
}
}
/// Updates `poll_cache` for `id` from this tick's observation and returns
/// the data needed to emit notifications. Returns
/// `(final_state, previous_proto_state, percent, Option<bytes_delta>, state_changed)`.
async fn diff_and_update_cache(
&self,
id: Uuid,
raw_state: TorrentState,
progress_bytes: u64,
live_peers: u32,
total_bytes: u64,
) -> (TorrentState, ProtoState, u32, Option<u64>, bool) {
let mut poll_cache = self.poll_cache.lock().await;
let cache = poll_cache
.entry(id)
.or_insert_with(|| PollCache::new(proto_state(raw_state)));
compute_diff(cache, raw_state, progress_bytes, live_peers, total_bytes)
}
}
/// Pure diff/staleness logic. Updates `cache` in place and returns
/// `(final_state, previous_proto_state, percent, Option<bytes_delta>, state_changed)`.
/// Extracted from `diff_and_update_cache` so it can be unit-tested without a
/// live TorrentManager (which needs Postgres + librqbit session).
fn compute_diff(
cache: &mut PollCache,
raw_state: TorrentState,
progress_bytes: u64,
live_peers: u32,
total_bytes: u64,
) -> (TorrentState, ProtoState, u32, Option<u64>, bool) {
if raw_state == TorrentState::Downloading {
if progress_bytes == cache.last_bytes {
cache.stale_ticks = cache.stale_ticks.saturating_add(1);
} else {
cache.stale_ticks = 0;
}
} else {
cache.stale_ticks = 0;
}
cache.last_bytes = progress_bytes;
let final_state = if raw_state == TorrentState::Downloading
&& cache.stale_ticks >= STALE_TICKS_THRESHOLD
&& live_peers == 0
{
TorrentState::Stale
} else {
raw_state
};
let prev_proto = cache.last_state;
let final_proto = proto_state(final_state);
let state_changed = prev_proto != final_proto;
cache.last_state = final_proto;
// u128 mul to avoid overflow on multi-exabyte totals; cap at 100.
let percent = if total_bytes == 0 {
0
} else {
((progress_bytes as u128 * 100 / total_bytes as u128) as u32).min(100)
};
let bytes_delta = if percent != cache.last_percent {
let delta = progress_bytes.saturating_sub(cache.bytes_at_last_progress);
cache.last_percent = percent;
cache.bytes_at_last_progress = progress_bytes;
Some(delta)
} else {
None
};
(final_state, prev_proto, percent, bytes_delta, state_changed)
}
fn raw_state_from_stats(stats: &librqbit::TorrentStats) -> TorrentState {
if stats.finished {
TorrentState::Finished
} else {
match stats.state {
@@ -276,18 +441,6 @@ impl TorrentManager {
TorrentStatsState::Paused => TorrentState::Paused,
TorrentStatsState::Error => TorrentState::Error,
}
};
let progress = db::Progress {
name: name.as_deref(),
total_bytes: i64::try_from(stats.total_bytes).unwrap_or(i64::MAX),
downloaded_bytes: i64::try_from(stats.progress_bytes).unwrap_or(i64::MAX),
state,
error_message: stats.error.as_deref(),
};
if let Err(err) = db::update_progress(&self.pool, *id, progress).await {
error!(id = %id, error = %err, "failed to persist torrent progress");
}
}
}
}
@@ -301,6 +454,34 @@ struct LiveTorrentStats {
seeds: u32,
}
#[derive(Clone)]
struct PollCache {
last_state: ProtoState,
last_percent: u32,
last_bytes: u64,
bytes_at_last_progress: u64,
stale_ticks: u32,
}
impl PollCache {
fn new(state: ProtoState) -> Self {
Self {
last_state: state,
last_percent: 0,
last_bytes: 0,
bytes_at_last_progress: 0,
stale_ticks: 0,
}
}
}
fn now_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
async fn persist_error(pool: &PgPool, id: Uuid, message: &str) {
let progress = db::Progress {
name: None,
@@ -318,10 +499,13 @@ fn extract_live_stats(tracked: &HashMap<Uuid, Arc<ManagedTorrent>>, id: &Uuid) -
let Some(handle) = tracked.get(id) else {
return LiveTorrentStats::default();
};
let stats = handle.stats();
live_stats_from_stats(&handle.stats())
}
fn live_stats_from_stats(stats: &librqbit::TorrentStats) -> LiveTorrentStats {
let uploaded_bytes = stats.uploaded_bytes;
let Some(live) = stats.live else {
let Some(live) = stats.live.as_ref() else {
return LiveTorrentStats {
uploaded_bytes,
..Default::default()
@@ -364,6 +548,7 @@ fn proto_state(state: TorrentState) -> ProtoState {
TorrentState::Paused => ProtoState::Paused,
TorrentState::Finished => ProtoState::Finished,
TorrentState::Error => ProtoState::Error,
TorrentState::Stale => ProtoState::Stale,
}
}
@@ -419,9 +604,18 @@ impl Torrents for GrpcTorrents {
.add(&req.magnet, output_dir)
.await
.map_err(|err| Status::internal(err.to_string()))?;
Ok(Response::new(AddResponse {
id: row.id.to_string(),
}))
let id_str = row.id.to_string();
let status = row_to_status(row, LiveTorrentStats::default());
let source = status.source.clone();
self.manager.notify(Notification {
observed_at_unix_millis: 0,
kind: NotificationKind::TorrentAdded as i32,
torrent: Some(status),
detail: Some(tora_proto::notification_detail::Detail::Added(AddedInfo {
source,
})),
});
Ok(Response::new(AddResponse { id: id_str }))
}
async fn status(
@@ -468,6 +662,17 @@ impl Torrents for GrpcTorrents {
.map_err(|err| Status::internal(err.to_string()))?
.ok_or_else(|| Status::not_found("torrent not found"))?;
info!(id = %id, name = ?row.name, "torrent removed");
let status = row_to_status(row, LiveTorrentStats::default());
self.manager.notify(Notification {
observed_at_unix_millis: 0,
kind: NotificationKind::TorrentRemoved as i32,
torrent: Some(status),
detail: Some(tora_proto::notification_detail::Detail::Removed(
tora_proto::RemovedInfo {
files_deleted: req.delete_files,
},
)),
});
Ok(Response::new(RemoveResponse {}))
}
@@ -508,4 +713,263 @@ impl Torrents for GrpcTorrents {
info!(id = %id, name = ?row.name, "torrent resumed");
Ok(Response::new(ResumeResponse {}))
}
type NotificationsStream =
tokio_stream::wrappers::ReceiverStream<std::result::Result<Notification, Status>>;
async fn notifications(
&self,
request: Request<NotificationsRequest>,
) -> Result<Response<Self::NotificationsStream>, Status> {
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
let req = request.into_inner();
// Resolve id-prefix filter once at subscribe time. If any prefix fails
// to resolve, the whole subscription is rejected — partial filters
// would surprise the client.
let allowed_ids: Option<HashSet<Uuid>> = if req.ids.is_empty() {
None
} else {
let mut set = HashSet::new();
for id_str in &req.ids {
let uuid = self.manager.resolve_id(id_str).await.map_err(resolve_err)?;
set.insert(uuid);
}
Some(set)
};
let allowed_kinds: Option<HashSet<i32>> = if req.kinds.is_empty() {
None
} else {
Some(req.kinds.iter().copied().collect())
};
let mut rx = self.manager.subscribe();
let (tx, rx_stream) = mpsc::channel::<std::result::Result<Notification, Status>>(64);
tokio::spawn(async move {
loop {
match rx.recv().await {
Ok(notification) => {
if let Some(ref allowed) = allowed_ids
&& let Some(ref torrent) = notification.torrent
&& Uuid::parse_str(&torrent.id)
.ok()
.filter(|id| allowed.contains(id))
.is_none()
{
continue;
}
if let Some(ref kinds) = allowed_kinds
&& !kinds.contains(&notification.kind)
{
continue;
}
if tx.send(Ok(notification)).await.is_err() {
break;
}
}
Err(broadcast::error::RecvError::Lagged(n)) => {
warn!(skipped = n, "notifications subscriber lagged");
continue;
}
Err(broadcast::error::RecvError::Closed) => break,
}
}
});
Ok(Response::new(ReceiverStream::new(rx_stream)))
}
}
#[cfg(test)]
mod tests {
use super::*;
/// A `PollCache` primed as though a prior tick already recorded
/// `bytes` at `total` size, in the Downloading state. The first
/// `compute_diff` call from a test then represents the first
/// *observation* after that baseline (not an artificial advance from 0).
fn primed_cache(bytes: u64, total: u64) -> PollCache {
let percent = if total == 0 {
0
} else {
((bytes as u128 * 100 / total as u128) as u32).min(100)
};
PollCache {
last_state: ProtoState::Downloading,
last_percent: percent,
last_bytes: bytes,
bytes_at_last_progress: bytes,
stale_ticks: 0,
}
}
/// `(final_state, state_changed, percent, bytes_delta)`
fn simplify(
out: (TorrentState, ProtoState, u32, Option<u64>, bool),
) -> (TorrentState, bool, u32, Option<u64>) {
(out.0, out.4, out.2, out.3)
}
#[test]
fn below_stale_threshold_stays_downloading() {
let mut cache = primed_cache(100_000, 1_000_000);
// Tick 1: stale_ticks=1.
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
assert_eq!(simplify(out), (TorrentState::Downloading, false, 10, None));
// Tick 2: stale_ticks=2.
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
assert_eq!(simplify(out), (TorrentState::Downloading, false, 10, None));
}
#[test]
fn stale_triggers_at_threshold_with_zero_peers() {
let mut cache = primed_cache(100_000, 1_000_000);
// Tick 1: stale_ticks=1.
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
// Tick 2: stale_ticks=2.
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
// Tick 3: stale_ticks=3 → STALE.
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Stale);
assert_eq!(out.1, ProtoState::Downloading);
assert!(out.4);
}
#[test]
fn peers_above_zero_prevents_stale() {
let mut cache = primed_cache(100_000, 1_000_000);
for _ in 0..10 {
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 1, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
}
}
#[test]
fn bytes_advance_resets_stale_counter() {
let mut cache = primed_cache(100_000, 1_000_000);
// Two no-progress ticks bring stale_ticks to 2.
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
// Tick 3: bytes advance to 200 — stale_ticks resets to 0, stays Downloading.
let out = compute_diff(&mut cache, TorrentState::Downloading, 200_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
assert!(!out.4);
// Tick 4: no progress again, stale_ticks=1 (not threshold).
let out = compute_diff(&mut cache, TorrentState::Downloading, 200_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
}
#[test]
fn stale_recovers_on_bytes_advance() {
let mut cache = primed_cache(100_000, 1_000_000);
// Reach STALE state.
for _ in 0..3 {
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
}
assert_eq!(cache.last_state, ProtoState::Stale);
// Bytes advance — should recover to Downloading and emit state_changed.
let out = compute_diff(&mut cache, TorrentState::Downloading, 200_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
assert_eq!(out.1, ProtoState::Stale);
assert!(out.4);
}
#[test]
fn stale_recovers_on_peer_connect() {
let mut cache = primed_cache(100_000, 1_000_000);
// Reach STALE state.
for _ in 0..3 {
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
}
assert_eq!(cache.last_state, ProtoState::Stale);
// Peer connects (bytes unchanged).
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 1, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
assert_eq!(out.1, ProtoState::Stale);
assert!(out.4);
}
#[test]
fn stale_persists_when_nothing_changes() {
let mut cache = primed_cache(100_000, 1_000_000);
for _ in 0..3 {
compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
}
assert_eq!(cache.last_state, ProtoState::Stale);
// Subsequent tick: still no progress, still no peers.
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Stale);
assert!(!out.4);
}
#[test]
fn pause_and_resume_emit_state_change() {
let mut cache = primed_cache(100_000, 1_000_000);
// Pause: raw_state changes from Downloading to Paused.
let out = compute_diff(&mut cache, TorrentState::Paused, 100_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Paused);
assert!(out.4);
// Resume: back to Downloading.
let out = compute_diff(&mut cache, TorrentState::Downloading, 100_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Downloading);
assert!(out.4);
}
#[test]
fn finished_takes_precedence_over_stale() {
let mut cache = primed_cache(100_000, 1_000_000);
let out = compute_diff(&mut cache, TorrentState::Finished, 1_000_000, 0, 1_000_000);
assert_eq!(out.0, TorrentState::Finished);
assert!(out.4);
}
#[test]
fn percent_change_emits_progress_with_delta() {
let mut cache = primed_cache(0, 1000);
// 0% → 10% (bytes from 0 to 100 of 1000).
let out = compute_diff(&mut cache, TorrentState::Downloading, 100, 1, 1000);
assert_eq!(out.2, 10);
assert_eq!(out.3, Some(100));
// 10% → 50% (bytes from 100 to 500 of 1000).
let out = compute_diff(&mut cache, TorrentState::Downloading, 500, 1, 1000);
assert_eq!(out.2, 50);
assert_eq!(out.3, Some(400));
}
#[test]
fn no_progress_emit_when_percent_unchanged() {
let mut cache = primed_cache(0, 1000);
// 0% → 10%.
compute_diff(&mut cache, TorrentState::Downloading, 100, 1, 1000);
// 10% again (bytes from 100 to 109, still 10%).
let out = compute_diff(&mut cache, TorrentState::Downloading, 109, 1, 1000);
assert_eq!(out.2, 10);
assert_eq!(out.3, None);
}
#[test]
fn zero_total_bytes_gives_zero_percent() {
let mut cache = primed_cache(0, 0);
let out = compute_diff(&mut cache, TorrentState::Downloading, 0, 0, 0);
assert_eq!(out.2, 0);
assert_eq!(out.3, None);
}
#[test]
fn first_tick_initializes_state_without_false_event() {
let mut cache = PollCache::new(ProtoState::Pending);
let out = compute_diff(&mut cache, TorrentState::Pending, 0, 0, 0);
assert!(!out.4);
}
#[test]
fn percent_caps_at_100_on_full_download() {
let mut cache = primed_cache(0, 1000);
let out = compute_diff(&mut cache, TorrentState::Downloading, 1000, 1, 1000);
assert_eq!(out.2, 100);
}
}
+2 -1
View File
@@ -3,7 +3,8 @@ CREATE TYPE torrent_state AS ENUM (
'downloading',
'paused',
'finished',
'error'
'error',
'stale'
);
CREATE TABLE torrents (
+73
View File
@@ -15,6 +15,10 @@ service Torrents {
rpc Pause(PauseRequest) returns (PauseResponse);
// Resume a paused torrent.
rpc Resume(ResumeRequest) returns (ResumeResponse);
// Subscribe to a live stream of torrent notifications. Events are delivered
// from the subscribe point onward no history is replayed. New subscribers
// only see events that fire after they connect.
rpc Notifications(NotificationsRequest) returns (stream Notification);
}
message AddRequest {
@@ -56,6 +60,69 @@ message ResumeRequest {
message ResumeResponse {}
message NotificationsRequest {
// If non-empty, only events for these torrent ids (or id prefixes, as
// accepted by `Status`/`Remove`) are delivered.
repeated string ids = 1;
// If non-empty, only these event kinds are delivered. An empty list means
// all kinds.
repeated NotificationKind kinds = 2;
}
message Notification {
// Wall-clock time the daemon observed the change, in milliseconds since the
// Unix epoch.
uint64 observed_at_unix_millis = 1;
// What changed. Use this to dispatch; use the `oneof detail` for
// kind-specific payload.
NotificationKind kind = 2;
// Full snapshot of the torrent *after* the change. Always populated so
// consumers don't need to fan out to `Status` for context.
TorrentStatus torrent = 3;
oneof detail {
StateChanged state_changed = 10;
ProgressMark progress = 11;
AddedInfo added = 12;
RemovedInfo removed = 13;
}
}
enum NotificationKind {
NOTIFICATION_KIND_UNSPECIFIED = 0;
// Emitted once when a torrent is added via `Add`.
TORRENT_ADDED = 1;
// Emitted once when a torrent is removed via `Remove`.
TORRENT_REMOVED = 2;
// Emitted on any `State` transition, including PAUSED <-> DOWNLOADING,
// DOWNLOADING <-> STALE, * -> FINISHED, * -> ERROR.
STATE_CHANGED = 3;
// Emitted when the integer download percentage changes (rate-limited).
// No event is sent if the percentage did not advance since the last tick.
PROGRESS = 4;
}
message StateChanged {
State previous = 1;
State current = 2;
}
message ProgressMark {
// Current integer percentage 0..=100.
uint32 percent = 1;
// Bytes downloaded since the previous PROGRESS event for this torrent.
uint64 bytes_delta = 2;
}
message AddedInfo {
// Source string as provided to `Add` (magnet, URL, or file path).
string source = 1;
}
message RemovedInfo {
// True if `Remove` was called with `delete_files = true`.
bool files_deleted = 1;
}
enum State {
STATE_UNSPECIFIED = 0;
PENDING = 1;
@@ -63,6 +130,12 @@ enum State {
PAUSED = 3;
FINISHED = 4;
ERROR = 5;
// Live per librqbit, but the daemon has detected a stall: no byte progress
// for several ticks AND zero connected peers. The torrent is still
// considered live by the underlying engine; this state is an application-
// level overlay. Recovers to DOWNLOADING automatically when bytes advance
// or a peer connects.
STALE = 6;
}
message TorrentStatus {