Compare commits

...

4 Commits

17 changed files with 294 additions and 54 deletions

View File

@ -135,12 +135,18 @@ endif()
FetchContent_Declare(httplib SYSTEM
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib
GIT_TAG 2eaa2ea64f9fb12773306534d461d9ed63cb76b6 # v0.14.1
GIT_TAG c765584e6b1055fe0dfe3e9e6d1b4b09aa305070
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(httplib)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
find_package(PkgConfig)
pkg_check_modules(QRENCODE REQUIRED libqrencode)
include_directories(${QRENCODE_INCLUDE_DIRS})
link_directories(${QRENCODE_LIBRARY_DIRS})
target_link_libraries(checks-parser PRIVATE ${QRENCODE_LIBRARIES})
include_directories( ${OpenCV_INCLUDE_DIRS} )
target_include_directories(checks-parser PUBLIC ${OpenCV_INCLUDE_DIRS})

View File

@ -29,6 +29,7 @@ In general, you need to install following dependencies in order to build that ap
* curl
* nlohmann-json
* qt5
* qrencode
Please, do not hesitate to open an issue if you cannot build that. I will help and if you are building on a distro that is not listed there, we can append that list as soon as we will solve your problem!
### Linux
@ -36,7 +37,7 @@ Please, do not hesitate to open an issue if you cannot build that. I will help a
I recommend using aur helper (I use yay) to install dependencies. Or, if you're masochist, you can build all by yourself ¯\\\_(ツ)\_/¯
```
#Install dependencies
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode
#Install a language package for OCR. Replace ``LANG` to your language. For example, ``tesseract-data-rus`` for russian language
yay -S tesseract-data-LANG
#Clone and compile an app
@ -52,9 +53,9 @@ In debian-based distributions most, but not every, package names are the same.
Installation of dependencies for different debian-based distros:
###### Ubuntu 18.04
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev```
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev```
###### Ubuntu 20.04, LMDE (tested only 6), Debian (tested only 12)
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev```
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev```
Next steps are identical for every debian-based distro
```

View File

@ -37,7 +37,7 @@
Я рекомендую использовать помощник для АУРа (я использую yay) чтобы установить зависимости. Или, если вы мазохист, можете собрать все зависимости ручками ¯\\\_(ツ)\_/¯
```
#Установка зависимостей
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode
#Установка языкового пакета для OCR. Замените ``LANG` на желаемый язык. Например, ``tesseract-data-rus`` для русского языка
yay -S tesseract-data-LANG
#Загрузка исходгого кода и сборка приложения

View File

@ -41,7 +41,13 @@ void AdjustPictureDialog::accept() {
infoDialog.setWindowTitle(tr("No QR code"));
infoDialog.exec();
} else {
emit decodedData(result);
std::map<std::string, std::string> paramsMap;
std::vector<std::string> dataSplit = split(result, "&");
for (std::string &pair : dataSplit) {
std::vector<std::string> values = split(pair, "=");
paramsMap.insert(std::pair<std::string, std::string>(values[0], values[1]));
}
emit decodedData(paramsMap);
QDialog::accept();
}

View File

@ -22,7 +22,7 @@ public:
void computeContrastLookupTable();
std::vector<unsigned short> contrastLUT[100];
signals:
void decodedData(std::string data);
void decodedData(std::map<std::string, std::string> data);
private slots:

View File

@ -1,4 +0,0 @@
#!/bin/bash
export TESSDATA_PREFIX=$APPDIR/usr/share/tesseract-ocr/4.00/tessdata
$APPDIR/usr/bin/checks-parser

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

4
deploy/appimage/AppRun Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
export TESSDATA_PREFIX=\$APPDIR/usr/share/tesseract-ocr/4.00/tessdata
\$APPDIR/usr/bin/checks-parser

View File

@ -0,0 +1,71 @@
FROM ubuntu:20.04
# Installing dependencies
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev
RUN DEBIAN_FRONTEND=noninteractive apt install -y wget git cmake make gcc g++ fuse
# For gcc 12
RUN DEBIAN_FRONTEND=noninteractive apt install -y libmpc-dev libmpfr-dev libgmp-dev
# The program uses std::regex_constants::multiline and some other C++17 things that are not found in gcc for Ubuntu Focal. Thus, we should compile it.
WORKDIR /
RUN wget https://mirror.linux-ia64.org/gnu/gcc/releases/gcc-12.3.0/gcc-12.3.0.tar.gz
RUN tar xf gcc-12.3.0.tar.gz
WORKDIR /gcc-12.3.0
RUN ./configure --disable-multilib
RUN make -j $(nproc) && make install
WORKDIR /
RUN rm -rf gcc-12.3.0
ENTRYPOINT bash
# Download linuxdeployqt
RUN wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage -O /usr/bin/linuxdeployqt && \
chmod +x /usr/bin/linuxdeployqt
# Prepare AppDir and its files
WORKDIR /appimage
RUN mkdir -p AppDir
COPY deploy/appimage/checks-parser.desktop AppDir
COPY icon.png AppDir/checks-parser.png
COPY deploy/appimage/AppRun AppDir
RUN chmod +x AppDir/AppRun
#Copy only necessities
COPY assets ./assets
COPY check ./check
COPY exceptions ./exceptions
COPY goods ./goods
COPY image ./image
COPY image_redactor ./image_redactor
COPY output ./output
COPY parser ./parser
COPY settings ./settings
COPY scenes ./scenes
COPY net ./net
COPY translations ./translations
COPY utils ./utils
COPY ./*cpp ./*.h ./*.ui ./*.qrc CMakeLists.txt .
RUN mkdir build
WORKDIR /appimage/build
RUN cmake -DBUILD_TRANSLATIONS=on .. && make -j 8
WORKDIR /appimage/AppDir/usr/bin
RUN cp /appimage/build/checks-parser .
WORKDIR /appimage
RUN LD_LIBRARY_PATH=LD_LIBRARY_PATH=/usr/local/lib64 linuxdeployqt AppDir/usr/bin/checks-parser -no-copy-copyright-files -appimage
ENTRYPOINT bash

View File

@ -33,6 +33,7 @@ static QWidget *loadUI(QWidget *parent, std::string filename) {
}
int main(int argc, char *argv[]) {
srand(time(0));
QApplication app(argc, argv);
@ -92,10 +93,6 @@ int main(int argc, char *argv[]) {
OCRScene *ocrscene = new OCRScene();
OFDScene *ofdscene = new OFDScene();
// ofdscene->startHttpServer();
// get_local_ip_address();
sceneLayout->addWidget(mainwindowscene);
sceneLayout->addWidget(emailTextScene);
sceneLayout->addWidget(ocrscene);

View File

@ -7,16 +7,22 @@
#include <adjustpicturedialog.h>
#include <httplib.h>
#include <outputdialog.h>
#include <qpixmap.h>
#include <solvecaptchadialog.h>
#include <net/net.h>
#include <exceptions/ofdrequestexception.h>
#include <bits/basic_string.h>
#include <qrencode.h>
#include <bits/basic_string.h>
OFDScene::OFDScene(QWidget *parent)
: QWidget(parent)
, ui(new Ui::OFDScene) {
ui->setupUi(this);
QObject::connect(this, &OFDScene::httpErrorOccured, this, &OFDScene::notifyHttpServerFailure);
}
OFDScene::~OFDScene() {
@ -25,6 +31,7 @@ OFDScene::~OFDScene() {
void OFDScene::startHttpServer() {
std::string localIp = "";
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
@ -32,13 +39,43 @@ void OFDScene::startHttpServer() {
return;
}
httplib::Server svr;
//TODO: generate random port from 1024 to 65535 and check if its used.
svr.Get("/", [&](const httplib::Request &, httplib::Response &res){
res.set_redirect("http://"+ localIp +":8080/", 301);
});
unsigned short number_of_retries = 0;
svr.listen("0.0.0.0", 8080);
do {
if (number_of_retries == 10) {
emit httpErrorOccured();
return;
}
this->port = rand() % (65535 - 1024) + 1024;
std::string connectionString = "binaryeye://scan/?ret=http://"+ localIp +":"+ std::to_string(port) +"/?result={RESULT}";
server.Get("/", [&](const httplib::Request &req, httplib::Response &res){
std::map<std::string, std::string> paramsMap;
if (req.params.size() < 1) {
res.set_redirect(connectionString, 301);
std::cerr << "Too few params: " << req.params.size() << std::endl;
return;
}
std::string result = req.params.find("result")->second;
std::vector<std::string> dataSplit = split(result, "&");
for (std::string &pair : dataSplit) {
std::vector<std::string> values = split(pair, "=");
paramsMap.insert(std::pair<std::string, std::string>(values[0], values[1]));
}
emit onDataDecode(paramsMap);
res.set_redirect(connectionString, 301);
});
std::cerr << "Listening on port: " << this->port << std::endl;
if (!server.listen("0.0.0.0", this->port)) {
std::cerr << "Random port seems to be occupied. Trying to generate another one" << std::endl;
number_of_retries ++;
continue;
}
} while(true);
}
void OFDScene::on_choose_image_button_clicked() {
@ -53,7 +90,6 @@ void OFDScene::on_choose_image_button_clicked() {
return;
}
ui->info_label->setText(tr("Selected image: ") + filename);
AdjustPictureDialog dialog = AdjustPictureDialog(this, filename.toStdString());
@ -61,27 +97,27 @@ void OFDScene::on_choose_image_button_clicked() {
dialog.exec();
}
void OFDScene::onDataDecode(std::string data) {
std::vector<std::string> dataSplit = split(data, "&");
void OFDScene::onDataDecode(std::map<std::string, std::string> data) {
ui->fn_line_edit->setText(QString::fromStdString(data["fn"]));
ui->fd_line_edit->setText(QString::fromStdString(data["i"]));
ui->fi_line_edit->setText(QString::fromStdString(data["fp"]));
ui->fn_line_edit->setText(QString::fromStdString(split(dataSplit[2], "=")[1]));
ui->fd_line_edit->setText(QString::fromStdString(split(dataSplit[3], "=")[1]));
ui->fi_line_edit->setText(QString::fromStdString(split(dataSplit[4], "=")[1]));
QString extractedDateTime = QString::fromStdString(split(dataSplit[0], "=")[1]);
QString extractedDateTime = QString::fromStdString(data["t"]);
//TODO: some QRs contain datetime in format yyyyMMddThhmmss. Perhaps there is more different formats, should write function to detect them.
QDateTime datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmm");
if (datetime == QDateTime::fromString(extractedDateTime, "20000101T1200")) {
datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmmss");
}
ui->purchase_datetime_edit->setDateTime(datetime);
int type = std::stoi(split(dataSplit[5], "=")[1]);
int type = std::stoi(data["n"]);
ui->operation_type_combo_box->setCurrentIndex(type - 1);
std::string total = split(dataSplit[1], "=")[1];
std::string total = data["s"];
ui->total_spin_box->setValue(std::stod(total));
}
void OFDScene::on_parse_button_clicked() {
Net net;
net.get_captcha_from_ofdru();
@ -140,11 +176,41 @@ void OFDScene::on_parse_button_clicked() {
OutputDialog d = OutputDialog(this, check);
d.exec();
}
}
void OFDScene::on_binary_eye_button_clicked() {
http_thread = new std::thread(&OFDScene::startHttpServer, this);
ui->binary_eye_button->setEnabled(false);
while (!server.is_running());
std::string localIp;
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
std::cerr << e.what() << std::endl;
return;
}
std::string connectionString = "binaryeye://scan?ret=http://" + localIp + ":" + std::to_string(port) + "/?result={RESULT}";
generate_qr_code(connectionString);
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(QString::fromStdString(connectionString));
infoDialog.setIconPixmap(QPixmap(QString::fromStdString(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"))).scaled(400, 400, Qt::KeepAspectRatio));
infoDialog.setWindowTitle(tr("QR code for binaryeye to connect"));
infoDialog.setButtonText(1, tr("I've scanned"));
infoDialog.exec();
}
void OFDScene::notifyHttpServerFailure() {
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(tr("Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer."));
infoDialog.setIcon(QMessageBox::Warning);
infoDialog.setWindowTitle(tr("Could not start http server."));
infoDialog.exec();
}
unsigned int OFDScene::getPort() {
return port;
}

View File

@ -2,6 +2,8 @@
#define OFDSCENE_H
#include <QWidget>
#include <httplib.h>
#include <thread>
namespace Ui {
class OFDScene;
@ -15,16 +17,27 @@ public:
explicit OFDScene(QWidget *parent = nullptr);
~OFDScene();
void startHttpServer();
unsigned int getPort();
private slots:
void on_choose_image_button_clicked();
void onDataDecode(std::string data);
void onDataDecode(std::map<std::string, std::string>);
void on_parse_button_clicked();
void on_binary_eye_button_clicked();
void notifyHttpServerFailure();
signals:
void httpErrorOccured();
private:
Ui::OFDScene *ui;
std::thread *http_thread;
unsigned int port;
httplib::Server server;
};
#endif // OFDSCENE_H

View File

@ -367,50 +367,74 @@
<translation>Parse</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="49"/>
<location filename="../ofdscene.cpp" line="205"/>
<source>Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn&apos;t lucky, please, contact the developer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="207"/>
<source>Could not start http server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="84"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Please, select a picture where QR code that contains info about check is present</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="51"/>
<location filename="../ofdscene.cpp" line="86"/>
<source>Picture was not selected</source>
<translation>Picture was not selected</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="57"/>
<location filename="../ofdscene.cpp" line="91"/>
<source>Selected image: </source>
<translation>Selected image: </translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="116"/>
<location filename="../ofdscene.cpp" line="150"/>
<source>Captcha was not solved correctly!</source>
<translation>Captcha was not solved correctly!</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="118"/>
<location filename="../ofdscene.cpp" line="152"/>
<source>Captcha is incorrect</source>
<translation>Captcha is incorrect</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="123"/>
<location filename="../ofdscene.cpp" line="157"/>
<source>Internal server error. Please, try again later.</source>
<translation>Internal server error. Please, try again later.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="125"/>
<location filename="../ofdscene.cpp" line="159"/>
<source>Internal server error</source>
<translation>Internal server error</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="130"/>
<location filename="../ofdscene.cpp" line="164"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Check not found. Please, ensure correctness of entered data.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="132"/>
<location filename="../ofdscene.cpp" line="166"/>
<source>Check was not found</source>
<translation>Check was not found</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="198"/>
<source>QR code for binaryeye to connect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="199"/>
<source>I&apos;ve scanned</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>123 123</source>
<translation type="obsolete">123 123</translation>
</message>
</context>
<context>
<name>OutputDialog</name>

View File

@ -367,50 +367,74 @@
<translation>Парсить</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="49"/>
<location filename="../ofdscene.cpp" line="205"/>
<source>Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn&apos;t lucky, please, contact the developer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="207"/>
<source>Could not start http server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="84"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="51"/>
<location filename="../ofdscene.cpp" line="86"/>
<source>Picture was not selected</source>
<translation>Изображение не было выбрано</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="57"/>
<location filename="../ofdscene.cpp" line="91"/>
<source>Selected image: </source>
<translation>Выбранное изображение: </translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="116"/>
<location filename="../ofdscene.cpp" line="150"/>
<source>Captcha was not solved correctly!</source>
<translation>Капча была решена неверно!</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="118"/>
<location filename="../ofdscene.cpp" line="152"/>
<source>Captcha is incorrect</source>
<translation>Капча введена неверно</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="123"/>
<location filename="../ofdscene.cpp" line="157"/>
<source>Internal server error. Please, try again later.</source>
<translation>Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="125"/>
<location filename="../ofdscene.cpp" line="159"/>
<source>Internal server error</source>
<translation>Внутренняя ошибка сервера</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="130"/>
<location filename="../ofdscene.cpp" line="164"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Чек не найден. Пожалуйста, убедитесь в правильности введённых данных.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="132"/>
<location filename="../ofdscene.cpp" line="166"/>
<source>Check was not found</source>
<translation>Чек не найден</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="198"/>
<source>QR code for binaryeye to connect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="199"/>
<source>I&apos;ve scanned</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>123 123</source>
<translation type="obsolete">123 123</translation>
</message>
</context>
<context>
<name>OutputDialog</name>

View File

@ -5,6 +5,10 @@
#include <cstring>
#include <iostream>
#include <locale>
#include <opencv2/core/mat.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <qrencode.h>
#include <regex>
#include <string>
#include "../exceptions/ofdrequestexception.h"
@ -13,6 +17,7 @@
#include <fstream>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <opencv2/opencv.hpp>
std::string get_local_ip_address() {
struct ifaddrs * ifAddrStruct=NULL;
@ -214,3 +219,28 @@ Check parseOfdRuAnswer(std::string html) {
return c;
}
void generate_qr_code(std::string data) {
QRcode *qrCode = QRcode_encodeString(data.c_str(), 2, QR_ECLEVEL_L, QR_MODE_8, 1);
if (qrCode == NULL) {
std::cerr << "Error on generating qr code" << std::endl;
}
cv::Mat qrCodeImage = cv::Mat::zeros(qrCode->width, qrCode->width, CV_8UC3);
for (int y = 0; y < qrCode->width; y++) {
for (int x = 0; x < qrCode->width; x++) {
cv::rectangle(
qrCodeImage,
cv::Point(x, y),
cv::Point(x + 1, y + 1),
((qrCode->data[y * qrCode->width + x] & 1) ?
cv::Scalar(255., 255., 255.) : cv::Scalar(0., 0., 0.)
),
-1
);
}
}
cv::imwrite(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"), qrCodeImage);
QRcode_free(qrCode);
}

View File

@ -20,4 +20,6 @@ Check parseOfdRuAnswer(std::string);
std::wstring trim_html_response(std::wstring& check);
void generate_qr_code(std::string data);
#endif // UTILS_H