2023-04-17 20:12:27 +00:00
|
|
|
#include "ankiconnector.hh"
|
2022-05-21 06:03:26 +00:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonValue>
|
|
|
|
#include "utils.hh"
|
2023-03-16 11:55:47 +00:00
|
|
|
|
|
|
|
QString markTargetWord(QString const& sentence, QString const& word)
|
|
|
|
{
|
|
|
|
// TODO properly handle inflected words.
|
|
|
|
QString result = sentence;
|
|
|
|
return result.replace(word, "<b>" + word + "</b>", Qt::CaseInsensitive);
|
|
|
|
}
|
|
|
|
|
2022-05-21 06:03:26 +00:00
|
|
|
AnkiConnector::AnkiConnector( QObject * parent, Config::Class const & _cfg ) : QObject{ parent }, cfg( _cfg )
|
|
|
|
{
|
|
|
|
mgr = new QNetworkAccessManager( this );
|
|
|
|
connect( mgr, &QNetworkAccessManager::finished, this, &AnkiConnector::finishedSlot );
|
|
|
|
}
|
|
|
|
|
2023-03-22 20:34:05 +00:00
|
|
|
void AnkiConnector::sendToAnki( QString const & word, QString text, QString const & sentence )
|
2022-05-21 06:03:26 +00:00
|
|
|
{
|
2023-03-22 20:34:05 +00:00
|
|
|
if ( word.isEmpty() ) {
|
|
|
|
emit this->errorText( tr( "anki: can't create a card without a word" ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Anki doesn't understand the newline character, so it should be escaped.
|
|
|
|
text = text.replace( "\n", "<br>" );
|
|
|
|
|
|
|
|
QString const postTemplate = R"anki({
|
2023-03-22 21:55:19 +00:00
|
|
|
"action": "addNote",
|
|
|
|
"version": 6,
|
|
|
|
"params": {
|
|
|
|
"note": {
|
|
|
|
"deckName": "%1",
|
|
|
|
"modelName": "%2",
|
|
|
|
"fields": %3,
|
|
|
|
"options": {
|
|
|
|
"allowDuplicate": true
|
|
|
|
},
|
|
|
|
"tags": []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})anki";
|
2022-05-21 06:03:26 +00:00
|
|
|
|
|
|
|
QJsonObject fields;
|
2023-03-16 11:55:47 +00:00
|
|
|
fields.insert( cfg.preferences.ankiConnectServer.word, word );
|
|
|
|
fields.insert( cfg.preferences.ankiConnectServer.text, text );
|
|
|
|
if (!cfg.preferences.ankiConnectServer.sentence.isEmpty()) {
|
|
|
|
QString sentence_changed = markTargetWord(sentence, word);
|
|
|
|
fields.insert( cfg.preferences.ankiConnectServer.sentence, sentence_changed );
|
|
|
|
}
|
2022-05-21 06:03:26 +00:00
|
|
|
|
|
|
|
QString postData = postTemplate.arg( cfg.preferences.ankiConnectServer.deck,
|
|
|
|
cfg.preferences.ankiConnectServer.model,
|
|
|
|
Utils::json2String( fields ) );
|
|
|
|
|
|
|
|
// qDebug().noquote() << postData;
|
2023-03-22 20:34:05 +00:00
|
|
|
postToAnki( postData );
|
2023-03-22 02:04:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AnkiConnector::ankiSearch( QString const & word )
|
|
|
|
{
|
|
|
|
if( !cfg.preferences.ankiConnectServer.enabled ) {
|
|
|
|
emit this->errorText( tr( "Anki search: AnkiConnect is not enabled." ) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString postTemplate = R"anki({
|
|
|
|
"action": "guiBrowse",
|
|
|
|
"version": 6,
|
|
|
|
"params": {
|
|
|
|
"query": "%1"
|
|
|
|
}
|
|
|
|
})anki";
|
|
|
|
postToAnki( postTemplate.arg( word ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnkiConnector::postToAnki( QString const & postData )
|
|
|
|
{
|
2022-05-21 06:03:26 +00:00
|
|
|
QUrl url;
|
|
|
|
url.setScheme( "http" );
|
|
|
|
url.setHost( cfg.preferences.ankiConnectServer.host );
|
|
|
|
url.setPort( cfg.preferences.ankiConnectServer.port );
|
|
|
|
QNetworkRequest request( url );
|
2023-03-22 21:55:45 +00:00
|
|
|
request.setTransferTimeout( transfer_timeout );
|
2022-05-21 06:03:26 +00:00
|
|
|
// request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy );
|
2023-03-22 21:25:39 +00:00
|
|
|
request.setHeader( QNetworkRequest::ContentTypeHeader, "application/json" );
|
2022-05-21 06:03:26 +00:00
|
|
|
auto reply = mgr->post( request, postData.toUtf8() );
|
|
|
|
connect( reply,
|
|
|
|
&QNetworkReply::errorOccurred,
|
|
|
|
this,
|
|
|
|
[ this ]( QNetworkReply::NetworkError e )
|
|
|
|
{
|
|
|
|
qWarning() << e;
|
|
|
|
emit this->errorText( tr( "anki: post to anki failed" ) );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
void AnkiConnector::finishedSlot( QNetworkReply * reply )
|
|
|
|
{
|
2023-03-22 20:34:05 +00:00
|
|
|
if ( reply->error() == QNetworkReply::NoError ) {
|
|
|
|
QByteArray const bytes = reply->readAll();
|
|
|
|
QJsonDocument const json = QJsonDocument::fromJson( bytes );
|
|
|
|
auto const obj = json.object();
|
2022-05-21 06:03:26 +00:00
|
|
|
|
2023-03-22 20:34:05 +00:00
|
|
|
// Normally AnkiConnect always returns result and error,
|
|
|
|
// unless Anki is not running.
|
|
|
|
if ( obj.size() == 2 && obj.contains( "result" ) && obj.contains( "error" ) && obj[ "error" ].isNull() ) {
|
|
|
|
emit errorText( tr( "anki: post to anki success" ) );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
emit errorText( tr( "anki: post to anki failed" ) );
|
|
|
|
}
|
2022-05-21 06:03:26 +00:00
|
|
|
|
2023-03-22 20:34:05 +00:00
|
|
|
qDebug().noquote() << "anki response:" << Utils::json2String( obj );
|
2022-05-21 06:03:26 +00:00
|
|
|
}
|
2023-03-22 20:34:05 +00:00
|
|
|
else {
|
|
|
|
qDebug() << "anki connect error" << reply->errorString();
|
|
|
|
emit errorText( "anki:" + reply->errorString() );
|
2022-05-21 06:03:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reply->deleteLater();
|
|
|
|
}
|