public inbox for [email protected]
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot
@ 2024-09-10 21:44 Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 2/9] gwarnt: Create initial P2P ad data structure Alviro Iskandar Setiawan
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Hi,

Following the chief's approval of the concept to develop a bot for
identifying arbitrage opportunities between two exchanges, I am excited
to introduce the initial version of the GNU/Weeb Arbitrage Opportunity
Notification Bot.

This bot efficiently scans multiple exchanges for a given currency pair,
detecting profitable arbitrage opportunities in real-time. Additionally,
it can automatically send these alerts to a designated Telegram chat.

Currently, the bot onlu supports Binance and OKX exchanges. However,
it is designed to be easily extensible to support additional exchanges.

I believe this tool would be a valuable addition to the GNU/Weeb group
and would streamline the process of identifying arbitrage possibilities.
Please consider integrating this bot (@FreezingNightBot) into the group
for the benefit of our community.

Thank you for your consideration!

The following changes since commit acd5fc366c900e29d88ffac8ccac96dfe87df23d:

  Initial commit (2024-09-11 02:28:37 +0700)

are available in the Git repository at:

  https://github.com/alviroiskandar/gwarnt.git master

for you to fetch changes up to 315cd5c14d3fbc8a7cbf5b49038ed61674ed537e:

  gwarnt: Add Telegram bot usage to the README file (2024-09-11 04:07:44 +0700)

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>

----------------------------------------------------------------

Alviro Iskandar Setiawan (9):
  gwarnt: Create initial P2P ad data structure
  gwarnt: p2p: Add P2P Binance
  gwarnt: p2p: Add P2P OKX
  gwarnt: Create function to find arbitrage opportunities
  gwarnt: p2p/binance: Fix invalid page
  gwarnt: Create the initial example
  gwarnt: Add README file
  gwarnt: Add Telegram bot
  gwarnt: Add Telegram bot usage to the README file

 Makefile                   |   7 +-
 README                     |  21 ++++
 src/gwarnt/arbitrage.cpp   |  27 ++++
 src/gwarnt/arbitrage.hpp   |  21 ++++
 src/gwarnt/entry.cpp       | 248 +++++++++++++++++++++++++++++++++++++
 src/gwarnt/net.cpp         |   2 +
 src/gwarnt/p2p/binance.cpp | 142 +++++++++++++++++++++
 src/gwarnt/p2p/binance.hpp |  42 +++++++
 src/gwarnt/p2p/okx.cpp     | 101 +++++++++++++++
 src/gwarnt/p2p/okx.hpp     |  35 ++++++
 src/gwarnt/p2p_ad.cpp      |  31 +++++
 src/gwarnt/p2p_ad.hpp      |  31 +++++
 src/gwarnt/tgbot.cpp       |  75 +++++++++++
 src/gwarnt/tgbot.hpp       |  46 +++++++
 14 files changed, 828 insertions(+), 1 deletion(-)
 create mode 100644 README
 create mode 100644 src/gwarnt/arbitrage.cpp
 create mode 100644 src/gwarnt/arbitrage.hpp
 create mode 100644 src/gwarnt/p2p/binance.cpp
 create mode 100644 src/gwarnt/p2p/binance.hpp
 create mode 100644 src/gwarnt/p2p/okx.cpp
 create mode 100644 src/gwarnt/p2p/okx.hpp
 create mode 100644 src/gwarnt/p2p_ad.cpp
 create mode 100644 src/gwarnt/p2p_ad.hpp
 create mode 100644 src/gwarnt/tgbot.cpp
 create mode 100644 src/gwarnt/tgbot.hpp

-- 
Alviro Iskandar Setiawan


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC PATCH 2/9] gwarnt: Create initial P2P ad data structure
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 3/9] gwarnt: p2p: Add P2P Binance Alviro Iskandar Setiawan
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Create an abstraction to make P2P ad uniform across multiple
crypto currency exchanges.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 Makefile              |  3 ++-
 src/gwarnt/p2p_ad.cpp | 31 +++++++++++++++++++++++++++++++
 src/gwarnt/p2p_ad.hpp | 31 +++++++++++++++++++++++++++++++
 3 files changed, 64 insertions(+), 1 deletion(-)
 create mode 100644 src/gwarnt/p2p_ad.cpp
 create mode 100644 src/gwarnt/p2p_ad.hpp

diff --git a/Makefile b/Makefile
index ba50192..7dbfb71 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,8 @@ C_SOURCES	:=
 CXX_SOURCES	:= \
 	src/gwarnt/entry.cpp \
 	src/gwarnt/helpers.cpp \
-	src/gwarnt/net.cpp
+	src/gwarnt/net.cpp \
+	src/gwarnt/p2p_ad.cpp
 
 C_OBJECTS	:= $(C_SOURCES:.c=.o)
 CXX_OBJECTS	:= $(CXX_SOURCES:.cpp=.o)
diff --git a/src/gwarnt/p2p_ad.cpp b/src/gwarnt/p2p_ad.cpp
new file mode 100644
index 0000000..7e8e0e5
--- /dev/null
+++ b/src/gwarnt/p2p_ad.cpp
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <gwarnt/p2p_ad.hpp>
+#include <cstddef>
+
+namespace gwarnt {
+
+std::string p2p_ad::dump(void) const
+{
+	std::string ret = "";
+	size_t i = 0;
+
+	ret += "ad_id: " + ad_id_ + "\n";
+	ret += "merchant_name: " + merchant_name_ + "\n";
+	ret += "fiat: " + fiat_ + "\n";
+	ret += "crypto: " + crypto_ + "\n";
+	ret += "trade_type: " + trade_type_ + "\n";
+	ret += "exchange: " + exchange_ + "\n";
+	ret += "price: " + std::to_string(price_) + "\n";
+	ret += "min_amount: " + std::to_string(min_amount_) + "\n";
+	ret += "max_amount: " + std::to_string(max_amount_) + "\n";
+	ret += "tradable_amount: " + std::to_string(tradable_amount_) + "\n";
+
+	ret += "methods: ";
+	for (const auto &m : methods_)
+		ret += (i++ > 0 ? ", " : "") + m;
+
+	return ret;
+}
+
+} /* namespace gwarnt */
diff --git a/src/gwarnt/p2p_ad.hpp b/src/gwarnt/p2p_ad.hpp
new file mode 100644
index 0000000..85799b2
--- /dev/null
+++ b/src/gwarnt/p2p_ad.hpp
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef GWARNT__P2PDS_HPP
+#define GWARNT__P2PDS_HPP
+
+#include <vector>
+#include <string>
+
+namespace gwarnt {
+
+struct p2p_ad {
+	std::string	ad_id_;
+	std::string	merchant_name_;
+	std::string	fiat_;
+	std::string	crypto_;
+	std::string	trade_type_;
+	std::string	exchange_;
+
+	double	price_;
+	double	min_amount_;
+	double	max_amount_;
+	double	tradable_amount_;
+
+	std::vector<std::string>	methods_;
+
+	std::string dump(void) const;
+};
+
+} /* namespace gwarnt */
+
+#endif /* #ifndef GWARNT__P2PDS_HPP */
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 3/9] gwarnt: p2p: Add P2P Binance
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 2/9] gwarnt: Create initial P2P ad data structure Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 4/9] gwarnt: p2p: Add P2P OKX Alviro Iskandar Setiawan
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Add an interface to collect P2P ads from Binance.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 Makefile                   |   3 +-
 src/gwarnt/p2p/binance.cpp | 142 +++++++++++++++++++++++++++++++++++++
 src/gwarnt/p2p/binance.hpp |  42 +++++++++++
 3 files changed, 186 insertions(+), 1 deletion(-)
 create mode 100644 src/gwarnt/p2p/binance.cpp
 create mode 100644 src/gwarnt/p2p/binance.hpp

diff --git a/Makefile b/Makefile
index 7dbfb71..732d1b7 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,8 @@ CXX_SOURCES	:= \
 	src/gwarnt/entry.cpp \
 	src/gwarnt/helpers.cpp \
 	src/gwarnt/net.cpp \
-	src/gwarnt/p2p_ad.cpp
+	src/gwarnt/p2p_ad.cpp \
+	src/gwarnt/p2p/binance.cpp
 
 C_OBJECTS	:= $(C_SOURCES:.c=.o)
 CXX_OBJECTS	:= $(CXX_SOURCES:.cpp=.o)
diff --git a/src/gwarnt/p2p/binance.cpp b/src/gwarnt/p2p/binance.cpp
new file mode 100644
index 0000000..541a5de
--- /dev/null
+++ b/src/gwarnt/p2p/binance.cpp
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <gwarnt/p2p/binance.hpp>
+#include <gwarnt/helpers.hpp>
+#include <gwarnt/json.hpp>
+#include <exception>
+
+using json = nlohmann::json;
+
+namespace gwarnt {
+namespace p2p {
+
+binance::binance(void)
+{
+	net_.add_header("Content-Type", "application/json");
+	net_.add_header("x-passthrough-token", "");
+}
+
+binance::~binance(void) = default;
+
+void binance::set_fiat(const std::string &fiat)
+{
+	fiat_ = fiat;
+}
+
+void binance::set_crypto(const std::string &crypto)
+{
+	crypto_ = crypto;
+}
+
+void binance::set_trade_type(const std::string &trade_type)
+{
+	trade_type_ = trade_type;
+}
+
+std::vector<gwarnt::p2p_ad> binance::get_data(void)
+{
+	return get_data(fiat_, crypto_, trade_type_);
+}
+
+std::vector<gwarnt::p2p_ad> binance::get_data(const std::string &fiat,
+					      const std::string &crypto,
+					      const std::string &trade_type,
+					      uint64_t page)
+{
+	std::vector<gwarnt::p2p_ad> ads;
+
+	if (page == ~0ull) {
+		std::vector<gwarnt::p2p_ad> tmp;
+		size_t i;
+
+		for (i = 1; i <= 5; i++) {
+			tmp = __get_data(fiat, crypto, trade_type, page);
+			ads.insert(ads.end(), tmp.begin(), tmp.end());
+		}
+	} else {
+		ads = __get_data(fiat, crypto, trade_type, page);
+	}
+
+	return ads;
+}
+
+std::vector<gwarnt::p2p_ad> binance::__get_data(const std::string &fiat,
+						const std::string &crypto,
+						const std::string &trade_type,
+						uint64_t page)
+{
+	std::vector<gwarnt::p2p_ad> ads;
+	std::string type;
+	json j;
+
+	type = trade_type;
+	strtoupper(type);
+	if (type == "BUY")
+		type = "BUY";
+	else if (type == "SELL")
+		type = "SELL";
+	else
+		throw std::runtime_error("Invalid trade type: " + trade_type);
+
+	j["fiat"] = fiat;
+	j["page"] = page;
+	j["rows"] = 20;
+	j["tradeType"] = type;
+	j["asset"] = crypto;
+	j["countries"] = json::array();
+	j["proMerchantAds"] = false;
+	j["shieldMerchantAds"] = false;
+	j["filterType"] = "all";
+	j["periods"] = json::array();
+	j["additionalKycVerifyFilter"] = 0;
+	j["publisherType"] = "merchant";
+	j["payTypes"] = json::array();
+	j["classifies"] = json::array();
+	j["classifies"].push_back("mass");
+	j["classifies"].push_back("profession");
+	j["classifies"].push_back("fiat_trade");
+
+	net_.set_url("https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search");
+	net_.set_method("POST");
+	net_.set_data(j.dump());
+	net_.set_curl_opt(CURLOPT_ACCEPT_ENCODING, "gzip");
+	net_.exec();
+
+	const std::string &res = net_.get_resp();
+	j = json::parse(res);
+
+	for (const auto &i : j["data"]) {
+		try {
+			const auto &adv = i["adv"];
+			gwarnt::p2p_ad p;
+
+			p.ad_id_ = adv["advNo"];
+			p.merchant_name_ = i["advertiser"]["nickName"];
+			p.fiat_ = adv["fiatUnit"];
+			p.crypto_ = adv["asset"];
+			p.trade_type_ = adv["tradeType"];
+			p.price_ = strtod(adv["price"].get<std::string>().c_str(), nullptr);
+			p.min_amount_ = strtod(adv["minSingleTransAmount"].get<std::string>().c_str(), nullptr);
+			p.max_amount_ = strtod(adv["maxSingleTransAmount"].get<std::string>().c_str(), nullptr);
+			p.tradable_amount_ = strtod(adv["tradableQuantity"].get<std::string>().c_str(), nullptr);
+			p.exchange_ = "binance";
+
+			for (auto &j : adv["tradeMethods"]) {
+				try {
+					p.methods_.push_back(j["tradeMethodName"]);
+				} catch (...) {
+					p.methods_.push_back("p:"+j["identifier"].get<std::string>());
+				}
+			}
+
+			ads.push_back(p);
+		} catch (std::exception &e) {
+			printf("gwarnt::p2p::binance::__get_data: %s\n", e.what());
+		}
+	}
+
+	return ads;
+}
+
+} /* namespace gwarnt */
+} /* namespace p2p */
diff --git a/src/gwarnt/p2p/binance.hpp b/src/gwarnt/p2p/binance.hpp
new file mode 100644
index 0000000..1b73dbe
--- /dev/null
+++ b/src/gwarnt/p2p/binance.hpp
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef GWARNT__P2P__BINANCE_HPP
+#define GWARNT__P2P__BINANCE_HPP
+
+#include <gwarnt/net.hpp>
+#include <gwarnt/p2p_ad.hpp>
+
+namespace gwarnt {
+namespace p2p {
+
+class binance {
+public:
+	binance(void);
+	~binance(void);
+
+	void set_fiat(const std::string &fiat);
+	void set_crypto(const std::string &crypto);
+	void set_trade_type(const std::string &trade_type);
+	std::vector<gwarnt::p2p_ad> get_data(void);
+	std::vector<gwarnt::p2p_ad> get_data(const std::string &fiat,
+					     const std::string &crypto,
+					     const std::string &trade_type,
+					     uint64_t page = ~0ull);
+
+private:
+	std::vector<gwarnt::p2p_ad> __get_data(const std::string &fiat,
+					      const std::string &crypto,
+					      const std::string &trade_type,
+					      uint64_t page);
+
+
+	gwarnt::net	net_;
+	std::string	fiat_;
+	std::string	crypto_;
+	std::string	trade_type_;
+};
+
+} /* namespace p2p */
+} /* namespace gwarnt */
+
+#endif /* GWARNT__P2P__BINANCE_HPP */
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 4/9] gwarnt: p2p: Add P2P OKX
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 2/9] gwarnt: Create initial P2P ad data structure Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 3/9] gwarnt: p2p: Add P2P Binance Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 5/9] gwarnt: Create function to find arbitrage opportunities Alviro Iskandar Setiawan
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Add an interface to collect P2P ads from OKX.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 Makefile               |   3 +-
 src/gwarnt/p2p/okx.cpp | 101 +++++++++++++++++++++++++++++++++++++++++
 src/gwarnt/p2p/okx.hpp |  35 ++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)
 create mode 100644 src/gwarnt/p2p/okx.cpp
 create mode 100644 src/gwarnt/p2p/okx.hpp

diff --git a/Makefile b/Makefile
index 732d1b7..6aa0b32 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,8 @@ CXX_SOURCES	:= \
 	src/gwarnt/helpers.cpp \
 	src/gwarnt/net.cpp \
 	src/gwarnt/p2p_ad.cpp \
-	src/gwarnt/p2p/binance.cpp
+	src/gwarnt/p2p/binance.cpp \
+	src/gwarnt/p2p/okx.cpp
 
 C_OBJECTS	:= $(C_SOURCES:.c=.o)
 CXX_OBJECTS	:= $(CXX_SOURCES:.cpp=.o)
diff --git a/src/gwarnt/p2p/okx.cpp b/src/gwarnt/p2p/okx.cpp
new file mode 100644
index 0000000..f37ace4
--- /dev/null
+++ b/src/gwarnt/p2p/okx.cpp
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <gwarnt/p2p/okx.hpp>
+#include <gwarnt/helpers.hpp>
+#include <gwarnt/json.hpp>
+#include <exception>
+
+using json = nlohmann::json;
+
+namespace gwarnt {
+namespace p2p {
+
+okx::okx(void)
+{
+	net_.add_header("Content-Type", "application/json");
+}
+
+okx::~okx(void) = default;
+
+void okx::set_fiat(const std::string &fiat)
+{
+	fiat_ = fiat;
+}
+
+void okx::set_crypto(const std::string &crypto)
+{
+	crypto_ = crypto;
+}
+
+void okx::set_trade_type(const std::string &trade_type)
+{
+	trade_type_ = trade_type;
+}
+
+std::vector<gwarnt::p2p_ad> okx::get_data(void)
+{
+	return get_data(fiat_, crypto_, trade_type_);
+}
+
+std::vector<gwarnt::p2p_ad> okx::get_data(const std::string &fiat,
+					  const std::string &crypto,
+					  const std::string &trade_type)
+{
+	std::vector<gwarnt::p2p_ad> ads;
+	std::string type;
+	std::string url;
+	std::string p;
+	json *jent;
+	json j;
+
+	type = trade_type;
+	strtoupper(type);
+	if (type != "BUY" && type != "SELL")
+		throw std::runtime_error("Invalid trade type: " + trade_type);
+
+	p = "side=" + trade_type + "&fiatCurrency=" + fiat + "&cryptoCurrency=" + crypto;
+	url = "https://www.okx.com/v3/c2c/tradingOrders/getMarketplaceAdsPrelogin?&paymentMethod=all&userType=all&hideOverseasVerificationAds=false&sortType=price_asc&limit=1000&currentPage=1&numberPerPage=1000&"
+		+ p;
+
+	net_.set_method("GET");
+	net_.set_url(url);
+	net_.set_curl_opt(CURLOPT_ACCEPT_ENCODING, "gzip");
+	net_.exec();
+
+	const std::string &res = net_.get_resp();
+	j = json::parse(res);
+
+	if (type == "SELL")
+		jent = &j["data"]["sell"];
+	else
+		jent = &j["data"]["buy"];
+
+	for (const auto &q : *jent) {
+		try {
+			gwarnt::p2p_ad p;
+
+			p.ad_id_ = q["id"];
+			p.merchant_name_ = q["nickName"];
+			p.trade_type_ = type;
+			p.fiat_ = fiat;
+			p.crypto_ = crypto;
+			p.price_ = strtod(q["price"].get<std::string>().c_str(), nullptr);
+			p.min_amount_ = strtod(q["quoteMinAmountPerOrder"].get<std::string>().c_str(), nullptr);
+			p.max_amount_ = strtod(q["quoteMaxAmountPerOrder"].get<std::string>().c_str(), nullptr);
+			p.tradable_amount_ = strtod(q["availableAmount"].get<std::string>().c_str(), nullptr);
+			p.exchange_ = "okx";
+
+			for (auto &r : q["paymentMethods"])
+				p.methods_.push_back(r);
+
+			ads.push_back(p);
+		} catch (const std::exception &e) {
+			printf("gwarnt::p2p::okx::get_data: %s\n", e.what());
+		}
+	}
+
+	return ads;
+}
+
+} /* namespace gwarnt */
+} /* namespace p2p */
diff --git a/src/gwarnt/p2p/okx.hpp b/src/gwarnt/p2p/okx.hpp
new file mode 100644
index 0000000..3422ac5
--- /dev/null
+++ b/src/gwarnt/p2p/okx.hpp
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef GWARNT__P2P__OKX_HPP
+#define GWARNT__P2P__OKX_HPP
+
+#include <gwarnt/net.hpp>
+#include <gwarnt/p2p_ad.hpp>
+
+namespace gwarnt {
+namespace p2p {
+
+class okx {
+public:
+	okx(void);
+	~okx(void);
+
+	void set_fiat(const std::string &fiat);
+	void set_crypto(const std::string &crypto);
+	void set_trade_type(const std::string &trade_type);
+	std::vector<gwarnt::p2p_ad> get_data(void);
+	std::vector<gwarnt::p2p_ad> get_data(const std::string &fiat,
+					     const std::string &crypto,
+					     const std::string &trade_type);
+
+private:
+	gwarnt::net	net_;
+	std::string	fiat_;
+	std::string	crypto_;
+	std::string	trade_type_;
+};
+
+} /* namespace p2p */
+} /* namespace gwarnt */
+
+#endif /* GWARNT__P2P__OKX_HPP */
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 5/9] gwarnt: Create function to find arbitrage opportunities
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (2 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 4/9] gwarnt: p2p: Add P2P OKX Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 6/9] gwarnt: p2p/binance: Fix invalid page Alviro Iskandar Setiawan
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

After collecting buy and sell ads from two different exchanges, find
the arbitrage opportunities.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 Makefile                 |  1 +
 src/gwarnt/arbitrage.cpp | 27 +++++++++++++++++++++++++++
 src/gwarnt/arbitrage.hpp | 21 +++++++++++++++++++++
 3 files changed, 49 insertions(+)
 create mode 100644 src/gwarnt/arbitrage.cpp
 create mode 100644 src/gwarnt/arbitrage.hpp

diff --git a/Makefile b/Makefile
index 6aa0b32..f775bfc 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ DEPFLAGS = -MT "$@" -MMD -MP -MF $(@:%.o=%.d)
 
 C_SOURCES	:=
 CXX_SOURCES	:= \
+	src/gwarnt/arbitrage.cpp \
 	src/gwarnt/entry.cpp \
 	src/gwarnt/helpers.cpp \
 	src/gwarnt/net.cpp \
diff --git a/src/gwarnt/arbitrage.cpp b/src/gwarnt/arbitrage.cpp
new file mode 100644
index 0000000..ec7c771
--- /dev/null
+++ b/src/gwarnt/arbitrage.cpp
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <gwarnt/arbitrage.hpp>
+
+namespace gwarnt {
+
+std::vector<struct arb_opp> find_arbitrage_opps(const std::vector<p2p_ad> &sell,
+						const std::vector<p2p_ad> &buy)
+{
+	std::vector<struct arb_opp> ret;
+
+	for (const auto &i : buy) {
+		for (const auto &j : sell) {
+			if (j.price_ < i.price_) {
+				struct arb_opp opp;
+
+				opp.buy = i;
+				opp.sell = j;
+				ret.push_back(opp);
+			}
+		}
+	}
+
+	return ret;
+}
+
+} /* namespace gwarnt */
diff --git a/src/gwarnt/arbitrage.hpp b/src/gwarnt/arbitrage.hpp
new file mode 100644
index 0000000..0b0338e
--- /dev/null
+++ b/src/gwarnt/arbitrage.hpp
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef GWARNT__ARBITRAGE_HPP
+#define GWARNT__ARBITRAGE_HPP
+
+#include <gwarnt/p2p_ad.hpp>
+
+namespace gwarnt {
+
+// Arbitrage opportunity
+struct arb_opp {
+	p2p_ad buy;
+	p2p_ad sell;
+};
+
+std::vector<struct arb_opp> find_arbitrage_opps(const std::vector<p2p_ad> &sell,
+						const std::vector<p2p_ad> &buy);
+
+} /* namespace gwarnt */
+
+#endif /* GWARNT__ARBITRAGE_HPP */
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 6/9] gwarnt: p2p/binance: Fix invalid page
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (3 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 5/9] gwarnt: Create function to find arbitrage opportunities Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 7/9] gwarnt: Create the initial example Alviro Iskandar Setiawan
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

The page passed to __get_data was wrong, it should take from the
iteration variable instead of 'page' argument which is always ~0ull in
value. While in there, also fix the inverted search as Binance will show
SELL ads when the filter is BUY and vice versa.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 src/gwarnt/p2p/binance.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/gwarnt/p2p/binance.cpp b/src/gwarnt/p2p/binance.cpp
index 541a5de..81d23fe 100644
--- a/src/gwarnt/p2p/binance.cpp
+++ b/src/gwarnt/p2p/binance.cpp
@@ -50,7 +50,7 @@ std::vector<gwarnt::p2p_ad> binance::get_data(const std::string &fiat,
 		size_t i;
 
 		for (i = 1; i <= 5; i++) {
-			tmp = __get_data(fiat, crypto, trade_type, page);
+			tmp = __get_data(fiat, crypto, trade_type, i);
 			ads.insert(ads.end(), tmp.begin(), tmp.end());
 		}
 	} else {
@@ -72,9 +72,9 @@ std::vector<gwarnt::p2p_ad> binance::__get_data(const std::string &fiat,
 	type = trade_type;
 	strtoupper(type);
 	if (type == "BUY")
-		type = "BUY";
-	else if (type == "SELL")
 		type = "SELL";
+	else if (type == "SELL")
+		type = "BUY";
 	else
 		throw std::runtime_error("Invalid trade type: " + trade_type);
 
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 7/9] gwarnt: Create the initial example
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (4 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 6/9] gwarnt: p2p/binance: Fix invalid page Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 8/9] gwarnt: Add README file Alviro Iskandar Setiawan
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Initial example to show the usage.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 src/gwarnt/entry.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/src/gwarnt/entry.cpp b/src/gwarnt/entry.cpp
index 4a74df9..044234e 100644
--- a/src/gwarnt/entry.cpp
+++ b/src/gwarnt/entry.cpp
@@ -1,6 +1,48 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
+#include <unistd.h>
+#include <iostream>
+#include <gwarnt/p2p/okx.hpp>
+#include <gwarnt/p2p/binance.hpp>
+#include <gwarnt/arbitrage.hpp>
+
 int main(void)
 {
+	std::vector<gwarnt::p2p_ad> buy_bnc, sell_bnc;
+	std::vector<gwarnt::p2p_ad> buy_okx, sell_okx;
+	std::vector<gwarnt::arb_opp> opportunities;
+	gwarnt::p2p::binance bnc;
+	gwarnt::p2p::okx okx;
+
+	while (1) {
+		printf("Fetching data ...\n");
+		buy_bnc = bnc.get_data("IDR", "USDT", "BUY");
+		sell_bnc = bnc.get_data("IDR", "USDT", "SELL");
+
+		buy_okx = okx.get_data("IDR", "USDT", "BUY");
+		sell_okx = okx.get_data("IDR", "USDT", "SELL");
+		printf("Binance: %lu buy, %lu sell\n", buy_bnc.size(), sell_bnc.size());
+		printf("OKX: %lu buy, %lu sell\n", buy_okx.size(), sell_okx.size());
+
+		opportunities = gwarnt::find_arbitrage_opps(sell_okx, buy_bnc);
+		for (const auto &i : opportunities) {
+			std::cout << i.sell.dump() << std::endl;
+			std::cout << "====================" << std::endl;
+			std::cout << i.buy.dump() << std::endl;
+			std::cout << "----------------------------------------------" << std::endl;
+		}
+
+		opportunities = gwarnt::find_arbitrage_opps(sell_bnc, buy_okx);
+		for (const auto &i : opportunities) {
+			std::cout << i.sell.dump() << std::endl;
+			std::cout << "====================" << std::endl;
+			std::cout << i.buy.dump() << std::endl;
+			std::cout << "----------------------------------------------" << std::endl;
+		}
+
+		printf("Sleeping...\n");
+		sleep(5);
+	}
+
 	return 0;
 }
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 8/9] gwarnt: Add README file
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (5 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 7/9] gwarnt: Create the initial example Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 21:44 ` [RFC PATCH 9/9] gwarnt: Add Telegram bot Alviro Iskandar Setiawan
  2024-09-10 22:21 ` [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Ammar Faizi
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 README | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 README

diff --git a/README b/README
new file mode 100644
index 0000000..23786c8
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+gwarnt
+------
+
+GNU/Weeb Arbitrage Opportunity Notification. Find the arbitrage
+opportunities across two different exchanges for a given currency pair.
+
+## Installation
+
+  sudo apt-get install -y git libcurl4-openssl-dev gcc g++ make;
+  git clone https://github.com/alviroiskandar/gwarnt.git;
+  cd gwarnt;
+  make -j$(nproc);
+
+## Run
+
+  ./gwarnt
+
+-- 
+Alviro Iskandar Setiawan
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC PATCH 9/9] gwarnt: Add Telegram bot
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (6 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 8/9] gwarnt: Add README file Alviro Iskandar Setiawan
@ 2024-09-10 21:44 ` Alviro Iskandar Setiawan
  2024-09-10 22:21 ` [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Ammar Faizi
  8 siblings, 0 replies; 10+ messages in thread
From: Alviro Iskandar Setiawan @ 2024-09-10 21:44 UTC (permalink / raw)
  To: Ammar Faizi, Michael William Jonathan
  Cc: Alviro Iskandar Setiawan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

Send a notification to Telegram using bot if arbitrage opportunities
detected.

Signed-off-by: Alviro Iskandar Setiawan <[email protected]>
---
 Makefile             |   1 +
 src/gwarnt/entry.cpp | 258 ++++++++++++++++++++++++++++++++++++++-----
 src/gwarnt/net.cpp   |   2 +
 src/gwarnt/tgbot.cpp |  75 +++++++++++++
 src/gwarnt/tgbot.hpp |  46 ++++++++
 5 files changed, 356 insertions(+), 26 deletions(-)
 create mode 100644 src/gwarnt/tgbot.cpp
 create mode 100644 src/gwarnt/tgbot.hpp

diff --git a/Makefile b/Makefile
index f775bfc..8de277b 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ CXX_SOURCES	:= \
 	src/gwarnt/helpers.cpp \
 	src/gwarnt/net.cpp \
 	src/gwarnt/p2p_ad.cpp \
+	src/gwarnt/tgbot.cpp \
 	src/gwarnt/p2p/binance.cpp \
 	src/gwarnt/p2p/okx.cpp
 
diff --git a/src/gwarnt/entry.cpp b/src/gwarnt/entry.cpp
index 044234e..9510c3c 100644
--- a/src/gwarnt/entry.cpp
+++ b/src/gwarnt/entry.cpp
@@ -1,47 +1,253 @@
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include <unistd.h>
+#include <ctime>
+#include <cstdlib>
+#include <sstream>
 #include <iostream>
+#include <unordered_map>
+
+#include <gwarnt/tgbot.hpp>
 #include <gwarnt/p2p/okx.hpp>
 #include <gwarnt/p2p/binance.hpp>
 #include <gwarnt/arbitrage.hpp>
+#include <gwarnt/helpers.hpp>
 
-int main(void)
+template <typename T>
+std::string to_string_wp(const T a_value, const int n = 2)
+{
+	std::ostringstream out;
+	out.precision(n);
+	out << std::fixed << a_value;
+	return std::move(out).str();
+}
+
+class gwarnt_bot {
+public:
+	inline void set_token(const std::string &token) { tg_.set_token(token); }
+	inline void set_chat_id(const std::string &chat_id) { chat_id_ = chat_id; }
+
+	void init_get_me(void);
+	void broadcast_opp(const gwarnt::arb_opp &opp);
+	void garbage_collect(void);
+
+private:
+	static constexpr time_t BROADCAST_INTERVAL = 300;
+	gwarnt::tgbot tg_;
+	std::string chat_id_;
+	std::unordered_map<std::string, time_t> broadcast_map_;
+
+	bool opp_need_broadcast(const gwarnt::arb_opp &opp);
+	void insert_broadcast_map(const gwarnt::arb_opp &opp);
+	std::string get_opp_hash(const gwarnt::arb_opp &opp);
+};
+
+inline
+void gwarnt_bot::init_get_me(void)
+{
+	auto j = tg_.get_me();
+	auto username = j["result"]["username"].get<std::string>();
+
+	printf("========================================\n");
+	printf("Bot username   : %s\n", username.c_str());
+	printf("Target Chat ID : %s\n", chat_id_.c_str());
+	printf("========================================\n");
+}
+
+inline
+std::string gwarnt_bot::get_opp_hash(const gwarnt::arb_opp &opp)
+{
+	std::string hash = "";
+
+	hash += opp.buy.ad_id_;
+	hash += opp.sell.ad_id_;
+	hash += to_string_wp(opp.buy.price_);
+	hash += to_string_wp(opp.sell.price_);
+
+	return hash;
+}
+
+inline
+void gwarnt_bot::garbage_collect(void)
+{
+	auto it = broadcast_map_.begin();
+
+	while (it != broadcast_map_.end()) {
+		if (time(NULL) - it->second > BROADCAST_INTERVAL)
+			it = broadcast_map_.erase(it);
+		else
+			++it;
+	}
+}
+
+inline
+bool gwarnt_bot::opp_need_broadcast(const gwarnt::arb_opp &opp)
+{
+	std::string hash = get_opp_hash(opp);
+	auto it = broadcast_map_.find(hash);
+
+	if (it == broadcast_map_.end())
+		return false;
+
+	if (time(NULL) - it->second > BROADCAST_INTERVAL) {
+		broadcast_map_.erase(it);
+		return false;
+	}
+
+	return true;
+}
+
+inline
+void gwarnt_bot::insert_broadcast_map(const gwarnt::arb_opp &opp)
+{
+	std::string hash = get_opp_hash(opp);
+	broadcast_map_[hash] = time(NULL);
+	printf("Inserted hash %s\n", hash.c_str());
+}
+
+static std::string merge_vec_str(const std::vector<std::string> &vec)
+{
+	std::string ret = "";
+	size_t i = 0;
+
+	for (const auto &s : vec)
+		ret += (++i > 1 ? ", " : "") + s;
+
+	return ret;
+}
+
+inline
+void gwarnt_bot::broadcast_opp(const gwarnt::arb_opp &opp)
+{
+	double min_avail;
+	double est_profit;
+
+	if (opp_need_broadcast(opp))
+		return;
+
+	std::string msg = "Arbitrage opportunity detected!\n\n";
+	const auto &b = opp.buy;
+	const auto &s = opp.sell;
+
+	msg += "Buy on " + s.exchange_ + "\n";
+	msg += "m: " + s.ad_id_ + " (" + s.merchant_name_ + ")\n";
+	msg += "price: " + to_string_wp(b.price_, 2) + "\n";
+	msg += "min_buy: " + to_string_wp(s.min_amount_, 2) + " " + s.fiat_ + "\n";
+	msg += "max_buy: " + to_string_wp(s.max_amount_, 2) + " " + s.fiat_ + "\n";
+	msg += "available: " + to_string_wp(s.tradable_amount_, 2) + " " + s.crypto_ + "\n";
+	msg += "methods: " + merge_vec_str(s.methods_) + "\n";
+
+	msg += "\n";
+
+	msg += "Sell on " + b.exchange_ + "\n";
+	msg += "m: " + b.ad_id_ + " (" + b.merchant_name_ + ")\n";
+	msg += "price: " + to_string_wp(s.price_, 2) + "\n";
+	msg += "min_sell: " + to_string_wp(b.min_amount_, 2) + " " + b.fiat_ + "\n";
+	msg += "max_sell: " + to_string_wp(b.max_amount_, 2) + " " + b.fiat_ + "\n";
+	msg += "available: " + to_string_wp(b.tradable_amount_, 2) + " " + b.crypto_ + "\n";
+	msg += "methods: " + merge_vec_str(b.methods_) + "\n";
+
+	msg += "------------------------------------------\n";
+
+	if (b.tradable_amount_ < s.tradable_amount_)
+		min_avail = b.tradable_amount_;
+	else
+		min_avail = s.tradable_amount_;
+
+	est_profit = s.price_ - b.price_;
+	msg += "est_profit_per_unit: " + to_string_wp(est_profit, 2) + " " + s.fiat_ + "\n";
+	msg += "maximum_extractable_value: " + to_string_wp(min_avail * est_profit, 2) + " " + s.fiat_ + "\n";
+
+	tg_.send_message(chat_id_, msg);
+	insert_broadcast_map(opp);
+	sleep(3);
+}
+
+static int broadcast_opp(gwarnt_bot *bot, const gwarnt::arb_opp &opp)
+{
+	bot->broadcast_opp(opp);
+	return 0;
+}
+
+static int broadcast_opps(gwarnt_bot *bot,
+			  const std::vector<gwarnt::arb_opp> &opps)
+{
+	std::string msg;
+	gwarnt::tgbot::json j;
+
+	if (opps.size() == 0)
+		return 0;
+
+	for (const auto &opp : opps)
+		broadcast_opp(bot, opp);
+
+	return 0;
+}
+
+static void run(gwarnt_bot *bot, gwarnt::p2p::binance *bnc,
+		gwarnt::p2p::okx *okx)
 {
 	std::vector<gwarnt::p2p_ad> buy_bnc, sell_bnc;
 	std::vector<gwarnt::p2p_ad> buy_okx, sell_okx;
-	std::vector<gwarnt::arb_opp> opportunities;
+	std::vector<gwarnt::arb_opp> opps;
+
+	printf("Fetching data...\n");
+
+	buy_bnc = bnc->get_data("IDR", "USDT", "BUY");
+	sell_bnc = bnc->get_data("IDR", "USDT", "SELL");
+	buy_okx = okx->get_data("IDR", "USDT", "BUY");
+	sell_okx = okx->get_data("IDR", "USDT", "SELL");
+
+	printf("Binance : %lu buy, %lu sell\n", buy_bnc.size(), sell_bnc.size());
+	printf("OKX     : %lu buy, %lu sell\n", buy_okx.size(), sell_okx.size());
+
+	opps = gwarnt::find_arbitrage_opps(sell_okx, buy_bnc);
+	printf("Opp1    : %zu\n", opps.size());
+	broadcast_opps(bot, opps);
+
+	opps = gwarnt::find_arbitrage_opps(sell_bnc, buy_okx);
+	printf("Opp2    : %zu\n", opps.size());
+	broadcast_opps(bot, opps);
+}
+
+int main(void)
+{
 	gwarnt::p2p::binance bnc;
 	gwarnt::p2p::okx okx;
+	const char *tmp;
+	gwarnt_bot bot;
+	unsigned u = 5;
+	
+	tmp = std::getenv("TG_BOT_TOKEN");
+	if (!tmp) {
+		printf("Please set TG_BOT_TOKEN environment variable!\n");
+		return 1;
+	}
+	bot.set_token(tmp);
+
+	tmp = std::getenv("TG_CHAT_ID");
+	if (!tmp) {
+		printf("Please set TG_CHAT_ID environment variable!\n");
+		return 1;
+	}
+	bot.set_chat_id(tmp);
+	bot.init_get_me();
 
 	while (1) {
-		printf("Fetching data ...\n");
-		buy_bnc = bnc.get_data("IDR", "USDT", "BUY");
-		sell_bnc = bnc.get_data("IDR", "USDT", "SELL");
-
-		buy_okx = okx.get_data("IDR", "USDT", "BUY");
-		sell_okx = okx.get_data("IDR", "USDT", "SELL");
-		printf("Binance: %lu buy, %lu sell\n", buy_bnc.size(), sell_bnc.size());
-		printf("OKX: %lu buy, %lu sell\n", buy_okx.size(), sell_okx.size());
-
-		opportunities = gwarnt::find_arbitrage_opps(sell_okx, buy_bnc);
-		for (const auto &i : opportunities) {
-			std::cout << i.sell.dump() << std::endl;
-			std::cout << "====================" << std::endl;
-			std::cout << i.buy.dump() << std::endl;
-			std::cout << "----------------------------------------------" << std::endl;
-		}
 
-		opportunities = gwarnt::find_arbitrage_opps(sell_bnc, buy_okx);
-		for (const auto &i : opportunities) {
-			std::cout << i.sell.dump() << std::endl;
-			std::cout << "====================" << std::endl;
-			std::cout << i.buy.dump() << std::endl;
-			std::cout << "----------------------------------------------" << std::endl;
+		try {
+			run(&bot, &bnc, &okx);
+			u = 5;
+		} catch (std::exception &e) {
+			printf("Exception: %s\n", e.what());
+
+			// Double the sleep time if exception occurs.
+			u *= 2;
 		}
 
-		printf("Sleeping...\n");
-		sleep(5);
+		bot.garbage_collect();
+		printf("Sleeping for %u seconds...\n", u);
+		sleep(u);
 	}
 
 	return 0;
diff --git a/src/gwarnt/net.cpp b/src/gwarnt/net.cpp
index 8aa78f4..da38047 100644
--- a/src/gwarnt/net.cpp
+++ b/src/gwarnt/net.cpp
@@ -129,6 +129,8 @@ void net::exec(void)
 	curl_easy_setopt(ch_, CURLOPT_PROXY, proxy_.c_str());
 	curl_easy_setopt(ch_, CURLOPT_WRITEFUNCTION, &curl_write_callback);
 	curl_easy_setopt(ch_, CURLOPT_WRITEDATA, &resp_);
+	curl_easy_setopt(ch_, CURLOPT_TIMEOUT, 30L);
+	curl_easy_setopt(ch_, CURLOPT_CONNECTTIMEOUT, 10L);
 
 	resp_.clear();
 	res = curl_easy_perform(ch_);
diff --git a/src/gwarnt/tgbot.cpp b/src/gwarnt/tgbot.cpp
new file mode 100644
index 0000000..2c39c37
--- /dev/null
+++ b/src/gwarnt/tgbot.cpp
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <gwarnt/tgbot.hpp>
+
+using json = nlohmann::json;
+
+namespace gwarnt {
+
+tgbot::tgbot(const std::string &token):
+	token_(token)
+{
+	init();
+}
+
+tgbot::tgbot(void)
+{
+	init();
+}
+
+inline void tgbot::init(void)
+{
+	net_.add_header("Content-Type", "application/json");
+}
+
+tgbot::~tgbot(void) = default;
+
+json tgbot::send_message(const std::string &chat_id, const std::string &text,
+			 tgbot_send_msg_t parse_mode)
+{
+	json j;
+
+	j["chat_id"] = chat_id;
+	j["text"] = text;
+
+	switch (parse_mode) {
+	case SEND_MSG_HTML:
+		j["parse_mode"] = "HTML";
+		break;
+	case SEND_MSG_MARKDOWN:
+		j["parse_mode"] = "Markdown";
+		break;
+	default:
+		break;
+	}
+
+	return exec_post("sendMessage", j);
+}
+
+json tgbot::get_me(void)
+{
+	return exec_get("getMe");
+}
+
+json tgbot::exec_post(const std::string &method, const json &data)
+{
+	std::string url = "https://api.telegram.org/bot" + token_ + "/" + method;
+
+	net_.set_url(url);
+	net_.set_data(data.dump());
+	net_.set_method("POST");
+	net_.exec();
+	return json::parse(net_.get_resp());
+}
+
+json tgbot::exec_get(const std::string &method)
+{
+	std::string url = "https://api.telegram.org/bot" + token_ + "/" + method;
+
+	net_.set_url(url);
+	net_.set_method("GET");
+	net_.exec();
+	return json::parse(net_.get_resp());
+}
+
+} /* namespace gwarnt */
diff --git a/src/gwarnt/tgbot.hpp b/src/gwarnt/tgbot.hpp
new file mode 100644
index 0000000..73cae79
--- /dev/null
+++ b/src/gwarnt/tgbot.hpp
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef GWARNT__TGBOT_HPP
+#define GWARNT__TGBOT_HPP
+
+#include <gwarnt/net.hpp>
+#include <gwarnt/json.hpp>
+
+#include <string>
+
+namespace gwarnt {
+
+typedef enum tgbot_send_msg {
+	SEND_MSG_PLAIN,
+	SEND_MSG_HTML,
+	SEND_MSG_MARKDOWN,
+} tgbot_send_msg_t;
+
+class tgbot {
+public:
+	using json = nlohmann::json;
+
+	tgbot(const std::string &token);
+	tgbot(void);
+	~tgbot(void);
+
+	json send_message(const std::string &chat_id, const std::string &text,
+			  tgbot_send_msg_t parse_mode = SEND_MSG_PLAIN);
+
+	json get_me(void);
+
+	void set_token(const std::string &token) { token_ = token; }
+
+private:
+	json exec_post(const std::string &method, const json &data);
+	json exec_get(const std::string &method);
+	void init(void);
+
+	net net_;
+
+	std::string token_;
+};
+
+} /* namespace gwarnt */
+
+#endif /* #ifndef GWARNT__TGBOT_HPP */
-- 
Alviro Iskandar Setiawan


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot
  2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
                   ` (7 preceding siblings ...)
  2024-09-10 21:44 ` [RFC PATCH 9/9] gwarnt: Add Telegram bot Alviro Iskandar Setiawan
@ 2024-09-10 22:21 ` Ammar Faizi
  8 siblings, 0 replies; 10+ messages in thread
From: Ammar Faizi @ 2024-09-10 22:21 UTC (permalink / raw)
  To: Alviro Iskandar Setiawan
  Cc: Michael William Jonathan, Ravel Kevin Ethan,
	GNU/Weeb Mailing List

On 9/11/24 4:44 AM, Alviro Iskandar Setiawan wrote:
> I believe this tool would be a valuable addition to the GNU/Weeb group
> and would streamline the process of identifying arbitrage possibilities.
> Please consider integrating this bot (@FreezingNightBot) into the group
> for the benefit of our community.
> 
> Thank you for your consideration!
Thanks for developing the bot!

Let's give it a whirl and see how useful it is.

-- 
Ammar Faizi


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-09-10 22:21 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-10 21:44 [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 2/9] gwarnt: Create initial P2P ad data structure Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 3/9] gwarnt: p2p: Add P2P Binance Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 4/9] gwarnt: p2p: Add P2P OKX Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 5/9] gwarnt: Create function to find arbitrage opportunities Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 6/9] gwarnt: p2p/binance: Fix invalid page Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 7/9] gwarnt: Create the initial example Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 8/9] gwarnt: Add README file Alviro Iskandar Setiawan
2024-09-10 21:44 ` [RFC PATCH 9/9] gwarnt: Add Telegram bot Alviro Iskandar Setiawan
2024-09-10 22:21 ` [RFC PATCH 0/9] Introducing GNU/Weeb Arbitrage Opportunity Notification Bot Ammar Faizi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox