質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.19%

MastodonのストリーミングAPI

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,793

wabisuke2718

score 95

C++(Qt)でMastodonのストリーミングAPIを実行したいのですが、うまくいきません。
まず、次のpythonコードだとストリーミングできました。

mstdn.py

# -*- coding: utf-8 -*-
import operator
from urllib.parse import urljoin

import requests

import json

class MstdnStream:
    """Mastodon Steam Class

    Usage::

        >>> from mstdn import MstdnStream, MstdnStreamListner
        >>> listener = MstdnStreamListner()
        >>> stream = MstdnStream('https://pawoo.net', 'your-access-token', listener)
        >>> stream.public()

    """
    def __init__(self, base_url, access_token, listener):
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({'Authorization': 'Bearer ' + access_token})
        self.listener = listener

    def public(self):
        url = urljoin(self.base_url, '/api/v1/streaming/public')

        resp = self.session.get(url, stream=True)
        resp.raise_for_status()
        event = {}
        for line in resp.iter_lines():
            line = line.decode('utf-8')

            if not line:
                # End of content.
                method_name = "on_{event}".format(event=event['event'])
                f = operator.methodcaller(method_name, event['data'])
                f(self.listener)
                # refreash
                event = {}
                continue

            if line.startswith(':'):
                # TODO: Handle heatbeat
                print('startwith ":" {line}'.format(line=line))
            else:
                key, value = line.split(': ', 1)
                if key in event:
                    event[key] += value
                else:
                    event[key] = value


class MstdnStreamListner:

    def on_update(self, data):
        print(data)

    def on_notification(self, data):
        print(data)

    def on_delete(self, data):
        print("Deleted: {id}".format(id=data))

stream_test.py

from mstdn import MstdnStream, MstdnStreamListner
listener = MstdnStreamListner()
stream = MstdnStream('https://streaming.mstdn.jp', '★アクセストークン★', listener)
stream.public()

呼び出し方
python stream_test.py

これをやるとストリーミングできるのですが、次のC++(Qt)だと

QAbstractSocket::ConnectionRefusedErrorというエラーが出力されます。
おそらくアクセストークンの指定の仕方が間違っている(ヘッダーじゃなくてGETパラメータにするなど)
かもしれないのですが、pythonのコードが何しているのか良く分からないので、
どう直してよいのか分かりません。
pythonのコードが何をやっているのかを教えてくださるだけでも助かります。
出来れば、C++をどう修正すればいいのかまで教えて頂けるともっと助かります。
よろしくお願いします。

WebSocket.cpp

#include "WebSocket.h"
#include <QDebug>
#include <QAbstractSocket>


WebSocket::WebSocket(const QUrl &url, QString accessToken, bool debug, QObject *parent) :
    QObject(parent),
    m_url(url),
    mAccessToken(accessToken),
    m_debug(debug)
{
    if (m_debug) {
        qDebug() << "WebSocket server:" << url;
    }

    connect(&m_webSocket, &QWebSocket::connected, this, &WebSocket::onConnected);
    connect(&m_webSocket, &QWebSocket::disconnected, this, &WebSocket::closed);

    connect(&m_webSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onWebSocketError(QAbstractSocket::SocketError)));

    //m_webSocket.open(QUrl(url));

    QNetworkRequest req(url);
    QString header_text = "Bearer " + mAccessToken;
    req.setRawHeader("Authorization", header_text.toUtf8());
    m_webSocket.open(req);
}

void WebSocket::onWebSocketError(QAbstractSocket::SocketError error)
{
    qDebug() << error;
}

void WebSocket::onConnected()
{
    if (m_debug) {
        qDebug() << "WebSocket connected";
    }
    connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocket::onTextMessageReceived);
    //m_webSocket.sendTextMessage(QStringLiteral("Hello, world!"));
}

void WebSocket::onTextMessageReceived(QString message)
{
    if (m_debug) {
        qDebug() << "Message received:" << message;
    }
    m_webSocket.close();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QLabel>
#include <QFrame>

#include <QNetworkAccessManager>
#include <QNetworkReply>

#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonArray>

#include <vector>

#include "IconWidget.h"
#include <QHBoxLayout>

#include "defineconstants.h"

#include <QUrlQuery>
#include <QSystemTrayIcon>

#include <QTextCodec>

#include "WebSocket.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    manager = new QNetworkAccessManager(this);
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));

    ui->menuStartLocalTimeline->setEnabled(false);
    ui->menuStopLocalTimeline->setEnabled(true);
    //★一時的に無効化
    //mLocalTimelineTimer = startTimer(LOCAL_TIMELINE_UPDATE_INTERVAL);

    connect(ui->menuStartLocalTimeline, SIGNAL(triggered(bool)), this, SLOT(startLocalTimelineTimer()));
    connect(ui->menuStopLocalTimeline, SIGNAL(triggered(bool)), this, SLOT(stopLocalTimelineTimer()));

    //★一時的に無効化
    //manager->get(QNetworkRequest(QUrl("https://mstdn.jp/api/v1/timelines/public?local=\"true\"")));

    trayIcon = new QSystemTrayIcon(this);

    QFile file("your_usercred.txt");
    if (!file.open(QIODevice::ReadOnly)) {
        if (DEBUG_MODE) {
            qDebug() << "file could not open.";
        }
    }
    QTextStream in(&file);
    mAccessToken = in.readLine();

    mWebSocketForTimeline = new WebSocket(QUrl(QStringLiteral("ws://streaming.mstdn.jp/api/v1/streaming/public")), mAccessToken, true);
}

MainWindow::~MainWindow()
{
    delete ui;
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

Pythonは普通にAuthrizationヘッダに認証情報をつけてHTTPSでGETリクエストを送って結果を都度読んでいるだけです。

QTはよくわからないですがWebSocketを使っているのは正しいのでしょうか?Pythonはhttps://でアクセスしている一方でQTはws://でアクセスという違いが何か関係あるかもしれないと思いました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/05/03 11:57

    Pythonコードの
    resp = self.session.get(url, stream=True)
    という部分はWebSocketを開いているのではないのですか?
    出来ればstream=Trueの意味を教えて頂けないでしょうか?
    もしかしたらPythonのコードは一定間隔でリクエストを送っているだけなのでしょうか?
    このコードがあったサイトには「StreamingAPIを叩く」と書いてあったのですが・・・
    そのサイト→http://qiita.com/kk6/items/8351a6541598cf7151ef

    キャンセル

  • 2017/05/03 17:01

    `stream=True`はレスポンスを一度にバッファに読み込んで処理するのではなく、都度都度処理するという動きで、大きなファイルのダウンロードや`Transfer-Encoding: chunked`のレスポンスをうまく扱うために使用します。

    http://docs.python-requests.org/en/master/user/advanced/#body-content-workflow

    なのでこの`stream=True`はWebSocketと関係なく、リクエスト回数は一回、返ってくるレスポンスも一回、そのレスポンスを細切れに処理するという動きです。

    キャンセル

0

こんにちは。

すいません。直接の回答ではないです。
pythonでうまく行っているのであれば、その通信内容を観察するのも手と思います。
通信プログラムを開発する時はいづれにせよ、プロトコル・アナラザは欲しいですし。

Ethernet通信を観察するフリーのプロトコル・アナライザがあります。
昔はEtherealと言う名前でしたが、今はWiresharkです。
非常に強力なのでちょっと使い方に手間取ると思いますが、苦労する価値はあると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.19%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る