slackのログをGoogle Driveに保存

Slackを無料アカウントで利用すると、
10,000発言しか残らないらしい。

でも、一括でエクスポートするAPIが提供されているので、
こまめに保存しておけばOK。

GoogleDriveに保存してみた。

GASはじめて触ったけど、クーロン実行もぽちぽちするだけでとっても便利でした。

//*********************************************************************/
//   Slackのログを一括保存
//-------------------------------------------------
//   
//*********************************************************************/
function excSaveLog() {
  //   定数 
  var SLACK_BASE_URL   = "https://slack.com/api";
  var SLACK_API_TOKEN  = "YourSlackAPI Token";
  var LOG_SAVE_FOLDER  = "YourSave Folder";
  var SAVE_PERIOD_HOR  = 2;
  
  //処理開始
  Logger.log("=====   START   =====")
  
  //
  var CHANNEL_LIST_URL = SLACK_BASE_URL + "/channels.list?token=" + SLACK_API_TOKEN;
  var CHANNEL_HIST_URL = SLACK_BASE_URL + "/channels.history?token=" + SLACK_API_TOKEN;
  var datNow           = Utilities.formatDate(new Date() , 'Asia/Tokyo' , 'yyyy_MM_dd_HH_mm_ss');  
  
  //チャンネルリスト取得
  var objChListJson = getJson(CHANNEL_LIST_URL);
  //保存
  saveAsText("ChannelList_" + datNow + ".json"
             ,objChListJson
             ,LOG_SAVE_FOLDER);
  //チャンネルループ
  for(var idxCh = 0; idxCh < objChListJson.channels.length; idxCh++){
    Logger.log(objChListJson.channels[idxCh].name);
    var objChHistJson = getJson(
                           getChannelHistroyURL(CHANNEL_HIST_URL,
                                                objChListJson.channels[idxCh].id,
                                                SAVE_PERIOD_HOR)
                        );
    //保存
    saveAsText( objChListJson.channels[idxCh].name + "_" + getTimePeriodString(SAVE_PERIOD_HOR) + ".json"
               ,objChHistJson
               ,LOG_SAVE_FOLDER);
  }
  
  //処理終了
  Logger.log("=====   END   =====")
}

//*************************************************/
// JSON取得
//--------------------------------------------
// return Json or false
//*************************************************/
function getJson(JSON_URL){
  var Response = UrlFetchApp.fetch(JSON_URL);
  var JsonStr  = Response.getContentText(); 
  if (JsonStr.length > 0){
    return JSON.parse(JsonStr);
  } else {
    return false;
  };
}
//**************************************************/
//   JsonObjectをテキストファイルに保存
//**************************************************/
function saveAsText(FileName,
                    JsonObject,
                    SaveFolderName){
  var txtfile = DriveApp.createFile(FileName, JSON.stringify(JsonObject), MimeType.PLAIN_TEXT);
  var folders = DriveApp.getFoldersByName(SaveFolderName);
  var file    = DriveApp.getFileById(txtfile.getId());
  if(folders.hasNext()){
    var folder = folders.next();
    var newfile = file.makeCopy(folder);
    newfile.setName(FileName);
    DriveApp.removeFile(file);
  }
}
//**************************************************/
//   履歴取得URL
//**************************************************/
function getChannelHistroyURL(BaseUrl,
                              ChannelId,
                              PeriodHour){
  //LatestTime算出
  var latTime = Math.floor( new Date().getTime() / 1000 );
  latTime = latTime - (3600 * PeriodHour);
  latTime = Math.round(latTime / (3600 * PeriodHour)) * (3600 * PeriodHour);
  
  //OldestTime
  var OldTime = latTime - (3600 * PeriodHour);
  
  var retUrl = BaseUrl;
  retUrl = retUrl + "&channel=" + ChannelId;
  retUrl = retUrl + "&latest="  + latTime;
  retUrl = retUrl + "&oldest="  + OldTime;
  
  return retUrl;
}
//**************************************************/
//   期間表示用文字列取得
//**************************************************/
function getTimePeriodString(PeriodHour){
  //LatestTime算出
  var latTime = Math.floor( new Date().getTime() / 1000 );
  latTime = latTime - (3600 * PeriodHour);
  latTime = Math.round(latTime / (3600 * PeriodHour)) * (3600 * PeriodHour);
  
  //OldestTime
  var OldTime = latTime - (3600 * PeriodHour);
  
  var retStr = "";
  retStr = retStr + Utilities.formatDate(new Date(OldTime * 1000) , 'Asia/Tokyo' , 'yyyy_MM_dd_HHmm');
  retStr = retStr + "to"
  retStr = retStr + Utilities.formatDate(new Date(latTime * 1000) , 'Asia/Tokyo' , 'HHmm');
  
  return retStr;
}

slackのOutgoing WebHooksをこにゃこにゃ

SlackのBOT作るのに、適当なネタがないかなぁと思っていたら、
なんか喋ったら末尾に「なっし〜」つけて返信すればOK><と言われ、
それならできそうなのでチャレンジしてみた。

環境

ASP.net MVC + AppHarbor

書いたこと

適当なコントローラに↓書いた。

        Function SlackOut(ByVal val As dtoSlackOutgoingWebHook)
            If val.user_name = "slackbot" OrElse _
               val.user_name = "NassyBot" Then
                Return Nothing
            Else
                Return Json(New With {.text = val.text & "なっし〜"})
            End If
        End Function

これだけ、後はAppHarborにデプロイして、
slackのOutgoingWebHookの設定すればOK

簡単ですね^^

一回返信した自分の投稿にさらに返信してしまい。
無限ループしたのはご愛嬌><

slackに.netのWebClientからつぶやく

Ingressで、お世話になっているコミュニティが、slackになった。
Hubot系はLAMPな方々が頑張りそうなので、
僕は.netからつぶやいてみることにした。

最終的には(Herokuではなくて)AppHorborからつぶやきくことになると思うけど、
とりあえずはローカルのWebClientから。

Imports Newtonsoft.Json

Module Module1

Sub Main()
    Dim strUrl As String = "https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ"
    Dim objEnc As System.Text.Encoding = New System.Text.UTF8Encoding

    'メッセージ作る
    Dim strPostMsg As String = JsonConvert.SerializeObject(New With {.text = "Hellow World", _
                                                                     .icon_emoji = ":grin:", _
                                                                     .username = "botbotbot"})

    'メッセージ投稿
    Using objWC As Net.WebClient = New Net.WebClient
        Dim objKV As New System.Collections.Specialized.NameValueCollection
        objKV("payload") = strPostMsg
        Dim objRes As Byte() = objWC.UploadValues(strUrl, "POST", objKV)
        Dim strRes As String = objEnc.GetString(objRes)
    End Using
End Sub
End Module

DapperでORACLEのテーブル定義を抽出する

Dapperを使ってテーブルデータのまるまるコピーバッチを作ってた。
で、コピー元のテーブル定義をコピー先にも反映してよ!!っと言われました。

でも、都合上、DB_LINKが使えない→Create select×

じゃーしょうがないDDL流すか。。。→定義はDB見てね(お約束)

素敵なサイトを発見
http://utsuutsu.blogspot.jp/2010/02/oracle9idbmsmetadatagetddltableindextri.html
DDL取るところまではマルパクリでうまくいきそう。

でもね、この部分、どうやってDapperで流すんだろう。。。

exec DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'SQLTERMINATOR', TRUE  );
exec DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'STORAGE'      , FALSE );

これで出来た^^

        Dim objSql As New StringBuilder
        With objSql
            .AppendLine("begin")
            .AppendLine("   DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'STORAGE', false );")
            .AppendLine("end;")
        End With
        'ストアド実行
        objDbConnection.Execute(objSql.ToString, Nothing, CommandType.Text)

        objSql.Length = 0
        With objSql
            .AppendLine("begin")
            .AppendLine("   DBMS_METADATA.SET_TRANSFORM_PARAM(DBMS_METADATA.SESSION_TRANSFORM,'SQLTERMINATOR', true );")
            .AppendLine("end;")
        End With
        'ストアド実行
        objDbConnection.Execute(objSql.ToString, Nothing, CommandType.Text)

後は↑の素敵サイトのSQLをまるっと実行すればOK
めでたしめでたし

Fessの検索結果にファイル名を使いたい

(多分)クロールがファイルシステムだけの時しかうまくいかないけど
下記にて対応した。

ファイル:%FessRoot%\webapps\fess\WEB-INF\view\searchResults.jsp

修正前

<h3 class="title ellipsis">
	<a class="link" href="${doc.urlLink}" data-uri="${doc.urlLink}" data-id="${doc.docId}">${f:h(doc.contentTitle)}</a>
</h3>

このdoc.contentTitleがよくわかっていないけど、
ファイルによってうまい事タイトル的なところを抜いてきてくれている感じはあるものの、
今回の案件ではおいしくないところを取ってくる場合が多く、利用者的にはファイル名を出してほしいと。。。

修正後

<h3 class="title ellipsis">
	<!-- 15.02.13 mod START -->
	<!-- タイトルではなくファイル名を出してほしいとのこと -->
	<a class="link" href="${doc.urlLink}" data-uri="${doc.urlLink}" data-id="${doc.docId}">${f:h(fn:split(doc.urlLink,"/")[fn:length(fn:split(doc.urlLink,"/"))-1])}</a>
	<!-- 15.02.13 mod END -->
</h3>

ファイル名を保持しているプロパティを見つけられず。
しょうがないので、urlLink(ファイルシステムなのでフルパス)からファイル名を抜き出して表示

2015-02-11 駿府城公園散歩

近所のお寺のプチ祭りに参加した後に、
駿府城公園にお散歩しに行ってきた。

緑エージェントさんに会うも314のフライヤーは
すでにお持ちでした><

その後青エージェントさん二人組みに会い、
フライヤーをわたすことができました。

2015-02-09 Translator金

ドリプラでハック
外気温2度、風速4mくらい?

水溜り凍ってたし。。。

そんな中でも2人の緑エージェントさんと遭遇したり、
別の緑エージェントさんがP8にRMH&RHSいれにきてくれたり。

寒さに負けずグリフしてたら金とれました^^