pub mod api;
pub mod request;
use serde::{Deserialize, Serialize};
const QUERY: &str = "podping";
pub(crate) const HIVE_API: &str = "https://api.hive.blog";
#[derive(Debug)]
pub(crate) struct HiveClient {
pub client: reqwest::Client,
block: Option<u64>,
}
impl HiveClient {
pub(crate) fn new(block: Option<u64>) -> Self {
let client = reqwest::Client::new();
Self { client, block }
}
pub(crate) async fn init(&mut self) {
match &self.block {
Some(_) => {}
None => {
let block = self.get_head_block_number();
self.block = Some(block.await);
}
}
}
pub(crate) async fn next_block(&mut self) {
if let Some(block) = self.block_mut() {
*block += 1
}
}
pub(crate) async fn get_head_block_number(&self) -> u64 {
self.get_dynamic_global_properties()
.await
.get_head_block_number()
.unwrap()
}
pub(crate) async fn get_dynamic_global_properties(&self) -> HiveResponse {
self.client
.post(HIVE_API)
.json(&HiveMessage::get_dynamic_global_properties())
.send()
.await
.unwrap()
.json::<HiveResponse>()
.await
.unwrap()
}
pub(crate) fn block(&self) -> Option<u64> {
self.block
}
pub(crate) fn block_mut(&mut self) -> &mut Option<u64> {
&mut self.block
}
pub(crate) fn set_block(&mut self, block: Option<u64>) {
self.block = block;
}
pub(crate) fn get_payloads(&self, operations: Vec<Operations>) -> Vec<String> {
let mut payloads = vec![];
for tr in operations {
let json_payload = serde_json::from_str::<OpPayload>(&tr.json.unwrap()).unwrap();
payloads = json_payload.iris;
if json_payload.reason == "live" {
let item = format!("live: {:?}", self.block);
std::process::Command::new("notify-send")
.arg(item)
.output()
.expect("failed to execute process");
}
}
if !payloads.is_empty() {
println!("block: {:?}, payloads: {:?}", self.block, payloads);
}
payloads
}
pub(crate) async fn get_operations(&self) -> Vec<Operations> {
let transactions = self.get_transactions().await;
let mut operations = vec![];
for transaction in transactions {
for operation in transaction.operations {
for op in operation {
match op {
HiveOperation::Operations(operation) => {
if let Some(name) = &operation.id {
if name == QUERY {
operations.push(operation);
}
}
}
HiveOperation::String(_)
| HiveOperation::Vote(_)
| HiveOperation::Transfer(_)
| HiveOperation::Transactions(_)
| HiveOperation::Comment(_) => {}
}
}
}
}
operations
}
pub(crate) async fn get_block(&self) -> HiveResponse {
let block_response = HiveMessage::get_block(self.block.unwrap());
let resp = self
.client
.post(HIVE_API)
.json(&block_response)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
serde_json::from_str::<HiveResponse>(&resp).unwrap()
}
pub(crate) async fn get_transactions(&self) -> Vec<Transactions> {
let hive_response = &self.get_block().await;
if let Some(HiveResponseResult::Block(block)) = &hive_response.result {
block.transactions.clone()
} else {
vec![]
}
}
}
#[derive(Debug)]
pub(crate) struct HiveClientBlocking {
pub client: reqwest::blocking::Client,
block: Option<u64>,
}
impl HiveClientBlocking {
pub(crate) fn new(block: Option<u64>) -> Self {
let client = reqwest::blocking::Client::new();
Self { client, block }
}
pub(crate) fn init(&mut self) {
match &self.block {
Some(_) => {}
None => {
let block = self.get_head_block_number();
self.block = Some(block);
}
}
}
pub(crate) fn get_head_block_number(&self) -> u64 {
let resp = self
.client
.post(HIVE_API)
.json(&HiveMessage::get_dynamic_global_properties())
.send()
.unwrap()
.json::<HiveResponse>()
.unwrap();
resp.get_head_block_number().unwrap()
}
pub(crate) fn get_operations(&self) -> Vec<Operations> {
let transactions = self.get_transactions();
let mut operations = vec![];
for transaction in transactions {
for operation in transaction.operations {
for op in operation {
match op {
HiveOperation::Operations(operation) => {
if let Some(name) = &operation.id {
if name == QUERY {
operations.push(operation);
}
}
}
HiveOperation::String(_)
| HiveOperation::Vote(_)
| HiveOperation::Transfer(_)
| HiveOperation::Transactions(_)
| HiveOperation::Comment(_) => {}
}
}
}
}
operations
}
pub(crate) fn get_transactions(&self) -> Vec<Transactions> {
let block_response = HiveMessage::get_block(self.block.unwrap());
let resp = self
.client
.post(HIVE_API)
.json(&block_response)
.send()
.unwrap()
.text()
.unwrap();
let hive_response = serde_json::from_str::<HiveResponse>(&resp).unwrap();
if let Some(HiveResponseResult::Block(block)) = hive_response.result {
block.transactions
} else {
vec![]
}
}
pub(crate) fn get_payloads(&self, operations: Vec<Operations>) -> Vec<String> {
let mut payloads = vec![];
for tr in operations {
let mut json_payload = serde_json::from_str::<OpPayload>(&tr.json.unwrap()).unwrap();
payloads.append(&mut json_payload.iris);
}
payloads
}
pub(crate) fn block(&self) -> Option<u64> {
self.block
}
pub(crate) fn block_mut(&mut self) -> &mut Option<u64> {
&mut self.block
}
pub(crate) fn set_block(&mut self, block: Option<u64>) {
self.block = block;
}
pub(crate) fn next_block(&mut self) {
if let Some(block) = self.block_mut() {
*block += 1
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct HiveRequest {
jsonrpc: String,
method: String,
id: u8,
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct HiveRequestParams {
jsonrpc: String,
method: String,
params: Vec<ParamMember>,
id: u8,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum HiveMessage {
Request(HiveRequest),
RequestParams(HiveRequestParams),
Response(HiveResponse),
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum ParamMember {
String(String),
Int(u16),
Int128(u128),
Int64(u64),
None,
}
impl HiveMessage {
pub(crate) fn get_followers() -> Self {
Self::RequestParams(HiveRequestParams {
jsonrpc: "2.0".into(),
method: "condenser_api.get_followers".into(),
params: vec![
ParamMember::String(QUERY.into()),
ParamMember::None,
ParamMember::String("blog".into()),
ParamMember::Int(100),
],
id: 1,
})
}
pub(crate) fn get_methods() -> Self {
Self::Request(HiveRequest {
jsonrpc: "2.0".into(),
method: "jsonrpc.get_methods".into(),
id: 1,
})
}
pub(crate) fn get_dynamic_global_properties() -> Self {
Self::Request(HiveRequest {
jsonrpc: "2.0".into(),
method: "database_api.get_dynamic_global_properties".into(),
id: 1,
})
}
pub(crate) fn get_block(block: u64) -> Self {
Self::RequestParams(HiveRequestParams {
jsonrpc: "2.0".into(),
method: "condenser_api.get_block".into(),
params: vec![ParamMember::Int64(block)],
id: 1,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HiveResponse {
pub jsonrpc: String,
pub id: u16,
pub result: Option<HiveResponseResult>,
}
impl HiveResponse {
pub(crate) fn get_head_block_number(&self) -> Option<u64> {
if let Some(response) = &self.result {
response.get_head_block_number()
} else {
None
}
}
}
impl HiveResponseResult {
pub(crate) fn get_head_block_number(&self) -> Option<u64> {
match self {
HiveResponseResult::DynamicGlobalProperties(properties) => {
Some(properties.head_block_number)
}
_ => None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HiveResponseResult {
DynamicGlobalProperties(HiveDynamicGlobalProperties),
Block(Block),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct HiveDynamicGlobalProperties {
id: u16,
head_block_number: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Block {
pub transactions: Vec<Transactions>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Transactions {
pub(crate) operations: Vec<Vec<HiveOperation>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum HiveOperation {
Operations(Operations),
String(String),
Vote(Vote),
Transfer(Transfer),
Transactions(Transactions),
Comment(Comment),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Operations {
required_auths: Vec<String>,
required_posting_auths: Vec<String>,
pub id: Option<String>,
pub json: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OpPayload {
version: String,
num_urls: Option<u8>,
pub medium: String,
pub reason: String,
pub iris: Vec<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Vote {
voter: Option<String>,
author: Option<String>,
permlink: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Transfer {
from: Option<String>,
to: Option<String>,
amount: Option<String>,
memo: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Comment {
parent_author: Option<String>,
parent_permlink: Option<String>,
author: Option<String>,
title: Option<String>,
}