0

Ruby内に人間への命令を書けるBabaScriptが完成に近づいている

ちゃんと馬場くんからのreturn valueが取れるようになった。

BabaScriptはコンピュータが得意な事はコンピュータが実行し、人間が得意な処理は馬場くんが実行するプログラム言語です。
メタプログラミングでRubyを拡張して人間への命令を送るための構文を追加し、人間を関数と同等に扱えるようにするスマホアプリと連動させます。
世界一柔軟で好意的に解釈してくれるプログラム言語BabaScriptを作りました

% gem install babascript
% baba --help


こういうのが実行できるようになった。

sample.bb
res = かず助に行きたい人の出欠取ってください
puts res # => "6人です"
loop do
num = res.to_i
if num > 0
puts では予約してください("#{num}人")
exit
else
res = 残念・・その次の週はどうですか?
puts res
end
end

% baba sample.bb

日本語で書いた部分が馬場くんが実行する関数として呼び出され、馬場くんから結果が返ってくるまでブロックされるようになりました。



rubyでもbabascriptからの命令を受けれるツールも追加した(開発に便利なので)
baba-vmコマンドでterminalでbabaになれる。


実装

インフラに使っているWeb版LindaRubyクライアントを拡張した。

今までread/take命令はコールバック登録するしかなかったが、コールバック関数を登録しなかった場合はタプルが得られるまでブロックするようにした。
EM::defer do
loop do
tuple = ts.take [1,2]
puts "blocking take #{tuple}"
end
end
コールバック関数を登録すれば今までどおり非同期に動く。なかなか良い仕様だと思う。

0

GUIから起動してないTerminalからDBUSを使う

LinuxのcrontabやserviceからSkype APIが使えるようになった
https://github.com/shokai/skype-ruby


問題

Skype gemで使っているruby-dbusは環境変数DBUS_SESSION_BUS_ADDRESSを使ってSkypeアプリと通信させてくれるのだが、GUIから起動したTerminalでないとDBUS_SESSION_BUS_ADDRESSが設定されていない。
つまりcronやserviceから起動したプロセスはDBUSが使えなかった


解決方法

~/.dbus/session-bus/ にテキストファイルでDBUS_SESSION_BUS_ADDRESSが書いてあった

kde – Run a dbus program in crontab , how to know about the SESSION id? – Unix & Linux Stack Exchange

DBUSに接続する前に環境変数DBUS_SESSION_BUS_ADDRESSが無かったらファイルから読んで設定するようにした。


Virtual Box

Skype APIは起動済みのSkypeアプリと通信するものなので、Skype botを作るにはSkypeを常に起動しておかなければならない。
家のMacMiniにVirtual BoxにUbuntu12.04入れてその中でSkype起動して運用している。

0

CUIでSkype Chatできるコマンド作った

v0.2.1以降のskype gemに付いている。現在v0.2.2になった。
あまり便利ではないのでおすすめできない。

% gem install skype
% skype-chat

起動するとチャットを選ばされるので、選ぶ。

どの辺が便利でないかというと、そもそもSkype APIはそのマシンでGUIで起動済みのSkypeと通信しているわけなので、あえてCUIでチャットする必要が無い。

ただskype gemのサンプルコードとして有用だと思ったのでつけておいた次第。

これがそのskype-chatコマンド。あの恐ろしいSkype APIがとても綺麗に使えているでしょう。
https://github.com/shokai/skype-ruby/blob/master/bin/skype-chat
本当はreadlineとか使った方がいいんだけどgemに依存が増えてしまうのでこの程度で止めておく。まともなチャットツールにするならこれを元に改造すればいいと思う。

Skype gemはコストの高いAPIは内部で自動的にキャッシュしてくれて便利です。
MacとLinuxに対応しているから、botとか作るのに活用してください。

windows対応はpull request募集中です。

0

Skypeのrubygemをアップデートした

前:Skype API Rubyラッパーのgem作った

gem install skype
https://github.com/shokai/skype-ruby

今v0.2.1になった。

主にrb-appscript廃止と、Chat APIとCall APIのラッパーを作った。

SkypeのAPIは建てまし続けた旅館のようになっていて、素直にラッパーを作ると逆に意味不明になると思ったので俺が欲しい機能を俺が欲しい形式で使えるようにしていく方針。

もちろんSkype.execにSkype APIの文字列を渡せば全機能が使えるようになっているので問題ない。


rb-appscript廃止

新しいmacbook airにrb-appscriptインストールしようとしたらCarbon関係のネイティブがビルドできなくてインストールできなかったのでやめた。
開発停止しているらしいし。
/usr/bin/osascriptに-eでapplescript渡して実行することにした。


Chat APIのラッパー

Skypeはchatを選択するときにIDで指定するんだけど、IDは同じチャットでも人によっては違うIDに見えていたり時間経過で変化してしまっていて、botを作る時に使い勝手が悪かった。

こうなった。
require 'skype'

chat = Skype.chats.find{|c| c.members.include? "shokaishokai" and c.topic =~ /testchat/ }

chat.post "hello chat!!" # 投稿

chat.messages.each do |m|
puts m
end
chatはmessageを受信イベントで受け取れるようにしようか考えている。


Call APIのラッパー

たとえば相手が通話に出た瞬間に切るのはこうやる
require 'skype'

call = Skype.call "shokaishokai"

while !call.talking?
puts call.status
sleep 1
end

call.hangup if call.talking?

0

Skype API Rubyラッパーのgem作った

今日は早起きしたのでさくっと作った。

rb-skypemacがRuby1.8でしか動かなかったり、Ruby4Skypeがソースを修正しないと動かなかったり、どのskype gemもリポジトリが公開されてなくてパッチ送れなかったので1から作りなおした。

https://github.com/shokai/skype-ruby
https://rubygems.org/gems/skype


linuxだとruby-dbus、macだとrb-appscriptを使うんだけどOS毎に別のgemをdependencyに入れるのどうすればいいんだろう・・
gemspecの中でRUBY_PLATFORMを見てspec.add_dependencyしたらローカルでbundle installすると大丈夫だけど、rubygems.org経由でgem installするとlinuxなのにrb-appscriptが入ってしまってcocoa.hが無くてビルドエラーする。


インストール

for Mac
% gem install skype rb-appscript

for Linux
% gem install skype ruby-dbus

Gemfile でインストールする場合
gem 'skype'
gem 'rb-appscript' if RUBY_PLATFORM =~ /darwin/i
gem 'ruby-dbus' if RUBY_PLATFORM =~ /linux/i

使い方

これはRubyのメタプログラミングを使った超単純なラッパーなので、使う前にSkype Developer – Skype Desktop API Reference Manualを読んで理解してください
読めばわかると思う


require 'rubygems'
require 'skype'

Skype.config :app_name => "my_skype_app"

## send message
Skype.message "USER_NAME", "hello!!"

## call
Skype.call "USER_NAME"

## get recent chat list
puts Skype.search("recentchats")

## send message to group chat
Skype.chatmessage "#name1/name2;$a1b2cdef3456", "hello chat!!"

簡単に電話かけたりチャットに投稿したりできますね。やりましたね。
しかもMacでもLinuxでも動く。


くわしい内部実装

Skype.messageやSkype.callなどの関数は実装されていません。
実装されていませんが、method_missingを使った簡単なメタプログラミングでSkype.execに変換されて実行されます。
以下と等価です
## send message
Skype.exec "MESSAGED USER_NAME hello!!"

## call
Skype.exec "CALL USER_NAME"

## get recent chat list
puts Skype.exec "SEARCH recentchats"

## send message to group chat
Skype.exec "CHATMESSAGE #name1/name2;$a1b2cdef3456 hello chat!!"
Skypeには各OS毎に通信方法こそ違うものの、同じフォーマットのQuery StringでやりとりするAPIがあります。
それが”MESSAGE ユーザー名 本文”とか、”CALL ユーザー名”です。
messageという関数を呼び出すとmethod_missingによってexec “MESSAGE ~~”になるわけですね


これだけの実装です
module Skype
def self.method_missing(name, *args)
self.exec "#{name.upcase} #{args.join(' ')}"
end

def self.exec(command)
Appscript.app("skype").send_ :script_name => self.config[:app_name], :command => command
end
end
実際には、LinuxとMacでSkype.execの中の実装がわかれています。


返り値がまだ全部Stringなので、SEARCH RECENTCHATSとかGET CHATMESSAGEとかを自分でparseしないとならないのがつらい。そのうちなんとかする。