技術的ひとりごと(備忘録)

いろいろな備忘録的な何か

TvRockの予約情報をツイートするスクリプト

TvRockに登録されている録画予約の情報を定期的に確認したいと思って作成したスクリプトを公開。ツイート飛ばすために twtcnsl を使うので、ここらへんを見てセットアップしてください。

使い方

  • 下記のソースを「tw_tvrock_sch.ps1」「yoyaku_reigai.txt」という名前で保存し、同じディレクトリに置いてください。
  • 「tw_tvrock_sch.ps1」については、環境に合わせて変数を設定しなおしてください。(コメント部参照)
  • 「yoyaku_reigai.txt」の中には、ツイートしたくない番組タイトルを正規表現で指定します。1行1パターンで指定します。
  • 変数の更新後、「tw_tvrock_sch.ps1」を実行すると、今から24時間以内に録画される番組のタイトルがツイートされます。(ツイート先は twtcnsl に紐つけているアカウントになります。)
  • タスクスケジューラ等を使ってスクリプトを定期的に実行させると、TvRock予約情報をタイムラインで確認できるようになります。


(1) tw_tvrock_sch.ps1

# 録画予約事前通知ボット
# Ver 1.0
#
# 仕様:
#
# 使い方:
#   tw_tvrock_sch.ps1 -time_base yyyyMMddHHmmss -time_range xx -tvrock_sch filepath -reigai_list reigai_filepath
# 引数:
#   
#   xx       → 数値を指定。スクリプト実行からxx分以内に始まる予約をツイートの対象にする。
#   filepath → tvrock.schファイルのパス 
#   reigai_filepath → 通知対象から外すもの

# 以下の値は適時変更してください。
#  ①[String]$tvrock_sch_file 
#    → tvrock.sch のファイルパスを指定してください。
#  ②[String]$reigai_list_file
#    → yoyaku_reigai.txt のファイルパスを指定してください。
#  ③$cmd_twtcnsl
#    → \twtcnsl.exe のファイルパスを指定してください。



#引数定義(デフォルト値)
Param(
  [int]$time_base      = [int]($(New-TimeSpan $([datetime]'1970/1/1') $([TimeZone]::CurrentTimeZone.ToUniversalTime((get-date)))).TotalSeconds),
  [int]$time_range     = 24 * 60,
  [String]$tvrock_sch_file  = ".\tvrock.sch",
  [String]$reigai_list_file = ".\yoyaku_reigai.txt"
)

#twtcnslコマンド指定
$cmd_twtcnsl = ".\twtcnsl.exe"
set-alias twtcnsl $cmd_twtcnsl

#関数:ツイート処理
function tweet($msg) {

     #ツイート実行
     $tw_msg = $hostname + "> " + $log_level + $msg 
     
     #ログ出力
     $log_date = Get-Date -format "yyyy/MM/dd HH:mm:ss"
     echo ("[" + $log_date + "] ツイート実行 > " + $tw_msg) >> $log_path
     
     twtcnsl /t $tw_msg
}

#tvrock.schの属性名
$sch_tag_id      = "ID"
$sch_tag_station = "STATION"
$sch_tag_title   = "TITLE"
$sch_tag_start   = "START"
$sch_tag_end     = "END"
$sch_separator   = " "

#予約情報オブジェクトを格納する連想配列
$obj_schlist = @{}

#ツイート対象オブジェクトを格納する連想配列
#(第1インデックス→予約時刻末尾に予約Noを連結した値, 第2インデックス→0:STATION,1:TITLE,2:START)
$obj_tw_targetlist1 = @{}

#ソートしたツイート対象オブジェクトを格納する配列
$obj_tw_targetlist2 = @()

#ツイート対象オブジェクトを格納する一時変数
$obj_tw_targetdata = ""


#時刻変換用
$timezone_offset = [system.timezoneinfo]::local.baseUtcOffset

#ホスト名(ログファイルに表示させるホスト名)
$hostname = "hostname"

#ログファイル
$log_dir  = (split-path $MyInvocation.MyCommand.Path -parent)
$log_name = "tw_tvrock_sch.log"
$log_path = $log_dir  + "\" + $log_name

#ログ出力
$log_date = Get-Date -format "yyyy/MM/dd HH:mm:ss"
echo ("[" + $log_date + "] 予約情報の出力を開始します。") >> $log_path
echo ("[" + $log_date + "] timebase=" + $time_base) >> $log_path

######################
#  録画予約情報を解析
######################

#ファイル読み込み
$sch_source = Get-Content $tvrock_sch_file
$reigai_list = Get-Content $reigai_list_file


#予約情報の格納とツイート対象の抽出
foreach ($line in $sch_source)
{
  #各行のデータをスプリット
  $rsv_info = $line -split $sch_separator
  
  #番組情報格納用配列init
  if ($null -eq $obj_schlist[$rsv_info[0]])
  {
     $obj_schlist[$rsv_info[0]] = New-Object psobject |
                                  Add-Member -Name $sch_tag_id -type NoteProperty -Value ""  -PassThru |
                                  Add-Member -Name $sch_tag_station -type NoteProperty -Value ""  -PassThru |
                                  Add-Member -Name $sch_tag_title -type NoteProperty -Value ""  -PassThru |
                                  Add-Member -Name $sch_tag_start -type NoteProperty -Value ""  -PassThru |
                                  Add-Member -Name $sch_tag_end -type NoteProperty -Value ""  -PassThru
  }
  
  #各行の第2列の値を確認
  switch($rsv_info[1])
  {
     #STATIONの場合
     $sch_tag_station
     {
         $obj_schlist[$rsv_info[0]].$sch_tag_station = $rsv_info[2]
     }

     #TITLEの場合
     $sch_tag_title
     {
         #番組名にセパレート用文字が含まれていた場合は復元
         $tmp_title = $rsv_info[2]
         for ($i=3; $i -lt $rsv_info.length; $i++)
         {
              $tmp_title = $tmp_title + $sch_separator + $rsv_info[$i]
         }
         
         $obj_schlist[$rsv_info[0]].$sch_tag_title = $tmp_title
     }
     
     #STARTの場合
     $sch_tag_start
     {
         $obj_schlist[$rsv_info[0]].$sch_tag_start = $rsv_info[2]
         
         #時刻がツイート対象の場合、リストに予約情報を追加
         if (([int]$rsv_info[2] -gt $time_base) -and ([int]$rsv_info[2] -lt $time_base + ($time_range *60)))
         {
         
             #出力情報格納用配列init
             if ($null -eq $obj_tw_targetlist1[$rsv_info[0]])
             {
                $obj_tw_targetlist1[$rsv_info[0]] = New-Object psobject |
                                                    Add-Member -Name $sch_tag_id -type NoteProperty -Value "" -PassThru |
                                                    Add-Member -Name $sch_tag_station -type NoteProperty -Value ""  -PassThru |
                                                    Add-Member -Name $sch_tag_title -type NoteProperty -Value ""  -PassThru |
                                                    Add-Member -Name $sch_tag_start -type NoteProperty -Value ""  -PassThru |
                                                    Add-Member -Name $sch_tag_end -type NoteProperty -Value ""  -PassThru
             }
             
             #この時点で確定する予約のID情報、録画開始時刻を格納しておく
             $obj_tw_targetlist1[$rsv_info[0]].$sch_tag_id = $rsv_info[0]
             $obj_tw_targetlist1[$rsv_info[0]].$sch_tag_start = $rsv_info[2]
         }
     }
     
     #ENDの場合
     $sch_tag_end
     {
         $obj_schlist[$rsv_info[0]].$sch_tag_end = $rsv_info[2]
     }
     
  }
}

$reigai_id = @()

#補完処理
foreach ($id in $obj_tw_targetlist1.keys)
{
     #局名、番組名を補完する
     $obj_tw_targetlist1[$id].$sch_tag_station = $obj_schlist[$id].$sch_tag_station
     $obj_tw_targetlist1[$id].$sch_tag_title   = $obj_schlist[$id].$sch_tag_title
     $obj_tw_targetlist1[$id].$sch_tag_end     = $obj_schlist[$id].$sch_tag_end
     
     #番組名が禁止ワードに含まれていた場合は予約情報を削除する
     foreach ($pattern in $reigai_list)
     {
         #例外リストに定義されたパターンにマッチした予約のidをリストする
         if ($obj_tw_targetlist1[$id].$sch_tag_title -match $pattern)
         {
            $reigai_id += $id
         }

     }
}

#例外リストに登録された予約情報を消す
foreach ($id in $reigai_id)
{
     $obj_tw_targetlist1[$id] = $null
}

#予約時刻順でソート
$obj_tw_targetlist2 = $obj_tw_targetlist1.values | sort $sch_tag_start

########################
#解析した情報をツイート
########################

#予約件数を出力

#ベース時刻
$msg_base_time = ((Get-Date -Date "1970/1/1").AddSeconds($time_base)).Add($timezone_offset)

#レンジ説明文
if ($time_range -lt 60)
{
   $msg_time_ramge = $time_range + "分"
}
else
{
   $msg_time_ramge = [String]([INT]($time_range/60)) + "時間"
}


$base_msg = "[" + $msg_base_time.toString('yyyy/MM/dd(ddd) HH:mm:ss') + "]" + "から" + $msg_time_ramge + "以内の予約をお伝えします。全部で" + $obj_tw_targetlist2.length + "件です。" 
tweet($base_msg)

$i = 1;

foreach ($obj_tw_targetdata in $obj_tw_targetlist2)
{

     #予約開始時刻生成
     $rsv_start = ((Get-Date -Date "1970/1/1").AddSeconds($obj_tw_targetdata.$sch_tag_start)).Add($timezone_offset)

     #予約終了時刻生成
     $rsv_end = ((Get-Date -Date "1970/1/1").AddSeconds($obj_tw_targetdata.$sch_tag_end)).Add($timezone_offset)
     
     #テキスト整形
     $base_msg = "【予約情報(" + $i + "/" + $obj_tw_targetlist2.length + ")】" + "[" + $rsv_start.toString('yyyy/MM/dd(ddd) HH:mm:ss') + " ~ " + $rsv_end.toString('HH:mm:ss') + "]" + " " + $obj_tw_targetdata.$sch_tag_title + "(" + $obj_tw_targetdata.$sch_tag_station + ")" 
     
     #ツイート実行
     tweet($base_msg)
     
     $i = $i + 1
}

#ログ出力
$log_date = Get-Date -format "yyyy/MM/dd HH:mm:ss"
echo ("[" + $log_date + "] 予約情報の出力を完了しました。") >> $log_path

(2) yoyaku_reigai.txt

.*おまかせ番組情報取得スケジュール.*
.*ツイートしたくない番組名.*