Skip to content

Commit

Permalink
Appservice transaction handling, user queries, bot-backed invites, etc
Browse files Browse the repository at this point in the history
Includes added functions for setting profiles and inviting users. Changes to the join strategy are also included to better support appservice bot-backed invites.

Not included is room handling and 3rd party stuff.
  • Loading branch information
turt2live committed Dec 20, 2018
1 parent 58a5e21 commit b124f68
Show file tree
Hide file tree
Showing 13 changed files with 321 additions and 38 deletions.
18 changes: 15 additions & 3 deletions examples/appservice.ts
Expand Up @@ -41,23 +41,35 @@ const appservice = new Appservice(options);
AutojoinRoomsMixin.setupOnAppservice(appservice);

appservice.on("room.event", (roomId, event) => {
console.log(`Received event ${event["id"]} (${event["type"]}) from ${event["sender"]} in ${roomId}`);
console.log(`Received event ${event["event_id"]} (${event["type"]}) from ${event["sender"]} in ${roomId}`);
});

appservice.on("room.message", (roomId, event) => {
if (!event["content"]) return;
if (event["content"]["msgtype"] !== "m.text") return;

const body = event["content"]["body"];
console.log(`Received message ${event["id"]} from ${event["sender"]} in ${roomId}: ${body}`);
console.log(`Received message ${event["event_id"]} from ${event["sender"]} in ${roomId}: ${body}`);

// We'll create fake ghosts based on the event ID. Typically these users would be mapped
// by some other means and not arbitrarily. The ghost here also echos whatever the original
// user said.
const intent = appservice.getIntentForSuffix(event["id"].replace(/^[a-z0-9]/g, '_'));
const intent = appservice.getIntentForSuffix(event["event_id"].toLowerCase().replace(/[^a-z0-9]/g, '_'));
intent.sendText(roomId, body, "m.notice");
});

appservice.on("query.user", (userId, createUser) => {
// This is called when the homeserver queries a user's existence. At this point, a
// user should be created. To do that, give an object or Promise of an object in the
// form below to the createUser function (as shown). To prevent the creation of a user,
// pass false to createUser, like so: createUser(false);
console.log(`Received query for user ${userId}`);
createUser({
display_name: "Test User",
avatar_mxc: "mxc://localhost/somewhere",
});
});

// Note: The following 3 handlers only fire for appservice users! These will NOT be fired
// for everyone.

Expand Down
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -17,7 +17,6 @@
"prepublishOnly": "npm run build",
"build": "tsc",
"lint": "tslint --project ./tsconfig.json -t stylish",

"build:examples": "tsc -p tsconfig-examples.json",
"example:appservice": "npm run build:examples && node lib/examples/appservice.js"
},
Expand All @@ -33,6 +32,7 @@
"express": "^4.16.4",
"hash.js": "^1.1.7",
"lowdb": "^1.0.0",
"morgan": "^1.9.1",
"request": "^2.88.0",
"tslint": "^5.11.0",
"typescript": "^3.1.1"
Expand Down
47 changes: 42 additions & 5 deletions src/MatrixClient.ts
Expand Up @@ -183,6 +183,18 @@ export class MatrixClient extends EventEmitter {
});
}

/**
* Invites a user to a room.
* @param {string} userId the user ID to invite
* @param {string} roomId the room ID to invite the user to
* @returns {Promise<*>} resolves when completed
*/
public inviteUser(userId, roomId) {
return this.doRequest("POST", "/_matrix/client/r0/rooms/" + encodeURIComponent(roomId) + "/invite", null, {
user_id: userId,
});
}

/**
* Kicks a user from a room.
* @param {string} userId the user ID to kick
Expand Down Expand Up @@ -333,7 +345,7 @@ export class MatrixClient extends EventEmitter {
let leaveEvent = null;
for (let event of room['timeline']['events']) {
if (event['type'] !== 'm.room.member') continue;
if (event['state_key'] !== this.userId) continue;
if (event['state_key'] !== await this.getUserId()) continue;

const oldAge = leaveEvent && leaveEvent['unsigned'] && leaveEvent['unsigned']['age'] ? leaveEvent['unsigned']['age'] : 0;
const newAge = event['unsigned'] && event['unsigned']['age'] ? event['unsigned']['age'] : 0;
Expand All @@ -359,7 +371,7 @@ export class MatrixClient extends EventEmitter {
let inviteEvent = null;
for (let event of room['invite_state']['events']) {
if (event['type'] !== 'm.room.member') continue;
if (event['state_key'] !== this.userId) continue;
if (event['state_key'] !== await this.getUserId()) continue;
if (event['membership'] !== "invite") continue;

const oldAge = inviteEvent && inviteEvent['unsigned'] && inviteEvent['unsigned']['age'] ? inviteEvent['unsigned']['age'] : 0;
Expand Down Expand Up @@ -438,20 +450,45 @@ export class MatrixClient extends EventEmitter {
return this.doRequest("GET", "/_matrix/client/r0/profile/" + userId);
}

/**
* Sets a new display name for the user.
* @param {string} displayName the new display name for the user, or null to clear
* @returns {Promise<*>} resolves when complete
*/
public async setDisplayName(displayName: string): Promise<any> {
const userId = encodeURIComponent(await this.getUserId());
return this.doRequest("PUT", "/_matrix/client/r0/profile/" + userId + "/displayname", null, {
displayname: displayName,
});
}

/**
* Sets a new avatar url for the user.
* @param {string} avatarUrl the new avatar URL for the user, in the form of a Matrix Content URI
* @returns {Promise<*>} resolves when complete
*/
public async setAvatarUrl(avatarUrl: string): Promise<any> {
const userId = encodeURIComponent(await this.getUserId());
return this.doRequest("PUT", "/_matrix/client/r0/profile/" + userId + "/avatar_url", null, {
avatar_url: avatarUrl,
});
}

/**
* Joins the given room
* @param {string} roomIdOrAlias the room ID or alias to join
* @returns {Promise<string>} resolves to the joined room ID
*/
public joinRoom(roomIdOrAlias: string): Promise<string> {
public async joinRoom(roomIdOrAlias: string): Promise<string> {
const apiCall = (targetIdOrAlias: string) => {
targetIdOrAlias = encodeURIComponent(targetIdOrAlias);
return this.doRequest("POST", "/_matrix/client/r0/join/" + targetIdOrAlias).then(response => {
return response['room_id'];
});
};

if (this.joinStrategy) return this.joinStrategy.joinRoom(roomIdOrAlias, apiCall);
const userId = await this.getUserId();
if (this.joinStrategy) return this.joinStrategy.joinRoom(roomIdOrAlias, userId, apiCall);
else return apiCall(roomIdOrAlias);
}

Expand Down Expand Up @@ -604,7 +641,7 @@ export class MatrixClient extends EventEmitter {
if (body && !Buffer.isBuffer(body)) console.debug("MatrixLiteClient (REQ-" + requestId + ")", "body = " + JSON.stringify(body));
if (body && Buffer.isBuffer(body)) console.debug("MatrixLiteClient (REQ-" + requestId + ")", "body = <Buffer>");

const params: {[k: string]: any} = {
const params: { [k: string]: any } = {
url: url,
method: method,
qs: qs,
Expand Down

0 comments on commit b124f68

Please sign in to comment.