This commit is contained in:
Vladimir N. Korotenko
2025-11-10 12:30:48 +03:00
commit 0a0d1f739e
48 changed files with 25194 additions and 0 deletions

237
dm-cli/AccountStatus.cpp Normal file
View File

@@ -0,0 +1,237 @@
#include "AccountStatus.hpp"
#include <string>
#include <cstdlib>
#include <iostream>
#include "PrintFile.hpp"
#include "common.hpp"
#include "ExecCommand.hpp"
#include "json.hpp"
#include "base64.h"
#include "cleanHtml.h"
#include <sstream>
using namespace std;
void AccountHelp(bool isRussian)
{
if (isRussian)
{
PrintFile(ACCOUNT_STATUS_RU);
}
else
{
PrintFile(ACCOUNT_STATUS_EN);
}
}
/// @brief Получение информации аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountStatus(const std::string address, const char* bkaddress, bool isDebug)
{
// curl "https://testnet-dm.bitdeals.org/api/v1/account/status?address=mnumHs9HQMrw2Q1iKLNnx9NzExS7nMLmyp"
string url("curl -s \"https://");
url += address + "/api/v1/account/status?address=" + bkaddress + "\"";
string result = ExecCommand(url.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
string payload("");
payload = jsonData["payload"];
bool success = jsonData["result"]["success"];
string decoded = base64_decode(payload);
string cleaned = cleanup_html(decoded);
cout << cleaned << endl;
if (isDebug) ShowDebug(url, result, success, decoded);
if (success) return 0; else return 1;
}
/// @brief Получение информации аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountInfo(const std::string address, const char* bkaddress, bool isDebug) {
// curl -H "Content-Type: application/json" -d "{ \"code\": \"mnumHs9HQMrw2Q1iKLNnx9NzExS7nMLmyp\"}" https://testnet-dm2.bitdeals.org/api/v1/user/profile
string url("curl -s ");
url.append("-H \"Content-Type: application/json\" ");
url.append("-d \"{ \\\"code\\\": \\\"");
url.append(bkaddress);
url.append("\\\"} \" ");
url.append(address);
url.append("/api/v1/user/profile");
string result = ExecCommand(url.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
string bitcoin("");
string btckey("");
string bitmessage("");
string pgpkey("");
bitcoin = bkaddress;
btckey = jsonData["btckey"];
bitmessage = jsonData["bitmessages"][0];
pgpkey = jsonData["pgpkey"];
string decoded("");
decoded.append("bitcoin: ");
decoded.append(bitcoin);
decoded.append("\n");
decoded.append("btckey: ");
decoded.append(btckey);
decoded.append("\n");
decoded.append("bitmessage: ");
decoded.append(bitmessage);
decoded.append("\n");
decoded.append("pgpkey: ");
decoded.append(pgpkey);
decoded.append("\n");
cout << decoded << endl;
if (isDebug) ShowDebug(url, result, 1, decoded);
return 0;
}
/// @brief Получение Рейтинга аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountRaiting(const std::string address, const char* bkaddress, bool isDebug) {
// curl -H "Content-Type: application/json" -d "{ \"code\": \"mnumHs9HQMrw2Q1iKLNnx9NzExS7nMLmyp\"}" https://testnet-dm2.bitdeals.org/api/v1/user/profile
string url("curl -s ");
url.append("-H \"Content-Type: application/json\" ");
url.append("-d \"{ \\\"code\\\": \\\"");
url.append(bkaddress);
url.append("\\\"} \" ");
url.append(address);
url.append("/api/v1/user/profile");
string result = ExecCommand(url.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
std::ostringstream stream;
stream << "created: " << jsonData["created"] << endl;
stream << "seller: " << endl;
stream << " count: " << jsonData["seller"]["count"] << endl;
stream << " positive: " << jsonData["seller"]["positive"] << endl;
stream << "customer: " << endl;
stream << " count: " << jsonData["customer"]["count"] << endl;
stream << " positive: " << jsonData["customer"]["positive"] << endl;
string decoded = stream.str();
cout << decoded << endl;
if (isDebug) ShowDebug(url, result, 1, decoded);
return 0;
}
/// @brief Получение Рейтинга аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
/// @param fb - тип фидбэка 0- все p - 1(positive) n-2 (negative)
int AccountFeedbacks(const std::string address, const char* bkaddress, bool isDebug, int fb) {
/*
curl -X POST "https://testnet-dm2.bitdeals.org/api/v1/deal/feedback/list" \
-H "Content-Type: application/json" \
-d '{"search":[{"field":"sellercode","compare":"EQL","value":"mraXx7JrmAmuKypdJ1vseQBXySsdRZE5AC"}],"orderby":["date DESC"],"reclimit":1000,"recoffset":0}'
*/
string url("curl -s ");
url.append("-H \"Content-Type: application/json\" ");
url.append("-d \"{ \\\"search\\\":[{\\\"field\\\":\\\"sellercode\\\",\\\"compare\\\": \\\"EQL\\\",\\\"value\\\":\\\"");
url.append(bkaddress);
url.append("\\\"}],\\\"orderby\\\":[\\\" date DESC\\\"],\\\"reclimit\\\":100,\\\"recoffset\\\":0} \" ");
url.append(address);
url.append("/api/v1/deal/feedback/list");
string result = ExecCommand(url.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
std::ostringstream stream;
size_t size = jsonData.size();
int positivefb = 0;
int negativefb = 0;
for (size_t i = 0;i < size;++i) {
// "positive"
// "negative"
// 0 - все p - 1(positive)n - 2 (negative)
auto val = jsonData[i];
string statustext = val["statustext"];
if (statustext.compare("positive") == 0) positivefb++;
if (statustext.compare("negative") == 0) negativefb++;
if (fb == 1 && statustext.compare("positive") == 0) {
stream << "- deal: " << val["deal"] << endl;
stream << " date: " << val["date"] << endl;
stream << " statustext: " << val["statustext"] << endl;
stream << " comments: " << val["comments"] << endl;
}
else if (fb == 2 && statustext.compare("negative") == 0) {
stream << "- deal: " << val["deal"] << endl;
stream << " date: " << val["date"] << endl;
stream << " statustext: " << val["statustext"] << endl;
stream << " comments: " << val["comments"] << endl;
}
else
{
stream << "- deal: " << val["deal"] << endl;
stream << " date: " << val["date"] << endl;
stream << " statustext: " << val["statustext"] << endl;
stream << " comments: " << val["comments"] << endl;
}
/*
- deal: ff5f337f-e0ab-4515-857f-3a0c8530dcd3
date: '2025-10-05T15:29:23'
statustext: positive
comments: positive comment about the deal
*/
}
stream << "------------Summary--------------" << endl;
stream << "total: " << size << endl;
stream << "positive: " << positivefb << endl;
stream << "neegative: " << negativefb << endl;
string decoded = stream.str();
cout << decoded << endl;
if (isDebug) ShowDebug(url, result, 1, decoded);
return 0;
return 0;
}

33
dm-cli/AccountStatus.hpp Normal file
View File

@@ -0,0 +1,33 @@
#ifndef _ACCOUNT_STATUS_
#define _ACCOUNT_STATUS_
#include <string>
/// @brief ПОлучение справки о параметрах команды
/// @param isRussian - указывает выводить ли русскую справку
void AccountHelp(bool isRussian);
/// @brief Получение статуса аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountStatus(const std::string address, const char *bkaddress, bool isDebug);
/// @brief Получение информации аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountInfo(const std::string address, const char* bkaddress, bool isDebug);
/// @brief Получение Рейтинга аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
int AccountRaiting(const std::string address, const char* bkaddress, bool isDebug);
/// @brief Получение Рейтинга аккаунта
/// @param address адресс АПИ
/// @param bkaddress номер кошелька
/// @param isDebug флаг отладки
/// @param fb - тип фидбэка 0- все p - 1(positive) n-2 (negative)
int AccountFeedbacks(const std::string address, const char* bkaddress, bool isDebug, int fb);
#endif

299
dm-cli/AccountUpdate.cpp Normal file
View File

@@ -0,0 +1,299 @@
#include "AccountUpdate.hpp"
#include <string>
#include <cstdlib>
#include "PrintFile.hpp"
#include "common.hpp"
#include "ExecCommand.hpp"
#include "json.hpp"
#include "base64.h"
#include <fstream>
#include "cleanHtml.h"
#include <sstream>
#include <iostream>
using namespace std;
int AccountUpdate::Help()
{
if (this->isRussian)
PrintFile(ACCOUNT_UPDATE_RU);
else
PrintFile(ACCOUNT_UPDATE_EN);
return 0;
}
AccountUpdate::AccountUpdate(const InputParser &parser, const bool isRussian)
{
this->parser = parser;
this->isRussian = isRussian;
address = (DEFAULTADDRESS);
const string &addressarg = parser.getCmdOption("--address");
if (!addressarg.empty())
{
address = addressarg;
}
isDebug = false;
if (parser.cmdOptionExists("--debug"))
{
isDebug = true;
}
}
int AccountUpdate::Process()
{
if (this->parser.cmdOptionExists("--help"))
return this->Help();
return Update();
}
int AccountUpdate::ProcessUrl(std::string date, std::string signature, std::string urls)
{
/*
curl -X POST "https://testnet-dm2.bitdeals.org/api/v1/client/update" \
-H "Content-Type: application/json" \
-d '{"address":"mnumHs9HQMrw2Q1iKLNnx9NzExS7nMLmyp","date":"2025-10-21","url":["https://testnet-dm2.bitdeals.org"],
"sign":"IAGHICUSCxUrcWTMSL9j94vRufb9l5nBckahT+RznlHMPx9LTPpDHsozxVyxkpjtgrhC8eCyJKNaOw8U/v56pL0="}'
*/
/*
[{{URLS}}],
*/
std::stringstream test(urls);
std::string segment;
std::vector<std::string> seglist;
string urlout("");
while (std::getline(test, segment, ','))
{
urlout.append("\\\"");
urlout.append(segment);
urlout.append("\\\",");
seglist.push_back(segment);
}
urlout = urlout.substr(0, urlout.length() - 1);
string data("curl -s ");
data += ReadFile(ACCOUNT_UPDATE_URL);
string bt("");
bt = parser.getLast();
data = Replace(data, string("{{ADDRESS}}"), address);
data = Replace(data, string("{{BT}}"), bt);
data = Replace(data, string("{{DATE}}"), date);
data = Replace(data, string("{{SIGN}}"), signature);
data = Replace(data, string("{{URLS}}"), urlout);
string result = ExecCommand(data.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
std::ostringstream stream;
stream << "success: " << jsonData["result"]["success"] << endl;
stream << "message: " << jsonData["result"]["message"] << endl;
string decoded = stream.str();
cout << decoded << endl;
string payload = base64_decode(jsonData["payload"]);
if (isDebug)
ShowDebug(data, decoded, 1, payload);
return 0;
}
int AccountUpdate::ProcessPgp(std::string date, std::string signature, std::string pgp)
{
string data("curl ");
data += ReadFile(ACCOUNT_UPDATE_PGP);
string bt("");
bt = parser.getLast();
data = Replace(data, string("{{ADDRESS}}"), address);
data = Replace(data, string("{{BT}}"), bt);
data = Replace(data, string("{{DATE}}"), date);
data = Replace(data, string("{{SIGN}}"), signature);
data = Replace(data, string("{{PGP}}"), pgp);
string result = ExecCommand(data.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
std::ostringstream stream;
stream << "success: " << jsonData["result"]["success"] << endl;
stream << "message: " << jsonData["result"]["message"] << endl;
string decoded = stream.str();
cout << decoded << endl;
string payload = cleanup_html(base64_decode(jsonData["payload"]));
if (isDebug)
ShowDebug(data, decoded, 1, payload);
return 0;
}
int AccountUpdate::ProcessBitmessage(std::string date, std::string signature, std::string btmsg)
{
string data("curl ");
data += ReadFile(ACCOUNT_UPDATE_BTM);
string bt("");
bt = parser.getLast();
data = Replace(data, string("{{ADDRESS}}"), address);
data = Replace(data, string("{{BT}}"), bt);
data = Replace(data, string("{{DATE}}"), date);
data = Replace(data, string("{{SIGN}}"), signature);
data = Replace(data, string("{{BTM}}"), btmsg);
string result = ExecCommand(data.c_str());
nlohmann::json jsonData = nlohmann::json::parse(result);
std::ostringstream stream;
stream << "success: " << jsonData["result"]["success"] << endl;
stream << "message: " << jsonData["result"]["message"] << endl;
string decoded = stream.str();
cout << decoded << endl;
string payload = cleanup_html(base64_decode(jsonData["payload"]));
if (isDebug)
ShowDebug(data, decoded, 1, payload);
return 0;
}
int AccountUpdate::Update()
{
if (!this->parser.cmdOptionExists("-d") && !this->parser.cmdOptionExists("--date"))
{
cout << "option -d | --date required" << endl;
return 1;
}
if (!this->parser.cmdOptionExists("-s") && !this->parser.cmdOptionExists("--signature"))
{
cout << "option -s | --signature required" << endl;
return 1;
}
if (!this->parser.cmdOptionExists("-p") && !this->parser.cmdOptionExists("--pgp") && !this->parser.cmdOptionExists("-u") && !this->parser.cmdOptionExists("--url") && !this->parser.cmdOptionExists("-b") && !this->parser.cmdOptionExists("--bitmessage"))
{
cout << "one of this parameter required: -p | --pgp, -u| --url, -b| --bitmessage" << endl;
return 1;
}
string datavalue("");
if (this->parser.cmdOptionExists("-d"))
{
string vl = this->parser.getCmdOption("-d");
if (vl.length() > 0)
{
datavalue = vl;
}
}
if (this->parser.cmdOptionExists("--data"))
{
string vl = this->parser.getCmdOption("--data");
if (vl.length() > 0)
{
datavalue = vl;
}
}
if (datavalue.length() == 0)
{
cout << "data cold't be empty" << endl;
return 1;
}
string signvalue("");
if (this->parser.cmdOptionExists("-s"))
{
string vl = this->parser.getCmdOption("-s");
if (vl.length() > 0)
{
signvalue = vl;
}
}
if (this->parser.cmdOptionExists("--signature"))
{
string vl = this->parser.getCmdOption("--signature");
if (vl.length() > 0)
{
signvalue = vl;
}
}
if (signvalue.length() == 0)
{
cout << "signature cold't be empty" << endl;
return 1;
}
// делаем обновление url
if (this->parser.cmdOptionExists("-u") || this->parser.cmdOptionExists("--url"))
{
string urlvalue("");
if (this->parser.cmdOptionExists("-u"))
{
string vl = this->parser.getCmdOption("-u");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (this->parser.cmdOptionExists("--url"))
{
string vl = this->parser.getCmdOption("--url");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (urlvalue.length() == 0)
{
cout << "url cold't be empty" << endl;
return 1;
}
return ProcessUrl(datavalue, signvalue, urlvalue);
}
if (this->parser.cmdOptionExists("-b") || this->parser.cmdOptionExists("--bitmessage"))
{
string urlvalue("");
if (this->parser.cmdOptionExists("-b"))
{
string vl = this->parser.getCmdOption("-b");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (this->parser.cmdOptionExists("--bitmessage"))
{
string vl = this->parser.getCmdOption("--bitmessage");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (urlvalue.length() == 0)
{
cout << "bitmessage cold't be empty" << endl;
return 1;
}
return ProcessBitmessage(datavalue, signvalue, urlvalue);
}
else if (this->parser.cmdOptionExists("-p") || this->parser.cmdOptionExists("--pgp"))
{
string urlvalue("");
if (this->parser.cmdOptionExists("-p"))
{
string vl = this->parser.getCmdOption("-p");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (this->parser.cmdOptionExists("--pgp"))
{
string vl = this->parser.getCmdOption("--pgp");
if (vl.length() > 0)
{
urlvalue = vl;
}
}
if (urlvalue.length() == 0)
{
cout << "pgp cold't be empty" << endl;
return 1;
}
return ProcessPgp(datavalue, signvalue, urlvalue);
}
cout << "no argument" << endl;
return 1;
return 0;
}

27
dm-cli/AccountUpdate.hpp Normal file
View File

@@ -0,0 +1,27 @@
#ifndef _ACCOUNT_UPDATE_
#define _ACCOUNT_UPDATE_
#include <string>
#include "InputParser.hpp"
class AccountUpdate {
public:
/// @brief Конструктор
/// @param parser - парсер аргументов
/// @param isRussian - вывод справки на русском
AccountUpdate(const InputParser& parser, const bool isRussian);
int Process();
private:
int ProcessUrl(std::string date, std::string signature, std::string urls);
int ProcessBitmessage(std::string date, std::string signature, std::string btmsg);
int ProcessPgp(std::string date, std::string signature, std::string pgp);
InputParser parser;
bool isRussian;
/// @brief Получение справки о параметрах команды
int Help();
/// @brief Обновление данных, url через запятую
int Update();
std::string address;
bool isDebug;
};
#endif

104
dm-cli/Deals.cpp Normal file
View File

@@ -0,0 +1,104 @@
#include "Deals.hpp"
#include <string>
#include <cstdlib>
#include "PrintFile.hpp"
#include "common.hpp"
#include "ExecCommand.hpp"
#include "json.hpp"
#include "base64.h"
#include <fstream>
#include "cleanHtml.h"
#include <sstream>
#include <iostream>
using namespace std;
int Deals::HelpCreate() const {
if (isRussian)
PrintFile(DEALS_RU);
else
PrintFile(DEALS_EN);
return 0;
}
int Deals::HelpDealStatus() const
{
if (isRussian)
PrintFile(DEAL_STATUS_RU);
else
PrintFile(DEAL_STATUS_EN);
return 0;
}
int Deals::HelpDealComplete() const
{
if (isRussian)
PrintFile(DEAL_COMPLETE_RU);
else
PrintFile(DEAL_COMPLETE_EN);
return 0;
}
int Deals::HelpDealCancel() const
{
if (isRussian)
PrintFile(DEAL_CANCEL_RU);
else
PrintFile(DEAL_CANCEL_EN);
return 0;
}
int Deals::HelpDealNegative() const
{
if (isRussian)
PrintFile(DEAL_NEGATIVE_RU);
else
PrintFile(DEAL_NEGATIVE_EN);
return 0;
}
Deals::Deals(const InputParser& parser, const bool isRussian, const string address, bool isDebug)
{
this->parser = parser;
this->isRussian = isRussian;
this->address = address;
this->isDebug = isDebug;
}
int Deals::Process()
{
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("create") && this->parser.cmdOptionExists("--help"))
return this->HelpCreate();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("status") && this->parser.cmdOptionExists("--help"))
return this->HelpDealStatus();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("complete") && this->parser.cmdOptionExists("--help"))
return this->HelpDealComplete();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("cancel") && this->parser.cmdOptionExists("--help"))
return this->HelpDealCancel();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("negative") && this->parser.cmdOptionExists("--help"))
return this->HelpDealNegative();
return Update();
return 0;
}
int Deals::Update() {
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("create"))
return Create();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("status"))
return Status();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("complete"))
return Complite();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("cancel"))
return Cancel();
if (parser.cmdOptionExists("deal") && this->parser.cmdOptionExists("negative"))
return Negative();
return 1;
}
int Deals::Create() {
return 0;
}
int Deals::Status() { return 0; }
int Deals::Complite() { return 0; }
int Deals::Cancel() { return 0; }
int Deals::Negative() { return 0; }

36
dm-cli/Deals.hpp Normal file
View File

@@ -0,0 +1,36 @@
#ifndef _DEALS_
#define _DEALS_
#include <string>
#include "InputParser.hpp"
class Deals {
public:
/// @brief Конструктор
/// @param parser - парсер аргументов
/// @param isRussian - вывод справки на русском
Deals(const InputParser& parser, const bool isRussian, const std::string address, bool isDebug);
int Process();
private:
InputParser parser;
bool isRussian;
std::string address;
bool isDebug;
/// @brief Получение справки о параметрах команды
int HelpCreate() const;
int HelpDealStatus() const;
int HelpDealComplete() const;
int HelpDealCancel() const;
int HelpDealNegative() const;
int Update();
int Create();
int Status();
int Complite();
int Cancel();
int Negative();
};
#endif

25
dm-cli/ExecCommand.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "ExecCommand.hpp"
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
#ifdef WINDOWS
#define pclose _pclose
#define popen _popen
#endif // WINDOWS
std::string ExecCommand(const char* cmd) {
std::array<char, 512> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}

5
dm-cli/ExecCommand.hpp Normal file
View File

@@ -0,0 +1,5 @@
#ifndef _EXEC_COMMAND_H_
#define _EXEC_COMMAND_H_
#include <string>
std::string ExecCommand(const char* cmd);
#endif

75
dm-cli/InputParser.hpp Normal file
View File

@@ -0,0 +1,75 @@
#ifndef _INPUTPARSER_
#define _INPUTPARSER_
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
/// @brief Анализирует входящие параметры
class InputParser
{
public:
InputParser(){}
InputParser(int &argc, char **argv)
{
for (int i = 1; i < argc; ++i)
this->tokens.push_back(std::string(argv[i]));
}
/// @brief Пакует без параметров --debug и address
/// @param argc
/// @param argv
InputParser(int &argc, char **argv, bool skip)
{
for (int i = 1; i < argc; ++i)
{
std::string cur(argv[i]);
if (cur.compare("--debug") == 0)
{
++i;
continue;
}
if (cur.compare("--address") == 0)
{
i += 2;
continue;
}
this->tokens.push_back(cur);
}
}
/// @brief получение строки параметров
const std::string &getCmdOption(const std::string &option) const
{
std::vector<std::string>::const_iterator itr;
itr = std::find(this->tokens.begin(), this->tokens.end(), option);
if (itr != this->tokens.end() && ++itr != this->tokens.end())
{
return *itr;
}
static const std::string empty_string("");
return empty_string;
}
/// @brief get last argument
const std::string& getLast() {
return tokens[tokens.size() - 1];
}
const void getBuffer() const
{
std::string ret;
for (const auto &s : this->tokens)
{
if (!ret.empty())
ret += ",";
ret += s;
}
std::cout << ret << std::endl;
}
/// @brief существует ли параметр
bool cmdOptionExists(const std::string &option) const
{
return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
}
private:
std::vector<std::string> tokens;
};
#endif

18
dm-cli/Makefile Normal file
View File

@@ -0,0 +1,18 @@
CC=g++
# CFLAGS=-c -Wall
CFLAGS=-c -Wall -g #debug version
# LDFLAGS=
LDFLAGS= -g
SOURCES=AccountStatus.cpp base64.cpp cleanHtml.cpp ExecCommand.cpp help.cpp main.cpp PrintFile.cpp ShowDebug.cpp AccountUpdate.cpp ReadFile.cpp Replace.cpp Deals.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=dm-cli
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.cpp.o:
$(CC) $(CFLAGS) $< -o $@
clean:
rm *.o

23
dm-cli/PrintFile.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include "PrintFile.hpp"
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
void PrintFile(const char *fileName)
{
ifstream inputFile(fileName);
if (inputFile.is_open())
{
string line;
while (getline(inputFile, line))
{
cout << line << endl;
}
inputFile.close();
}
else
{
cerr << "Error: Unable to open file " << fileName << endl;
}
}

4
dm-cli/PrintFile.hpp Normal file
View File

@@ -0,0 +1,4 @@
#ifndef _PRINT_FILE_
#define _PRINT_FILE_
void PrintFile(const char *fileName);
#endif

26
dm-cli/ReadFile.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include "common.hpp"
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
std::string ReadFile(const char* filename) {
string data("");
ifstream inputFile(filename);
if (inputFile.is_open())
{
string line;
while (getline(inputFile, line))
{
data.append(line);
// data.append("\r\n");
}
inputFile.close();
}
else
{
cerr << "Error: Unable to open file " << filename << endl;
}
return data;
}

9
dm-cli/Replace.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include <string>
#include "common.hpp"
std::string Replace(std::string source, std::string from, std::string out) {
size_t start = 0, stop = 0;
start = source.find(from);
stop = from.length();
if (start > -1 && stop) return source.replace(start, stop, out);
return std::string("");
}

22
dm-cli/ShowDebug.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "common.hpp"
#include <iostream>
#include <sstream>
using namespace std;
extern char** global_argv;
extern int global_argc;
void ShowDebug(std::string url, std::string result, bool success, std::string decoded) {
std::ostringstream stream;
for (int i = 0; i < global_argc; ++i) {
if (i) stream << ' ';
stream << global_argv[i];
}
std::string cmdstring = stream.str();
cout << "---------------DEBUG---------------" << endl;
cout << "command: " << cmdstring << endl;
cout << "success: " << success << endl;
cout << "url: " << url << endl;
cout << "Result: " << endl << result << endl;
cout << "Payload: " << endl << decoded << endl;
cout << "---------------END DEBUG---------------" << endl;
}

View File

@@ -0,0 +1,9 @@
Usage: dm-cli account status [options] <bitcoin_address>
Show info about a bitdeals user.
-f|--feedbacks [p|n] Show last 1K received feedbacks.
Use p or n to filter only positive or negative feedbacks.
-i|--info Show user account details.
-r|--rating Show user rating information.
-s|--status Show user account status (default action).

View File

@@ -0,0 +1,9 @@
Использование: dm-cli account status [параметры] <биткоин адрес>
Показать информацию о пользователе bitdeals.
-f|--feedbacks [p|n] Показать последнюю 1 тыс. полученных отзывов.
Используй p или n для фильтрации только позитивных или негативных отзывов.
-i|--info Показать данные учётной записи.
-r|--rating Показать данные рейтинга.
-s|--status Показать статус пользователя (действие по умолчанию).

View File

@@ -0,0 +1,15 @@
Usage: dm-cli account update [options] <bitcoin_address>
Update user account data.
To change the account data you should sign the current date and new user data
with your bitcoin private key. For example, current date, new bitmessage, signature:
2020-01-01
BM-2cUuxYUFWLCrtpKRMdHCpU1QKEuFtHh7vu
IEF1ysyhu8ps0m5xSJaZZg/5hBylmcWQQggkGO7yNN8iMf/EA2O287kxi58xCDDtxvdsC0TWqylVE5MT5CouamU=
-d|--date <yyyy-mm-dd> Current date.
-s|--signature <sig> Bitcoin signature.
-p|--pgp <pgp> Set up an armored RSA pgp key.
-u|--url [+/-]<url> Add/remove an url to trusted list.
-b|--bitmessage Change bitmessage address.

94
dm-cli/base64.cpp Normal file
View File

@@ -0,0 +1,94 @@
#include "base64.h"
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
size_t i = 0;
size_t j = 0;
size_t char_array_3[3];
size_t char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
size_t in_len = encoded_string.size();
size_t i = 0;
size_t j = 0;
size_t in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

12
dm-cli/base64.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef _BASE64_H_
#define _BASE64_H_
#include <string>
/// @brief Кодирование в BASE64
/// @param bytes_to_encode буфер для декодирования
/// @param in_len длина буфера
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len);
/// @brief Декодирование из BASE64
/// @param encoded_string Строкадля декодирования
std::string base64_decode(std::string const& encoded_string);
#endif

1
dm-cli/check.sh Normal file
View File

@@ -0,0 +1 @@
./dm-cli --address https://testnet-dm2.bitdeals.org --debug account update -d 2020-01-01 -s IEF1ysyhu8ps0m5xSJaZZg/5hBylmcWQQggkGO7yNN8iMf/EA2O287kxi58xCDDtxvdsC0TWqylVE5MT5CouamU= -b btmmessage mnumHs9HQMrw2Q1iKLNnx9NzExS7nMLmyp

28
dm-cli/cleanHtml.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "cleanHtml.h"
std::string cleanup_html(std::string const &encoded_string)
{
size_t start = encoded_string.find("<pre>") + 5;
size_t last = encoded_string.find("</pre>");
std::string ret;
ret = encoded_string.substr(start, last - start);
size_t b = 0;
for (size_t a = b; a < ret.length(); a++)
{
if (ret[a] == '<')
{
for (size_t b = a; b < ret.length(); b++)
{
if (ret[b] == '>')
{
ret.erase(a, (b - a + 1));
break;
}
}
}
}
return ret;
}

9
dm-cli/cleanHtml.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _CLEANHTML_H_
#define _CLEANHTML_H_
#include <string>
/// @brief Удаление HTML тэгов
/// @param encoded_string срока с HTML
std::string cleanup_html(std::string const& encoded_string);
#endif

53
dm-cli/common.hpp Normal file
View File

@@ -0,0 +1,53 @@
#ifndef _COMMON_
#define _COMMON_
#include <string>
#define PROGRAM_VERSION "1.0.1"
#define MAIN_HELP_RU "main_help_ru.txt"
#define MAIN_HELP_EN "main_help_en.txt"
#define ACCOUNT_STATUS_RU "account_status_ru.txt"
#define ACCOUNT_STATUS_EN "account_status_en.txt"
#define ACCOUNT_UPDATE_RU "account_update_en.txt"
#define ACCOUNT_UPDATE_EN "account_update_ru.txt"
#define DEALS_EN "deals_en.txt"
#define DEALS_RU "deals_ru.txt"
#define DEAL_STATUS_RU "deal_status_ru.txt"
#define DEAL_STATUS_EN "deal_status_en.txt"
#define DEAL_COMPLETE_RU "deal_complete_ru.txt"
#define DEAL_COMPLETE_EN "deal_complete_en.txt"
#define DEAL_CANCEL_RU "deal_cancel_ru.txt"
#define DEAL_CANCEL_EN "deal_cancel_en.txt"
#define DEAL_NEGATIVE_RU "deal_negative_ru.txt"
#define DEAL_NEGATIVE_EN "deal_negative_en.txt"
// LANG=ru_RU.UTF-8
#define LANG_RU "ru_RU.UTF-8"
#define DEFAULTADDRESS "https://127.0.0.1:4999"
#define ACCOUNT_UPDATE_URL "tpl/accountupdateurl.txt"
#define ACCOUNT_UPDATE_BTM "tpl/accountupdatebtm.txt"
#define ACCOUNT_UPDATE_PGP "tpl/accountupdatepgp.txt"
/*
Show help screen.
*/
void show_help(bool isRulang);
/// @brief Отображает отладочную информацию
void ShowDebug(std::string url, std::string result, bool success, std::string decoded);
/// @breef read file
std::string ReadFile(const char *filename);
/// @breef Replace in string
std::string Replace(std::string source, std::string from, std::string out);
#endif

1
dm-cli/createdistr.sh Normal file
View File

@@ -0,0 +1 @@
tar -czvf dm-cli_distr.tar.gz tpl dm-cli *.txt

12
dm-cli/deal_cancel_en.txt Normal file
View File

@@ -0,0 +1,12 @@
Usage: dm-cli deal cancel [options] <bitcoin_address>
Cancel a deal
To cancel a deal you should sign the deal payment address and feeback text message
with the seller bitcoin private key. For example, address, feedback, signature:
2NEVoXnRgUeb3j17s7pP6DoFHBVHMb94gF5
seller random cancel reason text
IEOGJ5q/a2cWJPMosQmCHNN6RsXF8Xy/UgaazIoY2i5udbw/v38g1/PdWs451dvFr9Iq/Bq5YL2YdpUFTuijQ60=
-m|--message <text> Deal cancel reason text message
-s|--signature <sig> Bitcoin signature.

12
dm-cli/deal_cancel_ru.txt Normal file
View File

@@ -0,0 +1,12 @@
Использование: dm-cli deal cancel [параметры] <биткоин адрес>
Отменить сделку
Для отмены сделки вы должны подписать адрес оплаты сделки и текст отзыва биткоин
ключом продавца. Например, адрес, отзыв, подпись:
2NEVoXnRgUeb3j17s7pP6DoFHBVHMb94gF5
seller random cancel reason text
IEOGJ5q/a2cWJPMosQmCHNN6RsXF8Xy/UgaazIoY2i5udbw/v38g1/PdWs451dvFr9Iq/Bq5YL2YdpUFTuijQ60=
-m|--message <text> Текст сообщения с причино отмены сделки
-s|--signature <sig> Биткоин подпись.

View File

@@ -0,0 +1,6 @@
Usage: dm-cli deal complete [options] { <bitcoin_address>|<deal_code> }
Complete a deal
-p|--positive Complete deal with positive feedback. (default action)
-r|--refund <num> Refund % number of a deal sum to the customer. Format: 100.00[%]

View File

@@ -0,0 +1,6 @@
Использование: dm-cli deal complete [параметры] { <биткоин адес>|<код сделки> }
Завершить сделку
-p|--positive Завершить сделку с позитивным отзывом. (действие по умолчанию)
-r|--refund <num> Возврат % от суммы сделки покупателю. Формат: 100.00[%]

View File

@@ -0,0 +1,12 @@
Usage: dm-cli deal negative [options] <bitcoin_address>
Leave a negative feedback
To leave a negative feedback you should sign the deal payment address and feeback text message
with the customer bitcoin private key. For example, address, feedback, signature:
2NEVoXnRgUeb3j17s7pP6DoFHBVHMb94gF5
customer random feedback text
H1u44C5cyEeKjHqnNnn09B8mIRMdqgR8PD9AeF8mv+cCEqM9HPkcWLrc8a/v2po2YmXJSeya6uy0XaLnmmmO/1o=
-m|--message <text> Deal feedback text message
-s|--signature <sig> Bitcoin signature.

View File

@@ -0,0 +1,12 @@
Использование: dm-cli deal negative [options] <биткоин адрес>
Оставить негативный отзыв
Для оставления негативного отзыва ва должны подписать адрес оплаты сделки и текст отзыва биткоин
ключом покупателя. Например, адрес, отзыв, подпись:
2NEVoXnRgUeb3j17s7pP6DoFHBVHMb94gF5
customer random feedback text
H1u44C5cyEeKjHqnNnn09B8mIRMdqgR8PD9AeF8mv+cCEqM9HPkcWLrc8a/v2po2YmXJSeya6uy0XaLnmmmO/1o=
-m|--message <text> Текст сообщения отзыва
-s|--signature <sig> Биткоин подпись.

View File

@@ -0,0 +1,7 @@
Usage: dm-cli deal status { <bitcoin_address>|<deal_code> }
Show a deal data details
To show a deal details use a bitcoin payment address or a deal code.
-i|--is-paid Check a deal is Paid. Returns an exit code 0 if deal is Paid, or non-zero otherwise.

View File

@@ -0,0 +1,7 @@
Использование: dm-cli deal status { <биткоин адрес>|<код сделки> }
Показать детали сделки
Чтобы показать детали сделки используйте адрес оплаты сделки или код сделки.
-i|--is-paid Проверить является ли сделка оплаченной. Возвращает exit code 0 если сделка является оплаченной, или не ноль в других случаях.

17
dm-cli/deals_en.txt Normal file
View File

@@ -0,0 +1,17 @@
Usage: dm-cli deal create [options]
Create a new deal
[smhd] means seconds, minutes, hours, days.
For example: --leave-before 2020-01-01, or --leave-before 10d.
-a|--at Deal site
-s|--seller Seller user in deal
-c|--customer Customer user in deal
-t|--type [prepayment|postpayment]
Deal type
-s|--sum Deal sum in BTC
-l|--leave-before { <yyyy-mm-dd> [hh:mm:ss UTC] | <time>[smhd] }
Deal end date (deal duration); default: 14d
-p|--pay { <yyyy-mm-dd> [hh:mm:ss UTC] | <time>[smhd] }
Time for make payment; default: 1d

17
dm-cli/deals_ru.txt Normal file
View File

@@ -0,0 +1,17 @@
Использование: dm-cli deal create [параметры]
Создать новую сделку
[smhd] означает секунды, минуты, часы, дни.
Например: --leave-before 2020-01-01, or --leave-before 10d.
-a|--at Сайт проведения сделки
-s|--seller Продавец в сделке
-c|--customer Покупатель в сделке
-t|--type [prepayment|postpayment]
Тип сделки
-s|--sum Сумма сделки в BTC
-l|--leave-before { <гггг-мм-дд> [чч:мм:сс UTC] | <время>[smhd] }
Время окончания сделки (длительность сделки), по умолчанию: 14d
-p|--pay { <гггг-мм-дд> [чч:мм:сс UTC] | <время>[smhd] }
Время для оплаты сделки; по умолчанию: 1d

19
dm-cli/help.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include "common.hpp"
#include "PrintFile.hpp"
void show_help(bool isRulang)
{
if (isRulang)
{
PrintFile(MAIN_HELP_RU);
}
else
{
PrintFile(MAIN_HELP_EN);
}
}

22875
dm-cli/json.hpp Normal file

File diff suppressed because it is too large Load Diff

115
dm-cli/main.cpp Normal file
View File

@@ -0,0 +1,115 @@
#include "common.hpp"
#include "InputParser.hpp"
#include <iostream>
#include "AccountStatus.hpp"
#include "AccountUpdate.hpp"
#include "Deals.hpp"
using namespace std;
char** global_argv;
int global_argc;
const char *GetEnv(const char *tag, const char *def = nullptr) noexcept
{
const char *ret = getenv(tag);
return ret ? ret : def;
}
bool IsRuLang()
{
if (GetEnv("LANG"))
{
string rulang("ru_RU.UTF-8");
string curlang(GetEnv("LANG"));
if (rulang.compare(curlang) == 0)
{
return true;
}
}
return false;
}
int main(int argc, char *argv[])
{
global_argv = argv;
global_argc = argc;
InputParser input(argc, argv);
int retcode = 0;
bool isRuLang = IsRuLang();
bool isDebug = false;
string address(DEFAULTADDRESS);
if (input.cmdOptionExists("--debug"))
{
isDebug = true;
}
const string &addressarg = input.getCmdOption("--address");
if (!addressarg.empty())
{
address = addressarg;
}
if (input.cmdOptionExists("--version"))
{
cout << "Version: " << PROGRAM_VERSION << endl;
return retcode;
}
if (input.cmdOptionExists("account") && input.cmdOptionExists("status"))
{
if (input.cmdOptionExists("--help"))
{
AccountHelp(isRuLang);
return retcode;
}
if (input.cmdOptionExists("-i") || input.cmdOptionExists("--info"))
{
return AccountInfo(address, argv[argc - 1], isDebug);
}
// -r|--rating Показать данные рейтинга.
if (input.cmdOptionExists("-r") || input.cmdOptionExists("--raiting"))
{
return AccountRaiting(address, argv[argc - 1], isDebug);
}
if (input.cmdOptionExists("-s") || input.cmdOptionExists("--status"))
{
return AccountStatus(address, argv[argc - 1], isDebug);
}
// -f|--feedbacks [p|n] Показать последнюю 1 тыс. полученных отзывов. Используй p или n для фильтрации только позитивных или негативных
if (input.cmdOptionExists("-f") || input.cmdOptionExists("--feedbacks"))
{
int fb = 0; // all
const string &fbp = input.getCmdOption("-f");
if (!fbp.empty())
{
if (fbp.compare("p") == 0)
fb = 1;
if (fbp.compare("n") == 0)
fb = 2;
}
const string &fbps = input.getCmdOption("--feedbacks");
if (!fbps.empty())
{
if (fbps.compare("p") == 0)
fb = 1;
if (fbps.compare("n") == 0)
fb = 2;
}
return AccountFeedbacks(address, argv[argc - 1], isDebug, fb);
}
return AccountStatus(address, argv[argc - 1], isDebug);
}
if (input.cmdOptionExists("account") && input.cmdOptionExists("update")) {
AccountUpdate acu(input, isRuLang);
return acu.Process();
}
if (input.cmdOptionExists("deal")) {
Deals deals(input, isRuLang, address,isDebug);
return deals.Process();
}
show_help(isRuLang);
return retcode;
}

23
dm-cli/main_help_en.txt Normal file
View File

@@ -0,0 +1,23 @@
Usage: dm-cli [global] <group> [<args>]
Global options:
--debug print debug information, input and output API requests
--address <ip:port> address of the bitdeals dm, default: 127.0.0.1:4999
--help print condensed help for all subcommands
--version print version string
dm-cli account status <bitcoin_address>
Show account data details
dm-cli account update [options]
Update your account data
dm-cli deal create
Create a new deal
dm-cli deal status
Show deal data details
dm-cli deal complete
Complete deal
dm-cli deal cancel
Cancel deal
dm-cli deal negative
Leave negative feedback

23
dm-cli/main_help_ru.txt Normal file
View File

@@ -0,0 +1,23 @@
Использование: dm-cli [глобальные параметры] <группа> [<аргументы>]
Глобальные параметры:
--debug выводить отладочную информацию, входные и выходные API-запросы
--address <ip:port> адрес модуля сделок (dm), по умолчанию: 127.0.0.1:4999
--help вывести краткую справку по всем подкомандам
--version вывести строку версии
dm-cli account status <биткоин адрес>
Показать учётные данные аккаунта
dm-cli account update [параметры]
Изменить учётные данные аккаунта
dm-cli deal create
Создать новую сделку
dm-cli deal status
Показать данные сделки
dm-cli deal complete
Завершить сделку
dm-cli deal cancel
Отменить сделку
dm-cli deal negative
Оставить негативный отзыв

View File

@@ -0,0 +1 @@
-X POST "{{ADDRESS}}/api/v1/client/update" -H "Content-Type: application/json" -d "{\"address\":\"{{BT}}\",\"date\":\"{{DATE}}\",\"bitmessage\":[\"{{BTM}}\"],\"sign\":\"{{SIGN}}\"}"

View File

@@ -0,0 +1 @@
-X POST "{{ADDRESS}}/api/v1/client/update" -H "Content-Type: application/json" -d "{\"address\":\"{{BT}}\",\"date\":\"{{DATE}}\",\"pgp\":\"{{PGP}}\",\"sign\":\"{{SIGN}}\"}"

View File

@@ -0,0 +1 @@
-X POST "{{ADDRESS}}/api/v1/client/update" -H "Content-Type: application/json" -d "{\"address\":\"{{BT}}\",\"date\":\"{{DATE}}\",\"url\":[{{URLS}}], \"sign\":\"{{SIGN}}\"}"