goldendict-ng/src/ankiconnector.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

123 lines
3.9 KiB
C++
Raw Normal View History

#include "ankiconnector.hh"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include "utils.hh"
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 );
}
AnkiConnector::AnkiConnector( QObject * parent, Config::Class const & _cfg ):
QObject{ parent },
cfg( _cfg )
{
mgr = new QNetworkAccessManager( this );
connect( mgr, &QNetworkAccessManager::finished, this, &AnkiConnector::finishedSlot );
}
void AnkiConnector::sendToAnki( QString const & word, QString text, QString const & sentence )
{
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";
QJsonObject fields;
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 );
}
QString postData = postTemplate.arg( cfg.preferences.ankiConnectServer.deck,
cfg.preferences.ankiConnectServer.model,
Utils::json2String( fields ) );
// qDebug().noquote() << postData;
postToAnki( postData );
}
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 )
{
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 );
// request.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy );
2023-03-22 21:25:39 +00:00
request.setHeader( QNetworkRequest::ContentTypeHeader, "application/json" );
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 )
{
if ( reply->error() == QNetworkReply::NoError ) {
QByteArray const bytes = reply->readAll();
QJsonDocument const json = QJsonDocument::fromJson( bytes );
auto const obj = json.object();
// 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" ) );
}
qDebug().noquote() << "anki response:" << Utils::json2String( obj );
}
else {
qDebug() << "anki connect error" << reply->errorString();
emit errorText( "anki:" + reply->errorString() );
}
reply->deleteLater();
}