-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve [Matrix] badge generation #2527
Changes from 15 commits
a20f367
d3f4110
d02e309
ffc3282
8453c2f
a72bb2d
55b33b0
a7468fd
48cff03
0b6a19b
e17fd8e
3d9a5bb
3ae25b6
1c1fccd
ea1433e
3ac4a25
b6c9938
d655fa1
75b799d
f929910
fad3add
2800387
8bb5ebe
3ec0c77
2a94b15
410015e
2d4ec57
56db2a9
fcdbd8e
61f7194
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,16 @@ | |
|
||
const Joi = require('joi') | ||
const BaseJsonService = require('../base-json') | ||
const errors = require('../errors') | ||
|
||
const matrixRegisterSchema = Joi.object({ | ||
access_token: Joi.string().required(), | ||
}).required() | ||
|
||
const matrixAliasLookupSchema = Joi.object({ | ||
room_id: Joi.string().required(), | ||
}) | ||
|
||
const matrixStateSchema = Joi.array() | ||
.items( | ||
Joi.object({ | ||
|
@@ -31,15 +36,36 @@ const documentation = ` | |
<ul> | ||
<li>Select the desired room inside the Riot.im client</li> | ||
<li>Click on the room settings button (gear icon) located near the top right of the client</li> | ||
<li>Scroll to the very bottom of the settings page and look under the <code>Advanced</code> tab</li> | ||
<li>You should see the <code>Internal room ID</code> with your rooms ID next to it (ex: <code>!ltIjvaLydYAWZyihee:matrix.org</code>)</li> | ||
<li>Replace the IDs <code>:</code> with <code>/</code></li> | ||
<li>The final badge URL should look something like this <code>/matrix/!ltIjvaLydYAWZyihee/matrix.org.svg</code></li> | ||
<li>Scroll to the very bottom of the settings page and look under the <code>Addresses</code> section</li> | ||
<li>You should see one or more <code>room addresses (or aliases)</code>, which can be easily identified with their starting hash (<code>#</code>) character (ex: <code>#twim:matrix.org</code>)</li> | ||
<li>If there is no address for this room, add one under <code>Local addresses for this room</code></li> | ||
<li>Remove the starting hash character (<code>#</code>)</li> | ||
<li>The final badge URL should look something like this <code>/matrix/twim:matrix.org.svg</code></li> | ||
</ul> | ||
</br> | ||
Some Matrix homeservers don't hold a server name matching where they live (e.g. if the homeserver <code>example.com</code> that created the room alias <code>#mysuperroom:example.com</code> lives at <code>matrix.example.com</code>). | ||
</br> | ||
If that is the case of the homeserver that created the room alias used for generating the badge, you will need to add an extra parameter to the URL, by adding a forward slash followed by the server's FQDN (fully qualified domain name) between the room alias and the <code>.svg</code> extension. | ||
</br> | ||
The final badge URL should then look something like this <code>/matrix/mysuperroom:example.com/matrix.example.com.svg</code>. | ||
</p> | ||
` | ||
|
||
module.exports = class Matrix extends BaseJsonService { | ||
async retrieveAccessToken({ host }) { | ||
let auth | ||
try { | ||
auth = await this.registerAccount({ host, guest: true }) | ||
} catch (e) { | ||
if (e.prettyMessage === 'guests not allowed') { | ||
// attempt fallback method | ||
auth = await this.registerAccount({ host, guest: false }) | ||
} else throw e | ||
} | ||
|
||
return auth.access_token | ||
} | ||
|
||
async registerAccount({ host, guest }) { | ||
return this._requestJson({ | ||
url: `https://${host}/_matrix/client/r0/register`, | ||
|
@@ -64,22 +90,51 @@ module.exports = class Matrix extends BaseJsonService { | |
}) | ||
} | ||
|
||
async fetch({ host, roomId }) { | ||
let auth | ||
try { | ||
auth = await this.registerAccount({ host, guest: true }) | ||
} catch (e) { | ||
if (e.prettyMessage === 'guests not allowed') { | ||
// attempt fallback method | ||
auth = await this.registerAccount({ host, guest: false }) | ||
} else throw e | ||
async lookupRoomAlias({ host, roomAlias, accessToken }) { | ||
return this._requestJson({ | ||
url: `https://${host}/_matrix/client/r0/directory/room/%23${roomAlias}`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What characters are valid for a room alias? Would it be better to use
in case there is some other character that needs URL-encoding? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be fairly okay without it, but better safe than sorry. Good catch, thanks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 2a94b15 |
||
schema: matrixAliasLookupSchema, | ||
options: { | ||
qs: { | ||
access_token: accessToken, | ||
}, | ||
}, | ||
errorMessages: { | ||
401: 'bad auth token', | ||
404: 'room not found', | ||
429: 'rate limited by rooms host', | ||
}, | ||
}) | ||
} | ||
|
||
async fetch({ roomAlias, serverFQDN }) { | ||
let host | ||
if (serverFQDN === undefined) { | ||
const splitAlias = roomAlias.split(':') | ||
// A room alias can either be in the form #localpart:server or | ||
// #localpart:server:port. In the latter case, it's wiser to skip the name | ||
// resolution and use that value right away. | ||
switch (splitAlias.length) { | ||
case 2: | ||
host = splitAlias[1] | ||
break | ||
case 3: | ||
host = splitAlias[2] + splitAlias[3] | ||
break | ||
default: | ||
throw new errors.InvalidParameter() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this be immediately obvious to users what the error is with the value they have provided for ...InvalidParameter({ prettyMessage: ' whatever makes sense here' }) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hah, right, that was before the second (optional) parameter was added, so it was obvious back then, but not that much now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 75b799d |
||
} | ||
} else { | ||
host = serverFQDN | ||
} | ||
const accessToken = await this.retrieveAccessToken({ host }) | ||
const lookup = await this.lookupRoomAlias({ host, roomAlias, accessToken }) | ||
const data = await this._requestJson({ | ||
url: `https://${host}/_matrix/client/r0/rooms/${roomId}/state`, | ||
url: `https://${host}/_matrix/client/r0/rooms/${lookup.room_id}/state`, | ||
schema: matrixStateSchema, | ||
options: { | ||
qs: { | ||
access_token: auth.access_token, | ||
access_token: accessToken, | ||
}, | ||
}, | ||
errorMessages: { | ||
|
@@ -109,11 +164,8 @@ module.exports = class Matrix extends BaseJsonService { | |
} | ||
} | ||
|
||
async handle({ roomId, host, authServer }) { | ||
const members = await this.fetch({ | ||
host, | ||
roomId: `${roomId}:${host}`, | ||
}) | ||
async handle({ roomAlias, serverFQDN }) { | ||
const members = await this.fetch({ roomAlias, serverFQDN }) | ||
return this.constructor.render({ members }) | ||
} | ||
|
||
|
@@ -128,17 +180,17 @@ module.exports = class Matrix extends BaseJsonService { | |
static get route() { | ||
return { | ||
base: 'matrix', | ||
format: '([^/]+)/([^/]+)', | ||
capture: ['roomId', 'host'], | ||
format: '([^/]+)(?:/([^/]+))?', | ||
capture: ['roomAlias', 'serverFQDN'], | ||
} | ||
} | ||
|
||
static get examples() { | ||
return [ | ||
{ | ||
title: 'Matrix', | ||
exampleUrl: '!ltIjvaLydYAWZyihee/matrix.org', | ||
pattern: ':roomId/:host', | ||
exampleUrl: 'twim:matrix.org', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use I think it would be something like this if I'm reading the route pattern correctly namedParams: {
roomAlias: 'twin:matrix.org',
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
pattern: ':roomAlias/:server_fqdn_optional', | ||
staticExample: this.render({ members: 42 }), | ||
documentation, | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option we have with the API is to put the FQDN in the query string, like the way we handle custom NPM registries. Would that be better?
Usually obscure parameters are handled that way, whereas commonly used parameters are placed into the path.
I don't have an opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I didn't think about putting it there. I agree on that it belongs there rather than the path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The gitlab pipeline status badge is a good modern example. Are you up for updating that? I'm good to get this merged after that! Really appreciate all your work on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 410015e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, didn't see your messages in the meantime. I based my work on the NPM badges (in a much simpler version though).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No prob :) I just pushed a commit with validation, and switching to snake case which for better or worse is what we've used for query params in services.
I'll merge this in a second if all looks good!