-
Notifications
You must be signed in to change notification settings - Fork 73
/
Copy pathSlackGhostStore.ts
128 lines (110 loc) · 4.75 KB
/
SlackGhostStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { SlackRoomStore } from "./SlackRoomStore";
import { Datastore } from "./datastore/Models";
import { SlackGhost } from "./SlackGhost";
import { IConfig } from "./IConfig";
import QuickLRU from "@alloc/quick-lru";
import { Logger, Bridge } from "matrix-appservice-bridge";
const log = new Logger("SlackGhostStore");
/**
* Class that supports the creation of slack ghosts.
*/
export class SlackGhostStore {
private ghostsByUserId: QuickLRU<string, SlackGhost>;
constructor(private rooms: SlackRoomStore, private datastore: Datastore, private config: IConfig, private bridge: Bridge) {
// XXX: Use cache value from config.
this.ghostsByUserId = new QuickLRU({ maxSize: 50 });
}
public get cached(): QuickLRU<string, SlackGhost> {
return this.ghostsByUserId;
}
/**
* Get the domain of a message by getting it from its keys, or by resolving the teamId.
* @param message The slack message, containing a team_domain.
* @param teamId Optionally pass the teamId, if known.
*/
public async getTeamDomainForMessage(message: {team_domain?: string}, teamId?: string): Promise<string> {
// TODO: Is the correct home for this function?
if (message.team_domain !== undefined) {
return message.team_domain;
}
if (!teamId) {
throw Error("Cannot determine team, no id given.");
}
const team = await this.datastore.getTeam(teamId!);
if (team) {
return team.domain;
} else {
throw Error("Cannot determine team, no team found for ID.");
}
}
public async getNullGhostDisplayName(channel: string, userId: string): Promise<string> {
const room = this.rooms.getBySlackChannelId(channel);
const nullGhost = new SlackGhost(this.datastore, userId, room!.SlackTeamId!, userId, undefined);
if (!room || !room.SlackClient) {
return userId;
}
return (await nullGhost.getDisplayname(room!.SlackClient!)) || userId;
}
public getUserId(id: string, teamDomain: string): string {
const localpart = `${this.config.username_prefix}${teamDomain.toLowerCase()}_${id.toUpperCase()}`;
return `@${localpart}:${this.config.homeserver.server_name}`;
}
public async getForSlackMessage(message: {team_domain?: string, user_id: string}, teamId?: string): Promise<SlackGhost> {
// Slack ghost IDs need to be constructed from user IDs, not usernames,
// because users can change their names
// TODO if the team_domain is changed, we will recreate all users.
// TODO(paul): Steal MatrixIdTemplate from matrix-appservice-gitter
// team_domain is gone, so we have to actually get the domain from a friendly object.
const teamDomain = (await this.getTeamDomainForMessage(message, teamId)).toLowerCase();
return this.get(message.user_id, teamDomain, teamId);
}
public async get(slackUserId: string, teamDomain?: string, teamId?: string): Promise<SlackGhost> {
if (!teamDomain && !teamId) {
throw Error("Must provide either a teamDomain or a teamId");
}
const domain = teamDomain || await this.getTeamDomainForMessage({}, teamId);
const userId = this.getUserId(
slackUserId,
domain,
);
const existing = this.ghostsByUserId.get(userId);
if (existing) {
log.debug("Getting existing ghost from cache for", userId);
return existing;
}
const intent = this.bridge.getIntent(userId);
const entry = await this.datastore.getUser(userId);
// TODO: Expose this
await (intent as unknown as any).ensureRegistered();
let ghost: SlackGhost;
if (entry) {
log.debug("Got ghost entry from datastore", userId);
ghost = SlackGhost.fromEntry(this.datastore, entry, intent);
} else {
log.debug("Creating new ghost for", userId);
ghost = new SlackGhost(
this.datastore,
slackUserId,
teamId,
userId,
intent,
);
await this.datastore.upsertUser(ghost);
}
this.ghostsByUserId.set(userId, ghost);
return ghost;
}
public async getExisting(userId: string): Promise<SlackGhost|null> {
if (!this.bridge.getBot().isRemoteUser(userId)) {
// Catch this early.
return null;
}
const entry = await this.datastore.getUser(userId);
log.debug("Getting existing ghost for", userId);
if (!entry) {
return null;
}
const intent = this.bridge.getIntent(userId);
return SlackGhost.fromEntry(this.datastore, entry, intent);
}
}