diff --git a/README.md b/README.md
index 251c01a43..08d4104db 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,7 @@ see [examples](examples/) for more:
|:--------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------:|
| [WeatherBot](examples/WeatherBot) | This example shows how to program a Telegram Bot that displays the weather information of a city using the [weather api](https://www.weatherapi.com/). | |
| [EarthquakeBot](examples/EarthquakeBot) | This example shows how to program a Telegram Bot that will alert you if there is a recent earthquake somewhere in the world. | |
+| [QrCodeBot](examples/QrCodeBot) | This example shows how to program a Telegram Bot that can generate QrCode images from text and extract text from QrCode Images. | |
| [UrlShortenerBot](examples/UrlShortenerBot) | This example shows how to program Telegram Bot for shortening URLs. | |
| [Buttons](examples/Buttons) | This example shows how to program a basic Telegram Bot that uses inline keyboard buttons to interact with users. | |
diff --git a/examples/QrCodeBot/CMakeLists.txt b/examples/QrCodeBot/CMakeLists.txt
new file mode 100644
index 000000000..714f259da
--- /dev/null
+++ b/examples/QrCodeBot/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.10)
+project(qrcode_bot)
+
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include(FetchContent)
+FetchContent_Declare(tgbotxx
+ GIT_REPOSITORY "https://github.com/baderouaich/tgbotxx"
+ GIT_TAG main
+ )
+FetchContent_MakeAvailable(tgbotxx)
+
+# Qr Code Processing Library
+FetchContent_Declare(ZXing
+ GIT_REPOSITORY "https://github.com/zxing-cpp/zxing-cpp"
+ GIT_TAG master
+ )
+FetchContent_MakeAvailable(ZXing)
+
+# Stb libs for image read/write
+FetchContent_Declare(stb
+ GIT_REPOSITORY https://github.com/nothings/stb.git
+ GIT_TAG master
+)
+FetchContent_MakeAvailable(stb)
+
+add_executable(${PROJECT_NAME} main.cpp)
+target_link_libraries(${PROJECT_NAME} PUBLIC tgbotxx ZXing)
+target_include_directories(${PROJECT_NAME} PUBLIC ${stb_SOURCE_DIR})
\ No newline at end of file
diff --git a/examples/QrCodeBot/README.md b/examples/QrCodeBot/README.md
new file mode 100644
index 000000000..55e9057d5
--- /dev/null
+++ b/examples/QrCodeBot/README.md
@@ -0,0 +1,22 @@
+## QR Code Bot
+This example shows how to program a Telegram Bot that can generate QrCode images from text and extract text from QrCode Images.
+
+### Run
+```bash
+mkdir build && cd build
+cmake .. -DCMAKE_BUILD_TYPE=Release
+make -j8
+./qrcode_bot YOUR_BOT_TOKEN
+```
+
+### How to create a new Bot and obtain its private token ?
+1. Open the Telegram mobile app and search BotFather
+2. Send BotFather a command /newbot
+3. Follow instructions to create a new Bot
+4. After you finish the instructions, you will receive a Bot Token, make sure you keep it secured.
+
+### Encode (Text to QRCode Image)
+
+
+### Decode (QRCode Image to Text)
+
\ No newline at end of file
diff --git a/examples/QrCodeBot/main.cpp b/examples/QrCodeBot/main.cpp
new file mode 100644
index 000000000..5184c46d8
--- /dev/null
+++ b/examples/QrCodeBot/main.cpp
@@ -0,0 +1,149 @@
+#include // std::signal
+#include
+#include // tgbotxx
+using namespace tgbotxx;
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define STB_IMAGE_IMPLEMENTATION
+#include
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include
+
+class QrCodeBot : public Bot {
+ public:
+ QrCodeBot(const std::string& token) : Bot(token) {}
+
+ private:
+ void onStart() override {
+ // Drop pending updates
+ api()->deleteWebhook(true);
+
+ // Register my commands
+ Ptr startCmd(new BotCommand());
+ startCmd->command = "/start";
+ startCmd->description = "Start interacting with the bot";
+ api()->setMyCommands({startCmd});
+
+ std::cout << "Bot " << api()->getMe()->username << " Started\n";
+ }
+
+ void onStop() override {
+ std::cout << "\nStopping Bot. Please wait...\n";
+ }
+
+ void onNonCommandMessage(const Ptr& message) override try {
+ if (not message->photo.empty()) { // Did user send a photo ?
+ // Convert QrCode image to text
+ api()->sendChatAction(message->chat->id, "typing");
+ std::string qrCodeText = extractTextFromQrCodeImage(message->photo);
+ api()->sendMessage(message->chat->id, qrCodeText);
+ } else if (not message->text.empty()) { // Did user send a text ?
+ // Convert text to QrCode image
+ api()->sendChatAction(message->chat->id, "upload_photo");
+ cpr::File qrCodePhoto = convertTextToQrCodeImage(message->text);
+ api()->sendPhoto(message->chat->id, qrCodePhoto);
+ }
+ } catch (const std::exception& e) {
+ std::cerr << e.what() << std::endl;
+ api()->sendMessage(message->chat->id, "Internal error");
+ }
+
+ void onCommand(const Ptr& message) override {
+ if (message->text == "/start") {
+ api()->sendMessage(message->chat->id, "Welcome to QrCodeBot! Please send a text to generate a QrCode Image, or send a QrCode Image to extract text from it.");
+ }
+ }
+
+ protected:
+ /// Extracts UTF-8 text from a QR Code Image
+ /// @param photos multiple resolution photo sizes from Message
+ /// @returns Extracted UTF-8 text from the QR Code image
+ std::string extractTextFromQrCodeImage(const std::vector>& photos) {
+ // Get the highest resolution image (Telegram provides 4 resolutions of photo sent by user)
+ const Ptr& photo = *std::max_element(photos.begin(), photos.end(), [](const Ptr& A, const Ptr& B) {
+ return A->height < B->height && A->width < B->width;
+ });
+
+ // Download photo from Telegram servers
+ Ptr file = api()->getFile(photo->fileId);
+ std::string bytes = api()->downloadFile(file->filePath, [](long total, long downloaded) -> bool {
+ std::cout << "Downloading photo " << downloaded << '/' << total << " bytes \r" << std::flush;
+ return true;
+ });
+ std::cout << std::endl;
+
+ // Save downloaded photo to ./photos/input/PHOTO.jpg
+ fs::path photosDir = "photos/input";
+ if (!fs::exists(photosDir)) fs::create_directory(photosDir);
+ fs::path photoPath = photosDir / fs::path(file->filePath).filename();
+ std::ofstream ofs{photoPath, std::ios::binary};
+ ofs.write(bytes.data(), bytes.size());
+ ofs.close();
+ bytes.clear();
+
+ // Load back image using stbi image
+ int width{}, height{}, channels{};
+ std::unique_ptr buffer(stbi_load(photoPath.string().c_str(), &width, &height, &channels, 3), stbi_image_free);
+ if (!buffer) {
+ throw Exception("Failed to read image: " + photoPath.string());
+ }
+
+ // Decode qr code image
+ ZXing::DecodeHints hints{};
+ hints.setTextMode(ZXing::TextMode::HRI); // Human Readable Interpretation
+ hints.setEanAddOnSymbol(ZXing::EanAddOnSymbol::Read);
+ hints.setTryHarder(true);
+ ZXing::ImageView image{buffer.get(), width, height, ZXing::ImageFormat::RGB};
+ auto result = ZXing::ReadBarcode(image, hints);
+ if (result.isValid()) {
+ return result.text();
+ }
+ return ZXing::ToString(result.error());
+ }
+
+ /// Converts @text to a QR Code image
+ /// @param text UTF-8 Text to convert
+ /// @returns cpr::File filename of the generated image
+ cpr::File convertTextToQrCodeImage(const std::string& text) {
+ ZXing::MultiFormatWriter writer(ZXing::BarcodeFormat::QRCode);
+ writer.setMargin(-1);
+ writer.setEncoding(ZXing::CharacterSet::UTF8);
+ ZXing::BitMatrix matrix = writer.encode(text, 128, 128);
+ ZXing::Matrix bitmap = ZXing::ToMatrix(matrix);
+
+ fs::path photosDir = "photos/output";
+ if (!fs::exists(photosDir)) fs::create_directories(photosDir);
+ fs::path photoPath = photosDir / (std::to_string(std::time(nullptr)) + ".jpg");
+ // int success = stbi_write_png(filePath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
+ int success = stbi_write_jpg(photoPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
+ if (!success) {
+ throw Exception("Failed to write image: " + photoPath.string());
+ }
+ return cpr::File(photoPath.string());
+ }
+};
+
+
+int main(int argc, const char *argv[]) {
+ if (argc < 2) {
+ std::cerr << "Usage:\nqrcode_bot \"BOT_TOKEN\"\n";
+ return EXIT_FAILURE;
+ }
+ static std::unique_ptr BOT;
+ std::signal(SIGINT, [](int) { // Graceful Bot exit on CTRL+C
+ if (BOT) {
+ BOT->stop();
+ }
+ std::exit(EXIT_SUCCESS);
+ });
+
+ BOT = std::make_unique(argv[1]);
+ BOT->start();
+ return EXIT_SUCCESS;
+}