299 lines
7.4 KiB
JavaScript
299 lines
7.4 KiB
JavaScript
const mm_client = (function() { "use strict";
|
|
|
|
|
|
class MattermostClient {
|
|
constructor (endpoint, credentials_provider) {
|
|
this.endpoint = endpoint;
|
|
|
|
this.credentials = credentials_provider;
|
|
const creds = this.credentials.get(this.endpoint);
|
|
this.token = creds ? creds.token : null;
|
|
console.info(`Created MattermostClient for ${this.endpoint}, ${this.token ? "found token" : "did not find token"}`);
|
|
|
|
this.socket = null;
|
|
this.channelStore = new store.ChannelStore(this);
|
|
}
|
|
|
|
async get(path, queryParams) {
|
|
const headers = this.token ? {"Authorization": `Bearer ${this.token}`} : {};
|
|
const response = await ajax.getJson(`${this.endpoint}${path}`, {headers, queryParams});
|
|
if (!response.ok) {
|
|
throw response;
|
|
}
|
|
return response.responseJson;
|
|
}
|
|
|
|
async post(path, data) {
|
|
const headers = this.token ? {"Authorization": `Bearer ${this.token}`} : {};
|
|
const response = await ajax.postJson(`${this.endpoint}${path}`, data, {headers});
|
|
if (!response.ok) {
|
|
throw response;
|
|
}
|
|
return response;
|
|
}
|
|
|
|
async paginatedGet(url, queryParams, perPage=200) {
|
|
let data = [];
|
|
|
|
let params = {page: 0, per_page: perPage};
|
|
if (queryParams) {
|
|
extend(params, queryParams);
|
|
}
|
|
|
|
let loadMore = true;
|
|
while (loadMore) {
|
|
if (params.page > 100) {
|
|
throw new Error("Requesting more than 100 pages, something looks wrong or the response is massive (>20,000 items)");
|
|
}
|
|
|
|
const response = await this.get(url, params);
|
|
data = data.concat(response);
|
|
|
|
loadMore = response.length > 0;
|
|
params.page++;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
start() {
|
|
assert(this.token);
|
|
|
|
if (this.socket) {
|
|
try {
|
|
this.socket.close();
|
|
} catch(e) {}
|
|
}
|
|
|
|
const users = this.regetUsers();
|
|
const me = this.userMe().then(data => {this.me = data;});
|
|
const channels = this.channelStore.fetch();
|
|
|
|
return Promise.all([
|
|
users,
|
|
me,
|
|
channels
|
|
])
|
|
.then(() => this.websocket())
|
|
.then(async () => {
|
|
const curChan = currentChannel();
|
|
if (curChan.endpoint === this.endpoint) {
|
|
console.debug("Reloading channel");
|
|
const {channels, teams, unread} = await this.channelStore.get();
|
|
const channel = channels[curChan.channel_id];
|
|
const team = channel["team_id"] ? teams[channel["team_id"]] : null;
|
|
await switchToChannel(this, team, channel);
|
|
}
|
|
})
|
|
.catch(e => {
|
|
console.error("Could not connect, trying again in 5 seconds", e);
|
|
window.setTimeout(() => this.start(), 5000);
|
|
});
|
|
}
|
|
|
|
async loggedIn() {
|
|
if (!this.token) {
|
|
return false;
|
|
}
|
|
try {
|
|
const meResponse = await this.userMe();
|
|
return true;
|
|
} catch (e) {
|
|
if (e instanceof ajax.NotOkError && e.xhr.status == 401) {
|
|
return false;
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
async logIn(login_id, password) {
|
|
if (await this.loggedIn()) {
|
|
throw Error("Already logged in on this server");
|
|
}
|
|
|
|
const response = await this.post("/users/login", {login_id, password});
|
|
const token = response.getResponseHeader("Token");
|
|
if (!token) {
|
|
throw Error("No Token header in response to log in request");
|
|
}
|
|
this.credentials.store(this.endpoint, login_id, token);
|
|
this.token = token;
|
|
return response.responseJson;
|
|
}
|
|
|
|
async logOut() {
|
|
console.log(this);
|
|
console.log(this.token);
|
|
assert(this.token, "logged in");
|
|
try {
|
|
const response = await this.post("/users/logout", this.token);
|
|
} catch (e) {
|
|
console.warn(e);
|
|
}
|
|
|
|
// Verify that the token is now invalidated
|
|
if (await this.loggedIn()) {
|
|
throw new Error("Failed to log out: token still works after trying to log out");
|
|
}
|
|
|
|
this.credentials.clear(this.endpoint);
|
|
this.token = null;
|
|
return response.responseJson;
|
|
}
|
|
|
|
user(user_id) {
|
|
assertIsMattermostId(user_id);
|
|
return this.get(`/users/${user_id}`);
|
|
}
|
|
userMe() { return this.get("/users/me"); }
|
|
myTeams() { return this.get("/users/me/teams"); }
|
|
|
|
myChannels(team_id) {
|
|
assertIsMattermostId(team_id);
|
|
return this.get(`/users/me/teams/${team_id}/channels`);
|
|
}
|
|
|
|
channelPosts(channel_id, beforePost=null, afterPost=null, since=null) {
|
|
assertIsMattermostId(channel_id);
|
|
assertIsNullOrMattermostId(beforePost);
|
|
assertIsNullOrMattermostId(afterPost);
|
|
return this.get(`/channels/${channel_id}/posts`, {
|
|
before: beforePost, after: afterPost, since
|
|
});
|
|
}
|
|
|
|
getUnread(team_id) {
|
|
return this.get(`/users/me/teams/${team_id}/channels/members`);
|
|
}
|
|
|
|
markChannelRead(channel_id) {
|
|
assertIsMattermostId(channel_id);
|
|
return this.post(`/channels/members/me/view`, {
|
|
channel_id: channel_id
|
|
});
|
|
}
|
|
|
|
writePost(channel_id, message) {
|
|
return this.post("/posts", {
|
|
"channel_id": channel_id,
|
|
"message": message
|
|
});
|
|
}
|
|
|
|
executeSlashCommand(channel_id, command) {
|
|
return this.post("/commands/execute", {channel_id, command});
|
|
}
|
|
|
|
getUsers() {
|
|
if (!this.users) {
|
|
this.users = this.paginatedGet("/users", {}).then(users => {
|
|
const newUsers = Object.create(null);
|
|
for (let user_data of users) {
|
|
const user = user_data;
|
|
newUsers[user.id] = user;
|
|
}
|
|
return newUsers;
|
|
});
|
|
}
|
|
return this.users;
|
|
}
|
|
regetUsers() {
|
|
this.users = null;
|
|
return this.getUsers();
|
|
}
|
|
|
|
async filePublicLink(file_id) {
|
|
const response = await this.get(`/files/${file_id}/link`, {});
|
|
return response.link;
|
|
}
|
|
|
|
websocket() {
|
|
assert(this.token, "logged in");
|
|
|
|
const endpoint_destructuring = this.endpoint.match(/^([^:]+)(:\/\/.*)/);
|
|
const protocol = endpoint_destructuring[1] === "https" ? "wss" : "ws";
|
|
const endpoint = protocol + endpoint_destructuring[2] + "/websocket";
|
|
|
|
const socket = new WebSocket(endpoint);
|
|
|
|
socket.addEventListener("error", event => {
|
|
console.error("Websocket errored", event);
|
|
try {
|
|
socket.close();
|
|
} catch(e) {}
|
|
|
|
this.start();
|
|
});
|
|
|
|
socket.addEventListener("close", event => {
|
|
this.socket = null;
|
|
console.info("Websocket closed", event);
|
|
});
|
|
|
|
socket.addEventListener("open", event => {
|
|
console.log(`Opened websocket connection to ${endpoint}`);
|
|
socket.send(`{"seq": 1, "action": "authentication_challenge", "data": {"token": "${this.token}"}}`);
|
|
});
|
|
|
|
socket.addEventListener("message", event => {
|
|
console.log("Message from server ", event.data);
|
|
const data = JSON.parse(event.data);
|
|
|
|
if (data.event === "posted") {
|
|
const post = JSON.parse(data.data.post);
|
|
pubsub.publish("MESSAGES_NEW", {...post, endpoint: this.endpoint});
|
|
|
|
} else if (data.event === "channel_viewed") {
|
|
pubsub.publish("CHANNEL_READ", {endpoint: this.endpoint, channel_id: data.data.channel_id, time: null});
|
|
|
|
} else if (data.event === "multiple_channels_viewed") {
|
|
console.log(data.data.channel_times);
|
|
for (let channel_id in data.data.channel_times) {
|
|
const time = data.data.channel_times[channel_id];
|
|
pubsub.publish("CHANNEL_READ", {endpoint: this.endpoint, channel_id, time});
|
|
}
|
|
|
|
} else if (
|
|
data.event === "user_added" ||
|
|
data.event === "user_updated" ||
|
|
data.event === "user_removed"
|
|
) {
|
|
this.regetUsers();
|
|
|
|
} else if (
|
|
data.event === "group_added" ||
|
|
data.event === "channel_created" ||
|
|
data.event === "channel_deleted"
|
|
) {
|
|
this.channelStore.fetch();
|
|
}
|
|
|
|
});
|
|
|
|
this.socket = socket;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
let clients = Object.create(null);
|
|
|
|
function getOrCreate(endpoint) {
|
|
if (!clients[endpoint]) {
|
|
clients[endpoint] = new MattermostClient(endpoint, localstorage_credentials);
|
|
}
|
|
return clients[endpoint];
|
|
}
|
|
|
|
function getOrCreateMultiple(endpoints) {
|
|
return endpoints.map(getOrCreate);
|
|
}
|
|
|
|
function drop(endpoint) {
|
|
delete clients[endpoint];
|
|
}
|
|
|
|
return {getOrCreate, getOrCreateMultiple, drop};
|
|
|
|
})();
|