GoogleCalendarに特定形式で予定を入れておけば指定したファイルのtailっぽいものをpusherに送信する仕組みを作ってみた 2011/8/27

pusherってどうなの?とおもってつくってみた。

説明

GoogleCalendarに特定形式で予定を入れておけば指定したファイルのtailっぽいものをpusherに送信します。

特定形式

"[Gcal2PusherTail]ファイルへのフルパス"で予定を作ってください。
予定の開始時刻にpusher送信が始まり、予定の終了時刻にpusher送信が終了します。
(例)
[Gcal2PusherTail]/var/log/system.log
開始:2011/9/1 12:00
終了:2011/9/1 13:00
だと/var/log/system.logの更新部分が2011/9/1 12:00 〜 13:00の期間に継続的にpusherに送信されます。

"pusher"

Google検索をお願いします。

どうなるの?

こうなります

どうやら自分の環境ではSIMBLGoogle日本語入力が死んでますね。
pusherの部分のみデモしてみました

使い道

使い道?特にありませんね。。。外部からサーバーのログを見たい時?ログインすればいいですよ
カレンダーからatを設定する部分はlinux環境とかでPT2とか使っているときの予約システムに流用できそうです。

セットアップ

必要なgemは人によって違うと思うのでエラーに従ってインストールしてください。
自環境では保存後のディレクトリはこうなりました

Googleカレンダーから予定を取ってきてatコマンドを入力する部分

gcal2at.rbで保存してください

#! /usr/bin/env ruby
# -*- coding:utf-8 -*-
require "/Users/seijiro/Desktop/test/my-lib.rb"

class ThisDo < MyObject
  include MyGCalModule
  include MyAtModule
end

o = ThisDo.new
o.gmail = "xxxxxxxxx"
o.gmailpass = "xxxxxxxx"
o.gcalfeedurl = 'xxxxxxxxxxxxxxxx'
o.myscriptdir = "/xxxxxxxxxxxx/"
o.myrbfiledir = "/xxxxxxxxxxxxxxx/"
o.gcal_read.gcal_parse_2_jobs.gcaljobs_2_at
#! /bin/bash
# cronでキックする用
#ユーザーの環境変数を読み込みたいときとか
source /Users/seijiro/.bashrc
ruby /Users/seijiro/Desktop/test/gcal2at.rb
#使い方
#パスとか環境変数とかいろいろあると思います。適宜。

設定
1.pusher送信を行うPCに保存したスクリプトを設置する
2.このファイルの以下を変更してください
#ファイルを配置した場所に従って
require "/ここを変更/my-lib.rb"
o.gmail = "Gmailアドレス"
o.gmailpass = "Gmailパスワード"
o.gcalfeedurl = 'http://www.google.com/calendar/feeds/あなたのメールアドレスの@を%40にかえたもの/private/full'
# atコマンドがキックするスクリプトファイルを貯めておくディレクトリパス
o.myscriptdir = "/フルパス/at/"
# atコマンドがキックするファイルが存在するディレクトリ
o.myrbfiledir = "/フ/ル/パ/ス/test/"
3.cronで適当な間隔でこのスクリプトまたはgcal2at.shを実行するように設定する。
gacl2at.shをつかうのは自環境ではrubyが特殊な位置にあるためです。

atコマンドからキックされてにtailっぽいものをpusherに送信する部分

pushertail.rbで保存してください

#! /usr/bin/env ruby
# -*- coding: utf-8 -*-
require "/Users/seijiro/Desktop/test/my-lib.rb"

class ThisDo < MyObject
  include RunPerSecModule
  include MyPusherModule
  
  def initialize
	@f = open(ARGV[0]||"/var/log/system.log")
	while @f.gets #最後の行までとりあえず読み込み
    end
    @endtime = Time.parse(ARGV[1]) || Time.now + 60 * 5 #5分
    @pusher_app_id = 'yourapp_id'
    @pusher_key = ' your key'
    @pusher_secret = 'your secret '
  end

  def main_loop
	data = ""
	while @f.gets
	  data << $_
	  print $_ #確認!!!
	end
	pusherkick(data) unless data==""
  end

  def loop_hook_post
    stop_run if loopok?
  end
  
  def pusherkick(data)
	data = data.toutf8.gsub("\n","<br>")
	push_pusher('my_app_tail',data)
  end

  def loopok?
    Time.now > @endtime
  end
end

instance = ThisDo.new
instance.run(1) do
  puts "check:" + Time.now.to_s 
end


設定
以下を設定してください
・require "/フルパス/my-lib.rb"
・@pusher_app_id = 'yourapp_id'
・@pusher_key = ' your key'
・@pusher_secret = 'your secret '

pusherを読む部分

適当な名前で保存してchromeで読みこめば(ファイルをドラッグアンドドロップ!!)いいと思います。
もしかするとサーバーからもらわないとchromejavascriptが実行しないかも知れない。
そのときはapache経由で読み込んでください。
htmlはどこかからかコピペしました。場所を忘れてしまいました。

<!DOCTYPE html>
<head>
  <title>PusherTail</title>
  <script src="http://js.pusherapp.com/1.8/pusher.min.js"></script>
  <script>
    // Enable pusher logging - don't include this in production
    Pusher.log = function(message) {
      if (window.console && window.console.log) window.console.log(message);
    };

    // Flash fallback logging - don't include this in production
    WEB_SOCKET_DEBUG = true;
    var pusher = new Pusher('xxxxxx');
    var channel = pusher.subscribe('test_channel');
    channel.bind('my_event', function(data) {
       hoge = document.getElementById("main").innerHTML;
       document.getElementById("main").innerHTML = data['my_app_tail'] + "<hr>" + hoge;
    });
  </script>
</head>
<body>
  run at Chrome
  <div id="main">&nbsp;</div>
</body>

設定
var pusher = new Pusher('xxxxxx');でxxxxxにapp_keyを設定してください

使ってる俺俺ライブラリ

my-lib.rbで保存してください

#! ruby
# -*- coding: utf-8 -*-
DEBUG = false

#
# 俺俺な典型的な動作を提供する
#
# こんな感じで使うと良い
# 
#  require "/Users/seijiro/code/ruby/my-lib.rb"
#  class This < MyObject
#    include RunPerSecModule
#    include MyDBModule
#    include MyPusherModule
#  .....
#
#  end
#

# 俺俺インスタンスをつくるためにクラスを用意した
# 典型的なrequireを書きこんでいく
# 読み込み済みモジュール
#   kconv time
class MyObject
  require "kconv"
  require "time"
end

# 指定秒数ごとにrun関数をループする
# 各メソッドを必要に応じて再定義して使う。
#
#   使い方:
#   someinstance.extend RunPerSecModule
#   someinstance.run(ループのインターバルsec){
#     __block__for__yield__
#   }
#   
# OR
#   
#   class ThisDo < MyObject
#     include RunPerSecModule
#   .....
# 
module RunPerSecModule
  # ループフラグ
  @loop_flg

  # main_loopをループする
  #   sec : ループ間隔 秒
  # before_run_loop,ループ,after_run_loopの順番で実行する。
  # ループの中身はloop_hook_pre,与えられたブロック,main_loop,loop_hook_postの順番で実行する
  def run(sec)
	init_run_per_sec_module
	before_run_loop
	while @loop_flg
	  loop_hook_pre
	  yield
	  main_loop
	  loop_hook_post
	  sleep sec
	end
	after_run_loop
  end

  # 外からは使わない
  # 無限ループフラグを立てる
  def init_run_per_sec_module
	@loop_flg = true
  end

  # runのループを止める
  def stop_run
    @loop_flg = false
  end
  
  # runメソッドが呼ばれるとループの前に一回だけ実行される
  def before_run_loop
	puts "beforerun" if DEBUG
  end

  # runメソッドのloopの中で最初に実行される
  def loop_hook_pre
	puts "prehook"  if DEBUG
  end

  # runメソッドのloopの中で実行される
  def main_loop
    puts "main loop" if DEBUG
  end

  # runのループの中でmain_loopのあとで実行される
  def loop_hook_post
	puts "posthook" if DEBUG
  end
  
  # runメソッドが呼ばれるとループのあとで実行される
  def after_run_loop
	puts "afterrun"  if DEBUG
  end
end

# Pusherサービスへのアクセスを提供する
# https://app.pusherapp.com/apps/7449/api_access?welcome=true
#  @pusher_app_id = 'your pusher app id'
#  @pusher_key = 'pusher key'
#  @pusher_secret = 'pusher secret'
# 
# 使い方など
# class ThisDo
#   include MyPusherModule
# して
# o = ThisDo.new
# o.pusher_app_id = 'your pusher app id'
# o.puserh_key = 'your pusher key' 
# o.pusher_secret = 'your pusher secret'
# o.push_pusher('test_app','test')
# とかでok
module MyPusherModule
  
  #pussherが設定されているか?
  @pusherconnected

  #設定
  attr_accessor :pusher_app_id,:pusher_key,:pusher_secret

  # Pusherへの接続を設定する
  def set_my_pusher
	require "pusher"
	Pusher.app_id = @pusher_app_id
	Pusher.key = @pusher_key
	Pusher.secret = @pusher_secret
    @pusher_event = 'my_event'  
    @pusher_channel = 'test_channel'
  end

  # Pusherにデータをpushする
  #   args
  #   app_name : string アプリの名前
  #   data     : string データ
  def push_pusher(app_name='test_app',data='test')
	if @pusherconnected == nil
      set_my_pusher
      @pusherconnected = true
	end
	
	begin
	  Pusher[@pusher_channel].trigger!(@pusher_event, { app_name => data})
	rescue Pusher::Error => e
	  p e  if DEBUG
	end
  end
end	

# Googleカレンダーへのアクセスを提供する
#    @gmail = "your gamil address"
#    @gmailpass = "your gmail password"
#    @gcalfeedurl = "your gmail feedurl"
#
# 使い方など
# class ThisDo
#   include MyGCalModule
# して
# o = ThisDo.new
# o.gmail = "your gamil address"
# o.gmailpass = "your gmail password"
# o.gcalfeedurl = "your gmail feedurl"
# #最近の日程を取ってくる
# o.gcal_read
# とかでok
module MyGCalModule
  attr_accessor :gmail,:gmailpass,:gcalfeedurl,:gcal_query
  
  def gcal_read
    service
    @gcal_events = @gcal.events
    return self
  end

  #GCalへ書きこむ
  def gcal_write(eventdata)
    service
    event = @gcal.create_event
    event.title = eventdata[:title]
    event.st = eventdata[:start]
    event.en = eventdata[:end]
    event.save!
    @gcal_event = event
    return self
  end

  # gcalのイベントをAtMduleが食べれる形に変換する #共通のJOBクラスで包もうかしら?
  def gcal_parse_2_jobs
    @gcal_jobs = []
    q = @gcal_query ||= '[Gcal2PusherTail'
    @gcal_events.each do |event|
      begin
        kind,filename = event.title.split(']')
        if(kind == @gcal_query && filename != nil)
          @gcal_jobs << {:filename => filename,
            :start => event.st,
            :end => event.en,
            :object => event}
        end
      rescue
        #握りつぶす
      end
    end
    return self
  end

  #fetchしたデータの取り込み済みマークを立てる
  def gcal_checkout(event)
    event.title = '[FETCHED]' + event.title
    event.save!
    return self
  end

  # GCalへのアクセス
  def service
    if @gcal_srv.nil?
      require 'gcalapi'
      @gcal_srv = GoogleCalendar::Service.new(@gmail, @gmailpass)
    end
    @gcal = GoogleCalendar::Calendar::new(@gcal_srv, @gcalfeedurl)
  end
end

# Atコマンドを突っ込む
# MyGCalModuleとの連携で使う
# 使い方など
# class ThisDo
#   include MyAtModule
#   include MyGCalModule
# して
# o = ThisDo.new
#o.gmail = "your gamil address"
#o.gmailpass = "your gmail password"
#o.gcalfeedurl = "your gmail feedurl"
#o.myscriptdir = "path/to/generatedscriptfilesavepath/"
#o.myrbfiledir = "path/to/rubyscriptpath/"
# 
# とかでok
module MyAtModule
  #設定
  attr_accessor :myscriptdir,:myrbfiledir
  
  def gcaljobs_2_at
    @gcal_jobs.each { |job| jobs2at(job) }
    return self
  end

  def jobs2at(job)
    command = _at_command(job)
    File.open("#{_at_scriptpath(job)}","w") do |io|
      io.write(command)
    end

    atcommand =  "/usr/bin/at -f #{_at_scriptpath(job)} #{job[:start].localtime.strftime("%H:%M %m/%d/%y")}"
    atcommand =  "/usr/bin/at -f #{_at_scriptpath(job)} #{(Time.now.localtime + 60).strftime("%H:%M %m/%d/%y")}" if DEBUG
    p atcommand
    p command
    system atcommand
    gcal_checkout(job[:object]) unless DEBUG
  end

  def _at_scriptpath(job)
    "#{@myscriptdir}job2at_#{job[:start].localtime.strftime("%Y%m%d%H%M")}.sh"
  end

  def _at_command(job)
    "#! /bin/bash
#ユーザーの環境変数パスを使いたい
source ~/.bashrc;
growlnotify -t 'Gcal2At' -m 'pusher tail #{job[:filename]} start . end is #{job[:start].localtime.strftime("%Y/%m/%d/%H/%M")}'
ruby #{@myrbfiledir}pushertail.rb #{job[:filename]} '#{job[:end].to_s}'
"
  end
end