Tipnemちゃん、画:あるひさん
初めに
こんにちは、Qiitaで初めて執筆します、@namuyan と申します。
昔はameblo.jpで為にならない記事をよく書いていました。このQiitaの記事は真面目に書く記事としては半年ぶりです。今回、ご縁があり@44uk_i3さんの企画nem Advent Calendar 2017に参加させていただくことになりました。抜けている所、不十分な所があるかもしれませんがよろしくお願いします。
tipnem-py とは?
前提知識として、tipnemというTwitter上でNEMのMosaicをやり取りする事ができるBotが存在します。これはXEMだけではなく自作のMosaicを投げる事ができます。そして、NEM利用者間の二層化が激しいと感じた為、Twitterを介したユーザーの結合を考えMainnet版のTipnemちゃんを作成しました。
なぜ tipnem-py が存在するのか?
tipnemのWebsocket APIを扱う為のPython3ライブラリです。
Tipnemには高機能なAPIが存在し、それを扱う為のライブラリとしてtipnem-pyが作られました。普通ならrequestパラメータとresponseの例を示せば終わりなのですがそうはいきません。
そもそも何故このような高機能APIと高い拡張を持つようになったのか?それはTipnemを他のBotと異なった特徴を持たせる為に拡張性の高いAPIを入れようと考えたからです。
Tipnem登場時には既にシェアを確保したTipbotがいくつか存在していました。このままでは上手くいかないと考え、打開策としてこのような事をしました。これの最初の結果としてXEMBookが存在します。
この界隈にいれば一回は見たことのある顔ですね。
なぜ Websocket なのか?
APIの大きな特徴は Websocket を使用している事です。本来ならば 歴史と技術の積み重ねのある REST を用いるべきですし安全です。ある時、NINJASOULでbitsharesの存在を知りました。bitsharesはサーバーサイドとWebsocketで通信し、リアルタイム性の高い操作画面を実現しています。そこで、このような操作画面の基礎となる高機能なAPIを実装しようと考えました。その結果が Websocket です。(一応RESTもあります)
Websocketの特徴として ”送信と受信が一対一関係でない” があります、これがWebsocket APIの利用を困難にしている要因ですが、逆に一方が一方に一方的にデータを送れるのでリアルタイムな動作を実現すると共に複数スレッドによる柔軟な設計を可能にします。
tipnem-py は何をしてくれるか?
tipnem-pyは送信と受信を一対一の関係に紐付けてくれると同時に、リアルタイムなデータの受け取りを担います。以下の実例を見るのが早いです。githubよりtipnem-pyをDLし、Pythonの手続き型コンソールを開きます。
from tipnem import WebSocketClient
ws = WebSocketClient("ws://tipnem.tk:8088")
ws.start()
まずはライブラリより WebSocketClient
をimportし接続を行います。
ok, result = ws.request("bot/info")
print(result)
簡単なHello worldです。以下のレスポンスが例です。
{
"net": "mainnet",
"height": 1384322,
"address": "NCR2CQE6AI3DIRHPHEPBSVDBOQFSHXFSQF4NIUAH",
"uptime": 6,
"ws_user": 3,
"thread": [
"main",
"log_handler",
"streaming",
"incomimg",
"ws_ticker",
"ws_server",
"analyze",
"price",
"lottery",
"rest_api",
"@nekopeg 89"
],
"version": "1.7.dev"
}
ほんの数行でTipnemちゃんとの通信を行えます。
使ってみよう
以下よりtipnem-pyを用いた例を示します。
接続しただけではLevel0のユーザーであり制限されます。
PINコードでログインしてみよう
ログインするにはTwitterアカウントを所有し、Tipnemちゃんをフォローする か、DM受信をできるようにしておいて下さい。
ws.login_by_pin_guest(screen="example_name")
# login OK!
と出れば成功です。DM経由でPINが送られてくるので入力してください。
残高を取得してみよう
※以下よりログインしている事を前提にします。
ok, result = ws.request("account/balance")
print(result)
{
"all": {
"bakuhatsu:shiro": 8964503540000,
"banana:breakfast": 1,
"banana:dinner": 10,
"banana:lunch": 878,
"blogger:ikehayacoin": 4,
"bluesky:blueskycoin": 99800,
"cash:back": 10000000000,
"cash:good-luck": 1
},
"network": {
"bluesky:blueskycoin": -200,
"ecobit:eco": 300,
"lovenem:lovenem": -24,
"namuyan:faucet": 1000,
"namuyan:nekonium": 150000,
"namuyan:nemrin": 1119,
"nem:xem": 114514
},
"tip": {
"bakuhatsu:shiro": 8964503540000,
"banana:breakfast": 1,
"banana:dinner": 10,
"banana:lunch": 878,
"blogger:ikehayacoin": 4,
"bluesky:blueskycoin": 100000,
"cash:back": 10000000000
}
}
長すぎたので一部切り出しましたがこんな感じの結果が出てきます。
基本allのみ見ればいいです。
投げ銭履歴を取得してみよう
ok, result = ws.request("account/history")
print(result)
[
{
"uuid": 9682,
"sender": "@namuyan_mine",
"recipient": "@recipient1",
"mosaic_name": "namuyan:nekonium",
"amount": 390,
"time": 1511270744,
"twitter": {
"original": {
"id_str": "932962327661383680",
"text": "@namuyan_mine@tipnemtip@namuyan_mine1009mmガガガ!https://t.co/fEenuRRhkX"
},
"tip": {
"id": 932963238257475600,
"id_str": "932963238257475584",
"text": "どうぞ~"
},
"reply": {}
},
"checked": true
}
]
入金履歴を取得してみよう
ok, result = ws.request("nem/history", {"type":1})
print(result)
[
{
"uuid": 417,
"type": "incoming",
"txhash": "c792e1957936d94ea7a06e20b789c1f8fdf6335d1113849367334c7a1c0df59b",
"sender": "NAAAAASSSSSSSSSDDDDDDDFFFFFFFFFFFFTTTTTT",
"recipient": "@namuyan_mine",
"message": "59bf2a7cc5",
"mosaics": {
"nem:xem": 1000
},
"height": 1360000,
"utime": 1509940000
}
]
アカウントをアップグレードしてみよう
ok, result = ws.login_by_pin_user()
print(result)
アカウントをLevel2に上げます。
こうすることで残高の操作が可能になります。
APIから誰かに投げ銭してみよう
data = {
"sender": "@あなたのScreen",
"recipient": "@xembook",
"mosaic": "nem:xem",
"amount": 100000,
"text": "0.1XEMを送ります",
"announce": True
}
ok, result = ws.request("account/throw", data)
print(result)
出金してみよう
data = {
"sender": "@あなたのScreen",
"recipient": "NAN7XF-G52NL3-V5AW3N-TSYO77-AVR6X5LYRJKXWKHY",
"mosaics": {
"nem:xem": 100000
},
"message": "send mosaic"
}
ok, result = ws.request("nem/send/estimate", data)
print(result)
if ok:
ok, result = ws.request("nem/send", data)
print(result)
else:
print("not ok", result)
APIより出金が可能です。
返り値に {'txhash': 'c4284c91a73cf0dbd5f6faae8859eb85d9b0a50b5494627032caa870f8f38694'}
と出れば成功です。
BlockExplorerで調べてみて下さい。
公開鍵ログイン
Tipnemには公開鍵でログインができます。
毎回PINを入力する手間を省くことができます。
鍵ペア作成
from ed25519 import Ed25519
ecc = Ed25519()
secret_key = ecc.secret_key()
public_key = ecc.public_key(secret_key)
data = {
"pubkey": public_key.decode(),
"level": 1
}
ok, result = ws.request("user/pubkey/setup", data)
print(result)
print(secret_key.decode(), public_key.decode())
ws.ws.close()
秘密鍵と公開鍵を作成します。 秘密鍵は見られないようにして下さい。
登録はLevel2で行う必要があります。
鍵でログイン
from tipnem import WebSocketClient
ws = WebSocketClient("ws://tipnem.tk:8088")
ws.start()
ok = ws.login_by_key(
screen="example_name",
seckey="123..abcdf",
pubkey="abc..123")
先ほど作成した公開鍵・秘密鍵を入力します。
上手くいくと設定したLevel1になれます。
実用例
こちらが実用例です。まだまだ少ないです。
終わりに
tipnem-pyはいかがでしたか?TipnemのAPIを使用すればさまざまな事ができます。
プログラムを初めて2年経過しそうな初心者ですがここまでできるのがPythonです。
これを期にPythonを入門にプログラムを初めてみるのはいかがでしょうか?
そしてTipnemを使ってみて下さい!
※tipnem-pyは発展途上です。仕様が変わり上記の例で上手く動かなくなるかもしれません。
※tipnem-pyになにかバグがあればIssueやプルリクをお願いします。