Skip to content

Instantly share code, notes, and snippets.

@kumagi
Created January 4, 2017 17:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kumagi/937e4161f47e579eb14f3631c8da19d8 to your computer and use it in GitHub Desktop.
Save kumagi/937e4161f47e579eb14f3631c8da19d8 to your computer and use it in GitHub Desktop.
// Configuration: Obtain Slack web API token at https://api.slack.com/web
var API_TOKEN = PropertiesService.getScriptProperties().getProperty('slack_api_token');
if (!API_TOKEN) {
throw 'You should set "slack_api_token" property from [File] > [Project properties] > [Script properties]';
}
Logger.log("API_" + API_TOKEN);
var FOLDER_NAME = 'Slack Logs';
/**** Do not edit below unless you know what you are doing ****/
var COL_LOG_RAW_JSON = 1;
var COL_MAX = COL_LOG_RAW_JSON;
// Slack offers 10,000 history logs for free plan teams
var MAX_HISTORY_PAGINATION = 10;
var HISTORY_COUNT_PER_PAGE = 1000;
function StoreLogsDelta() {
try {
Logger.log("started");
var logger = new SlackChannelHistoryLogger(API_TOKEN);
logger.run();
Logger.log("finished");
} catch (e) {
Logger.log("error:" + e);
}
};
function SlackClient(api_token) {
// wrapper of slack
this.api_token = api_token;
Logger.log("api token: " + this.api_token);
}
SlackClient.prototype.request = function(path, params) {
if (params === void 0) { params = {}; }
var url = "https://slack.com/api/" + path + "?";
var qparams = [("token=" + encodeURIComponent(this.api_token))];
for (var k in params) {
qparams.push(encodeURIComponent(k) + "=" + encodeURIComponent(params[k]));
}
url += qparams.join('&');
Logger.log("==> GET " + url);
var resp = UrlFetchApp.fetch(url);
var data = JSON.parse(resp.getContentText());
if (data.error) {
throw "GET " + path + ": " + data.error;
}
return data;
}
SlackClient.prototype.getUserList = function() {
var users = {};
var usersResp = this.request('users.list');
usersResp.members.forEach(function (member) {
users[member.id] = member.name;
});
return users;
};
SlackClient.prototype.getTeamName = function() {
var teamInfoResp = this.request('team.info');
return teamInfoResp.team.name;
};
SlackClient.prototype.getChannelList = function() {
var channelsResp = this.request('channels.list');
return channelsResp.channels;
}
SlackClient.prototype.loadMessagesBulk = function (ch, since) {
var _this = this;
var messages = [];
// channels.history will return the history from the latest to the oldest.
// If the result's "has_more" is true, the channel has more older history.
// In this case, use the result's "latest" value to the channel.history API parameters
// to obtain the older page, and so on.
var loadSince = function (since) {
var options = {
oldest: +since,
count: HISTORY_COUNT_PER_PAGE,
channel: ch.id
}
// order: recent-to-older
var resp = _this.request('channels.history', options);
messages = resp.messages.concat(messages);
return resp;
};
var resp = loadSince(since);
var page = 1;
while (resp.has_more && page <= MAX_HISTORY_PAGINATION) {
resp = loadSince(resp.messages[0].ts);
page++;
}
// oldest-to-recent
return messages.reverse();
};
var SlackChannelHistoryLogger = (function (api_token) {
function SlackChannelHistoryLogger() {
this.memberNames = {};
this.slack = new SlackClient(api_token)
}
SlackChannelHistoryLogger.prototype.run = function () {
var _this = this;
this.memberNames = this.slack.getUserList()
Object.keys(this.memberNames).forEach(function (user) {
Logger.log("id:" + user + " to be " + _this.memberNames[user]);
})
this.teamName = this.slack.getTeamName();
var channels = this.slack.getChannelList();
var _this = this;
channels.forEach(function (ch) {
Logger.log("Logging channel:" + ch.name);
_this.importChannelHistoryDelta(ch);
})
};
SlackChannelHistoryLogger.prototype.getLogsFolder = function (path) {
var folder = DriveApp.getRootFolder();
path.forEach(function (name) {
var it = folder.getFoldersByName(name);
if (it.hasNext()) {
folder = it.next();
}
else {
folder = folder.createFolder(name);
}
});
return folder;
};
SlackChannelHistoryLogger.prototype.getSheet = function (ch, d, cont) {
var dateString;
if (d instanceof Date) {
dateString = this.formatDate(d);
}
else {
dateString = '' + d;
}
var spreadsheet;
var sheetByID = {};
var folder = this.getLogsFolder([FOLDER_NAME, this.teamName]);
var spreadsheetName = dateString + "_" + ch.name + "_" + parseInt(cont);
var it = folder.getFilesByName(spreadsheetName);
if (it.hasNext()) {
var file = it.next();
spreadsheet = SpreadsheetApp.openById(file.getId());
}
else {
spreadsheet = SpreadsheetApp.create(spreadsheetName);
folder.addFile(DriveApp.getFileById(spreadsheet.getId()));
}
var sheets = spreadsheet.getSheets();
var first = sheets[0];
if (!first) {
first = spreadsheet.insertSheet();
}
first.setName(dateString + "_" + ch.name)
return first;
};
SlackChannelHistoryLogger.prototype.getChannelHistoryDelta = function (ch) {
try {
var _this = this;
Logger.log("getChannelHistoryDelta " + ch.name + " (" + ch.id + ")");
var now = new Date();
var existingSheet = this.getSheet(ch, now, 0);
if (!existingSheet) {
// try previous month
now.setMonth(now.getMonth() - 1);
existingSheet = this.getSheet(ch, now, 0);
}
var oldest = this.getLastTimeStamp(existingSheet);
if (!oldest) { oldest = "1"; }
return this.slack.loadMessagesBulk(ch, oldest);
} catch (e) {
Logger.log("get channel history delta:" + e);
return [];
}
}
SlackChannelHistoryLogger.prototype.getLastTimeStamp = function (sheet) {
try {
var lastRow = sheet.getLastRow();
var data = JSON.parse(sheet.getRange(lastRow, COL_LOG_RAW_JSON).getValue());
return +data.ts || 0;
}
catch (_) {
return 0;
}
}
SlackChannelHistoryLogger.prototype.importChannelHistoryDelta = function (ch) {
try {
var _this = this;
var messages = this.getChannelHistoryDelta(ch);
var dateStringToMessages = {};
Logger.log("got " + messages.length + " logs");
messages.forEach(function (msg) {
var date = new Date(+msg.ts * 1000);
var dateString = _this.formatDate(date);
if (!dateStringToMessages[dateString]) {
dateStringToMessages[dateString] = [];
}
msg["name"] = _this.memberNames[msg["user"]];
dateStringToMessages[dateString].push(msg);
/*
dateStringToMessage seems to be
{
"2016-01": [{message,,}, {message,,},,,],
"2016-02": [{message,,}, {message,,},,,]
}
*/
});
for (var dateString in dateStringToMessages) {
var sheet = this.getSheet(ch, dateString, 0);
var lastTS = this.getLastTimeStamp(sheet);
var rows = dateStringToMessages[dateString].filter(function (msg) {
return +msg.ts > lastTS;
}).map(function (msg) {
return [JSON.stringify(msg)];
});
var lastRow = sheet.getLastRow();
if (rows.length > 0) {
var range = sheet.insertRowsAfter(lastRow || 1, rows.length)
.getRange(lastRow + 1, 1, rows.length, COL_MAX);
range.setValues(rows)
};
Logger.log("wrote " + parseInt(rows.length) + " rows");
}
Logger.log("finished logging:" + ch.name);
} catch (e) {
Logger.log("exception on writing sheet: " + e);
}
};
SlackChannelHistoryLogger.prototype.formatDate = function (dt) {
return Utilities.formatDate(dt, Session.getScriptTimeZone(), 'yyyy-MM');
};
return SlackChannelHistoryLogger;
})(API_TOKEN);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment